View的层级结构和绘制流程

  • View的层级结构

    • ActivityThread的performLaunchActivity方法

      我们知道,在Activity的创建流程中,在ActivityThread的performLaunchActivity方法中调用了Activity的attach方法,在attach方法里创建了PhoneWindow,然后调用setWindowManager给PhoneWindow设置了WindowManager,这是我们需要理解这一切的前提。

      public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
              boolean hardwareAccelerated) {
          ...
          if (wm == null) {
              wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
          }
          mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
      }
      
      public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
          return new WindowManagerImpl(mContext, parentWindow);
      }
      
      

      通过以上,我们记住PhoneWindow的mWindowManager是WindowManagerImpl。

    • Activity的setContentView方法

      在performLaunchActivity方法的最后会回调Activity的onCreate方法,我们知道,在onCreate流程中会创建出PhoneWindow的DecorView:

     @Override
     public void setContentView(int layoutResID) {
         if (mContentParent == null) {
             //DecorView的创建
             installDecor();
         } ...
     
         if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
             ...
         } else {
             //自定义xml布局的View加载
             mLayoutInflater.inflate(layoutResID, mContentParent);
         }
         ...
     }
     
     ```
     
    *   #### ActivityThread的handleResumeActivity方法
     
     performLaunchActivity流程之后接着会调用handleResumeActivity流程:
     
    
    @Override
    public void handleResumeActivity(IBinder token, ...) {
        ...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            ...
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else ...
            }
        } else if ...
            ...
    }
    
    ```
    
    ViewManager是WindowManager实现的接口,可以看到,这里调用了WindowManager,也就是WindowManagerImpl的addView方法,第一个参数就是要添加的View,也就是DecorView:
    
     @Override
     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
         ...
         mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
     }
     
     ```
     
     mGlobal是WindowManagerGlobal:
     
     ```
     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
     
     public static WindowManagerGlobal getInstance() {
         synchronized (WindowManagerGlobal.class) {
             if (sDefaultWindowManager == null) {
                 sDefaultWindowManager = new WindowManagerGlobal();
             }
             return sDefaultWindowManager;
         }
     }
     
     ```
     
     可见,Android中所有的界面WindowDecorView都是通过同一个WindowManagerGlobal添加的。
     
    *   #### WindowManagerGlobal的addView方法
     
     ```
     public void addView(View view, ViewGroup.LayoutParams params,
             Display display, Window parentWindow) {
         ...
         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
         ...
         ViewRootImpl root;
         View panelParentView = null;
         //单例且有异步需求,因此这里需要同步
         synchronized (mLock) {
             ...
             root = new ViewRootImpl(view.getContext(), display);
             view.setLayoutParams(wparams);
             mViews.add(view);
             mRoots.add(root);
             mParams.add(wparams);
             try {
                 root.setView(view, wparams, panelParentView);
             } catch (RuntimeException e) {
                 if (index >= 0) {
                     removeViewLocked(index, true);
                 }
                 throw e;
             }
         }
     }
     
     ```
     
     我们或许在学习过程中都听过ViewRootImpl的名字,原来它是在这里创建的,它的setView方法如下:
     
     ```
     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
         synchronized (this) {
             if (mView == null) {
                 mView = view;
                     ...
                 mAttachInfo.mRootView = view;
                 ...
                 mAdded = true;
                 ...
                 requestLayout();
                 ...
                 view.assignParent(this);
                 ...
             }
         }
     }
     
     ```
     
     可以看到这里调用了requestLayout方法,对于这个方法我们应该有些眼熟,正是这个方法开启了绘制流程,我们下面会讲到。
     
     assignParent方法的意义是什么呢?
     
     ```
     void assignParent(ViewParent parent) {
         if (mParent == null) {
             mParent = parent;
         } else if (parent == null) {
             mParent = null;
         } else {
             throw new RuntimeException("view " + this + " being added, but"
                     + " it already has a parent");
         }
     }
     
     ```
     
     它给View的mParent方法赋值为ViewRootImpl本身,这是为了在View中调用requestLayout方法时可以回调到ViewRootImpl中(我们有时会通过手动调用requestLayout来触发界面的刷新和变化,像setLayoutParams这一类API内部也会调用requestLayout方法来刷新绘制):
     
     ```
     //View中的requestLayout方法
     public void requestLayout() {
         ...
         if (mParent != null && !mParent.isLayoutRequested()) {
             mParent.requestLayout();
         }
         ...
     }
     
     ```
     
     可见,这里会调用mParent,也就是ViewRootImpl的requestLayout方法,这会触发自上而下的绘制流程。
     
    *   #### 总结
     
     至此,我们总结一下View的层级结构:
     
     1.  所有的Window(即ActivityDialog中的PhoneWindow)都有一个ViewRootImpl,这些ViewRootImpl会被放到WindowManagerGlobal的mRoots(一个ArrayList)中保存,WindowManagerGlobal是单例,负责管理所有的ViewRootImpl2.  ViewRootImpl中的mView就是DecorView3.  DecorView中的mContenParent用来盛放我们xml布局中解析加载出的所有View(不在本篇讨论)。
     
     通过上面的分析,我们知道了对于一个Activity来说,它内部持有了一个PhoneWindowPhoneWindow内部又持有一个DecorView,而在handleResumeActivity流程中,WindowManagerImpl会创建ViewRootImpl,然后把DecorView赋值到它的mView上,DecorView是一个FrameLayout,自定义的xml布局中的根View会被添加到它的mContentParent中,这就是整个View层的结构。
     
    
  • ViewRootImpl的requestLayout方法

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    
    

    调用scheduleTraversals方法:

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            ...
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ...
        }
    }
    
    

    Choreographer的postCallback方逻辑中不管是走哪一路最终都会走到scheduleVsyncLocked中:

    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }
    
    

    mDisplayEventReceiver是FrameDisplayEventReceiver实例,scheduleVsync方法的实现在FrameDisplayEventReceiver的父类DisplayEventReceiver中实现:

    public void scheduleVsync() {
        ...
        nativeScheduleVsync(mReceiverPtr);
          ...
    }
    
    

    nativeScheduleVsync会绑定屏幕刷新信号,在刷新信号到来时,会执行FrameDisplayEventReceiver的run方法,其中会调用doFrame方法,在doFrame方法中会调用:

    doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    
    

    doCallbacks方法中会按照Choreographer.CALLBACK_TRAVERSAL查找mCallbackQueues中的CallbackRecord然后执行它的run方法,CallbackRecord是在前面mChoreographer.postCallback传入的,Choreographer.CALLBACK_TRAVERSAL对应的也就是ViewRootImpl中的mTraversalRunnable:

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
    

    可见最终调用的是doTraversal方法,它内部会调用performTraversals方法:

    private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
          ...
        dispatchApplyInsets(host);
        ...
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
          ...
        performLayout(lp, mWidth, mHeight);
        ...
        performDraw();
        ...
    }
    
    

    performMeasure方法中:

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    
    

    可以看到从这里进入到DecorView的measure流程,performLayout和performDraw也是同理的逻辑,也就是说三大绘制流程就是在这里依次触发的。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值