美文网首页view安卓面试宝典面试
Activity的生命周期 和 View的绘制流程 的关联

Activity的生命周期 和 View的绘制流程 的关联

作者: 慕尼黑凌晨四点 | 来源:发表于2021-10-24 20:14 被阅读0次

    问题

    我们都知道Android在子线程中更新UI会报错:

    Only the original thread that created a view hierarchy can touch its views

    但是,现在有如下代码,却可以正常运行。

    private ImageView mImageView;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = (ImageView)findViewById(R.id.iv);
        new Thread(new Runnable() {
            @Override
            public void run() {
                mImageView.setImageResource(R.drawable.ic_book);//更新 ui
            }
        }).start();
    }
    

    所以就从这个问题入手,回顾下Android中的绘制流程。

    声明周期和绘制流程

    我们知道Activity的声明周期是onCreate->onStart->onResume,View的绘制流程是 onMeasure->onLayout ->onDraw,而且,我们在activity的声明周期的这三个阶段是获取不到View的宽高信息的,也就是说View的绘制肯定是发生在activity声明周期onResume之后的,但具体是在哪个阶段就要从源码中找了。

    ActivityThread

    onCreateonStartonResume既然是个方法,肯定是从某个管理Activity声明周期执行的类中被调用的,这个类就是ActivityThread

    handleLaunchActivity

    • #performLaunchActivity

      • 通过反射创建Activity

      • activity.attach

        • mWindow = new PhoneWindow(this, window, activityConfigCallback);

        • mWindow.setWindowManager(
                  (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                  mToken, mComponent.flattenToString(),
                  (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
          mWindowManager = mWindow.getWindowManager();
          
      • mInstrumentation.callActivityOnCreate

        • activity.performCreate进而调用到onCreate
          • 然后setContentView会初始化decorView ,以及我们的视图放到decorView下构成View树

    handleStartActivity

    • activity.performStart("handleStartActivity")
      • mInstrumentation.callActivityOnStart(this)

    handleResumeActivity

    • #performResumeActivity

      • activity.performResume
    • decor.setVisibility(View.INVISIBLE);

    • wm = a.getWindowManager() 是一个WindowManagerImpl

    • wm.addView(decor, l)

      • WindowManagerImpl#addView()

        • WindowManagerGlobal#addView

          • root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            root.setView(view, wparams, panelParentView, userId);
            
            • requestLayout()//view的第一次requestLayout

              • scheduleTraversals

              • mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                //将在下一次Looper轮循的时候调用mTraversalRunnable#run()
                
            • view.assignParent(this);

            • 这里的view是decor,this是ViewRootImpl,也就是将decor的parent设置成了ViewRootImpl

            • 到这里为止,上至ViewRootImpl,到decor,再到咱们自己写的View,这一颗view树就算构建完成了。

    ViewRootImpl下的TraversalRunnable

    TraversalRunnableViewRootImpl下的子类,所以以下这些方法都是写在ViewRootImpl下的。

    所以就像Activity的生命周期相关方法是在ActivityThread中被调用一样,View的绘制流程相关方法被调用的源头就是ViewRootImpl

    • run() -> doTraversal()->performTraversals()
      • performMeasure
      • performLayout
      • performDraw

    这三个方法就对应了View下的onMeasure,onLayout ,onDraw,所以会从ViewRootImpl开始,至上而下的开始绘制流程。

    解答

    再回到最开始的那个问题,再onCreate中开辟线程,竟然可以成功更新UI。

    会一层一层往上执行requestLayout

    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    

    假设这时更新到decorView了,再去往上找ViewRootImpl,因为ViewRootImpl是在onResume之后root.setView()的时候才被设置为decor的parent,所以这个时候decor是没有parent的,所以自然会略过这行代码,直接开始更新了。

    如果为这个线程加上延时,使其在view.assignParent(this)执行完之后,再调用requestLayout,就会执行到ViewRootImpl#requestLayout方法中来:

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    

    这个时候,如果线程不对的话,就会报“子线程不能更新UI”的异常了。

    相关文章

      网友评论

        本文标题:Activity的生命周期 和 View的绘制流程 的关联

        本文链接:https://www.haomeiwen.com/subject/bvldaltx.html