【Android】为什么必须在主线程中执行UI操作?

  • View的绘制是单线程模型,view的创建与更新需要在同一个线程(绘制前会checkThread())
    • 每向系统中添加一个view,就会构造一个ViewRoot对象
    • ViewRoot的构造会记录当前线程,之后更新view的时候会检查线程。
    • View的绘制是非线程安全的,多线程下绘制view,容易出错。
  • 在子线程中创建view,并在子线程中更新,是可行的。

Android之为什么只能在UI线程操作View

为什么说android UI操作不是线程安全的

以上都是搜索得来的结论,这里的关键部分还是在ViewRoot,接下来看源码,确认下其内部的UI操作与线程有怎样的联系?

从源码中可以看出:

  • mThread的赋值是在ViewRootImpl的构造函数中确定的。
  • 在ViewRootImpl在调用requestLayout,invalideteChildInParent等似乎与UI相关的函数时,都执行了checkThread();
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    public ViewRootImpl(Context context, Display display) {
        ......
        mThread = Thread.currentThread();
        ......
    }
    ......
    @Override
    public void requestFitSystemWindows() {
        checkThread();
        mApplyInsetsRequested = true;
        scheduleTraversals();
    }

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    ......
    @Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
        ......
    }
    ......
    @Override
    public void requestTransparentRegion(View child) {
        // the test below should not fail unless someone is messing with us
        checkThread();
        if (mView == child) {
            ......
            requestLayout();
        }
    }
    ......
    @Override
    public void requestChildFocus(View child, View focused) {
        if (DEBUG_INPUT_RESIZE) {
            Log.v(mTag, "Request child focus: focus now " + focused);
        }
        checkThread();
        scheduleTraversals();
    }

    @Override
    public void clearChildFocus(View child) {
        if (DEBUG_INPUT_RESIZE) {
            Log.v(mTag, "Clearing child focus");
        }
        checkThread();
        scheduleTraversals();
    }

    @Override
    public void focusableViewAvailable(View v) {
        checkThread();
        if (mView != null) {
            ......
        }
    }

    @Override
    public void recomputeViewAttributes(View child) {
        checkThread();
        if (mView == child) {
            ......
        }
    }
    ......
    /**
     * {@inheritDoc}
     */
    @Override
    public void playSoundEffect(int effectId) {
        checkThread();
        ......
    }
    ......
     /**
     * {@inheritDoc}
     */
    @Override
    public View focusSearch(View focused, int direction) {
        checkThread();
        if (!(mView instanceof ViewGroup)) {
            return null;
        }
        return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public View keyboardNavigationClusterSearch(View currentCluster,
            @FocusDirection int direction) {
        checkThread();
        return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
                mView, currentCluster, direction);
    }
    ......
    void doDie() {
        checkThread();
        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
        ......
    }
    ......
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    ......
}

以requestLayout()为例,确认下checkThread()之后,还做了什么工作?

在checkThread()之后,仅执行了scheduleTraversals(),深入查看后发现TraversalRunnable.run() > doTraversal(); > performTraversals();中有大量的UI相关的操作。

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//测量?
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();//计划消耗批量输入?
            }
            notifyRendererOfFramePending();//通知帧渲染器挂起?
            pokeDrawLockIfNeeded();//如有需要,插入绘制锁?
        }
    }

确认了ViewRoot中的UI操作与线程的相关性,也就是说ViewRoot在哪个线程实例化,那么与之相关的View的UI操作就必须在这个线程中处理。

接下来我们继续看,ViewRoot是在哪里实例化的?

经过搜索,发现ViewRoot的实例化只有两处:

AttachInfo_Accessor不知道是干嘛的,先去看下WindowManagerGlobal都做了什么?

据搜索了解WindowManagerGlobal的作用:

  • 主要用于管理ViewRoot、创建并提供IWindowManager的proxy实例。
    • 管理所有的View
    • 实现跟View有关的接口,比如addView、removeView、updateViewLayout等

再来看下WindowManagerGlobal的源码中,ViewRootImpl的构造函数是在哪里调用的?

PhoneWindowManager WindowManagerGlobal WindowManagerImpl的作用和关系

从源码中可以看出:

  • 每调用一次WindowManagerGlobal.addView(),都会实例化一个ViewRoot
  • 而通过ViewRoot.setView()可知ViewRoot与View是一对一的关系
public final class WindowManagerGlobal {

    ......

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

           ......

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

    ......

}

什么地方调用了WindowManagerGlobal.addView()?

只在WindowManager的唯一实现类WindowManagerImpl中使用:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Context context) {
        this(context, null);
    }

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return new WindowManagerImpl(displayContext, mParentWindow);
    }

    ......

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
}

WindowManagerImpl.addView()调用的地方很多,那么在Activty中WindowManagerImpl.addView()是在哪个线程?

看下Activity中的实现:

  • 通过WindowManagerImpl.addView()新建一个ViewRoot与Activity中的mDecorView绑定
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback,
        AutofillManager.AutofillClient {
    ......
    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
    ......
}

Activity.makeVisible()有三处调用,以下节点依次向上层查询:

  • Activity.setVisible();//没有找到上层调用
  • ActivityThread.handleResumeActivity();
    • ResumeActivityItem.execute()
      • TransactionExecutor.executeCallbacks();
      • TransactionExecutor.executeLifecycleState();
  • ActivityThread.updateVisibility();
    • ActivityThread.handleStopActivity();
      • StopActivityItem.execute();
        • TransactionExecutor.executeCallbacks();
        • TransactionExecutor.executeLifecycleState();
      • TransactionExecutor.performLifecycleSequence();
    • ActivityThread.handleWindowVisibility();
      • WindowVisibilityItem.execute();
        • TransactionExecutor.executeCallbacks();T
        • ransactionExecutor.executeLifecycleState();
    • ActivityThread.handleSendResult();
      • ActivityResultItem.execute();
        • TransactionExecutor.executeCallbacks();
        • TransactionExecutor.executeLifecycleState();

其最终实现都是在TransactionExecutor类中,找到TransactionExecutor类的实例化所在的线程,也就找到了Activity中的mDecorView的UI操作所在的线程:

  • TransactionExecutor只有一个构造函数,且只在ActivityThread中有一次调用
public class TransactionExecutor {
    ......
    private ClientTransactionHandler mTransactionHandler;
    ......
    public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
        mTransactionHandler = clientTransactionHandler;
    }
}
public final class ActivityThread extends ClientTransactionHandler {
    ......
    private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
    ......
}

找出ActivityThread的实例化在哪个线程?

public final class ActivityThread extends ClientTransactionHandler {
    ......
    public static ActivityThread systemMain() {
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(true, 0);
        return thread;
    }
    ......
    public static void main(String[] args) {
        ......
        Looper.prepareMainLooper();
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    ......
}
  • static Activity.main();//没有找到上层调用 o.0
  • static ActivityThread.systemMain();
    • SystemServer.createSystemContext();
      • SystemServer.run();
        • static SystemServer.main();//未找到上层调用 o.0

为啥会有主线程与子线程之分?Looper.prepareMainLooper()与Looper.prepare()有什么区别?

  • 关键在于quitAllowed的值(ActivityThread所在的线程的Looper赋值为false),而quitAllowed最后传给了MessageQueen。
  • MessageQueen.quit()调用的时候,如果quitAllowed为false,则抛出异常:IllegalStateException("Main thread not allowed to quit.")

public final class Looper {
    ......
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    ......
}

由以上可知Activity.main()运行所在的线程就是主线程了,可是为什么没有看到创建Thread的过程啊?

仅在Thread的类注释中发现:

  • 当Java虚拟机启动时,通常有一个非守护进程线程(通常调用名为“main”)
  • Java虚拟机持续执行线程,直到出现以下任一情况
    • 调用exit();
    • 所有非守护线程均已终止;或者在run()中返回;或者抛出异常;

/**
 * 

* When a Java Virtual Machine starts up, there is usually a single * non-daemon thread (which typically calls the method named * main of some designated class). The Java Virtual * Machine continues to execute threads until either of the following * occurs: *

    *
  • The exit method of class Runtime has been * called and the security manager has permitted the exit operation * to take place. *
  • All threads that are not daemon threads have died, either by * returning from the call to the run method or by * throwing an exception that propagates beyond the run * method. *
*/ public class Thread implements Runnable { ...... }

 

 

你可能感兴趣的:(Android开发笔记)