当前位置:   article > 正文

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

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

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

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

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

从源码中可以看出:

  • mThread的赋值是在ViewRootImpl的构造函数中确定的。
  • 在ViewRootImpl在调用requestLayout,invalideteChildInParent等似乎与UI相关的函数时,都执行了checkThread();
  1. public final class ViewRootImpl implements ViewParent,
  2. View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
  3. public ViewRootImpl(Context context, Display display) {
  4. ......
  5. mThread = Thread.currentThread();
  6. ......
  7. }
  8. ......
  9. @Override
  10. public void requestFitSystemWindows() {
  11. checkThread();
  12. mApplyInsetsRequested = true;
  13. scheduleTraversals();
  14. }
  15. @Override
  16. public void requestLayout() {
  17. if (!mHandlingLayoutInLayoutRequest) {
  18. checkThread();
  19. mLayoutRequested = true;
  20. scheduleTraversals();
  21. }
  22. }
  23. ......
  24. @Override
  25. public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
  26. checkThread();
  27. if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
  28. ......
  29. }
  30. ......
  31. @Override
  32. public void requestTransparentRegion(View child) {
  33. // the test below should not fail unless someone is messing with us
  34. checkThread();
  35. if (mView == child) {
  36. ......
  37. requestLayout();
  38. }
  39. }
  40. ......
  41. @Override
  42. public void requestChildFocus(View child, View focused) {
  43. if (DEBUG_INPUT_RESIZE) {
  44. Log.v(mTag, "Request child focus: focus now " + focused);
  45. }
  46. checkThread();
  47. scheduleTraversals();
  48. }
  49. @Override
  50. public void clearChildFocus(View child) {
  51. if (DEBUG_INPUT_RESIZE) {
  52. Log.v(mTag, "Clearing child focus");
  53. }
  54. checkThread();
  55. scheduleTraversals();
  56. }
  57. @Override
  58. public void focusableViewAvailable(View v) {
  59. checkThread();
  60. if (mView != null) {
  61. ......
  62. }
  63. }
  64. @Override
  65. public void recomputeViewAttributes(View child) {
  66. checkThread();
  67. if (mView == child) {
  68. ......
  69. }
  70. }
  71. ......
  72. /**
  73. * {@inheritDoc}
  74. */
  75. @Override
  76. public void playSoundEffect(int effectId) {
  77. checkThread();
  78. ......
  79. }
  80. ......
  81. /**
  82. * {@inheritDoc}
  83. */
  84. @Override
  85. public View focusSearch(View focused, int direction) {
  86. checkThread();
  87. if (!(mView instanceof ViewGroup)) {
  88. return null;
  89. }
  90. return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
  91. }
  92. /**
  93. * {@inheritDoc}
  94. */
  95. @Override
  96. public View keyboardNavigationClusterSearch(View currentCluster,
  97. @FocusDirection int direction) {
  98. checkThread();
  99. return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
  100. mView, currentCluster, direction);
  101. }
  102. ......
  103. void doDie() {
  104. checkThread();
  105. if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
  106. ......
  107. }
  108. ......
  109. void checkThread() {
  110. if (mThread != Thread.currentThread()) {
  111. throw new CalledFromWrongThreadException(
  112. "Only the original thread that created a view hierarchy can touch its views.");
  113. }
  114. }
  115. ......
  116. }

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

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

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

确认了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是一对一的关系
  1. public final class WindowManagerGlobal {
  2. ......
  3. public void addView(View view, ViewGroup.LayoutParams params,
  4. Display display, Window parentWindow) {
  5. ......
  6. root = new ViewRootImpl(view.getContext(), display);
  7. view.setLayoutParams(wparams);
  8. mViews.add(view);
  9. mRoots.add(root);
  10. mParams.add(wparams);
  11. // do this last because it fires off messages to start doing things
  12. try {
  13. root.setView(view, wparams, panelParentView);
  14. } catch (RuntimeException e) {
  15. // BadTokenException or InvalidDisplayException, clean up.
  16. if (index >= 0) {
  17. removeViewLocked(index, true);
  18. }
  19. throw e;
  20. }
  21. }
  22. }
  23. ......
  24. }

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

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

  1. public final class WindowManagerImpl implements WindowManager {
  2. private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
  3. private final Context mContext;
  4. private final Window mParentWindow;
  5. private IBinder mDefaultToken;
  6. public WindowManagerImpl(Context context) {
  7. this(context, null);
  8. }
  9. private WindowManagerImpl(Context context, Window parentWindow) {
  10. mContext = context;
  11. mParentWindow = parentWindow;
  12. }
  13. public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
  14. return new WindowManagerImpl(mContext, parentWindow);
  15. }
  16. public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
  17. return new WindowManagerImpl(displayContext, mParentWindow);
  18. }
  19. ......
  20. @Override
  21. public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
  22. applyDefaultToken(params);
  23. mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
  24. }
  25. }

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

看下Activity中的实现:

  • 通过WindowManagerImpl.addView()新建一个ViewRoot与Activity中的mDecorView绑定
  1. public class Activity extends ContextThemeWrapper
  2. implements LayoutInflater.Factory2,
  3. Window.Callback, KeyEvent.Callback,
  4. OnCreateContextMenuListener, ComponentCallbacks2,
  5. Window.OnWindowDismissedCallback, WindowControllerCallback,
  6. AutofillManager.AutofillClient {
  7. ......
  8. void makeVisible() {
  9. if (!mWindowAdded) {
  10. ViewManager wm = getWindowManager();
  11. wm.addView(mDecor, getWindow().getAttributes());
  12. mWindowAdded = true;
  13. }
  14. mDecor.setVisibility(View.VISIBLE);
  15. }
  16. ......
  17. }

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中有一次调用
  1. public class TransactionExecutor {
  2. ......
  3. private ClientTransactionHandler mTransactionHandler;
  4. ......
  5. public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
  6. mTransactionHandler = clientTransactionHandler;
  7. }
  8. }
  1. public final class ActivityThread extends ClientTransactionHandler {
  2. ......
  3. private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
  4. ......
  5. }

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

  1. public final class ActivityThread extends ClientTransactionHandler {
  2. ......
  3. public static ActivityThread systemMain() {
  4. ......
  5. ActivityThread thread = new ActivityThread();
  6. thread.attach(true, 0);
  7. return thread;
  8. }
  9. ......
  10. public static void main(String[] args) {
  11. ......
  12. Looper.prepareMainLooper();
  13. ......
  14. ActivityThread thread = new ActivityThread();
  15. thread.attach(false, startSeq);
  16. Looper.loop();
  17. throw new RuntimeException("Main thread loop unexpectedly exited");
  18. }
  19. ......
  20. }
  • 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.")
  1. public final class Looper {
  2. ......
  3. public static void prepare() {
  4. prepare(true);
  5. }
  6. private static void prepare(boolean quitAllowed) {
  7. if (sThreadLocal.get() != null) {
  8. throw new RuntimeException("Only one Looper may be created per thread");
  9. }
  10. sThreadLocal.set(new Looper(quitAllowed));
  11. }
  12. /**
  13. * Initialize the current thread as a looper, marking it as an
  14. * application's main looper. The main looper for your application
  15. * is created by the Android environment, so you should never need
  16. * to call this function yourself. See also: {@link #prepare()}
  17. */
  18. public static void prepareMainLooper() {
  19. prepare(false);
  20. synchronized (Looper.class) {
  21. if (sMainLooper != null) {
  22. throw new IllegalStateException("The main Looper has already been prepared.");
  23. }
  24. sMainLooper = myLooper();
  25. }
  26. }
  27. ......
  28. }

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

仅在Thread的类注释中发现:

  • 当Java虚拟机启动时,通常有一个非守护进程线程(通常调用名为“main”)
  • Java虚拟机持续执行线程,直到出现以下任一情况
    • 调用exit();
    • 所有非守护线程均已终止;或者在run()中返回;或者抛出异常;
  1. /**
  2. * <p>
  3. * When a Java Virtual Machine starts up, there is usually a single
  4. * non-daemon thread (which typically calls the method named
  5. * <code>main</code> of some designated class). The Java Virtual
  6. * Machine continues to execute threads until either of the following
  7. * occurs:
  8. * <ul>
  9. * <li>The <code>exit</code> method of class <code>Runtime</code> has been
  10. * called and the security manager has permitted the exit operation
  11. * to take place.
  12. * <li>All threads that are not daemon threads have died, either by
  13. * returning from the call to the <code>run</code> method or by
  14. * throwing an exception that propagates beyond the <code>run</code>
  15. * method.
  16. * </ul>
  17. */
  18. public
  19. class Thread implements Runnable {
  20. ......
  21. }

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/251035
推荐阅读
相关标签
  

闽ICP备14008679号