android 为什么只能在主线程更新View
2017-07-13 本文已影响151人
过期的薯条
1.引言
前几天去面试有过这样的问题。都知道在非ui线程中不能进行ui操作,但是为什么不能进行ui线程的操作。问的我不知所措。所以决定抽时间,看看博客 找找源码,看看为什么
2.正题
2.1ViewManager
Paste_Image.png2.2WindowManager
windowManager 是继承于ViewManager。所以windowManager里面存在addView()方法。
Paste_Image.png2.3 WindowManager实现类WindowManagerImpl
windowManager.addView() 实际调用的是WindowManagerGlobal.addView()
Paste_Image.png2.4WindowManagerGlobal类
Paste_Image.pngaddView方法的核心代码
{
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);//重要
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
可以看到ViewRootImpl 又调用了addView。查看ViewRootImpl的源代码发现。
2.5ViewRootImpl 构造方法
Paste_Image.png根据mThread,进一步查找到
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
查到这步我们就可以知道了:setContentView是在主线程中进行的,其ViewRootImpl指向主线程。 在子线程操作View。Thread.currentThread()不再是UI线程。故而不想等,报错。