Android 开发录

android 为什么只能在主线程更新View

2017-07-13  本文已影响151人  过期的薯条

1.引言

前几天去面试有过这样的问题。都知道在非ui线程中不能进行ui操作,但是为什么不能进行ui线程的操作。问的我不知所措。所以决定抽时间,看看博客 找找源码,看看为什么

2.正题

2.1ViewManager

Paste_Image.png

2.2WindowManager

windowManager 是继承于ViewManager。所以windowManager里面存在addView()方法。

Paste_Image.png

2.3 WindowManager实现类WindowManagerImpl

windowManager.addView() 实际调用的是WindowManagerGlobal.addView()

Paste_Image.png

2.4WindowManagerGlobal类

Paste_Image.png

addView方法的核心代码

{
            // 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线程。故而不想等,报错。

上一篇下一篇

猜你喜欢

热点阅读