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中所有的界面Window的DecorView都是通过同一个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(即Activity和Dialog中的PhoneWindow)都有一个ViewRootImpl,这些ViewRootImpl会被放到WindowManagerGlobal的mRoots(一个ArrayList)中保存,WindowManagerGlobal是单例,负责管理所有的ViewRootImpl。
      2. ViewRootImpl中的mView就是DecorView。
      3. DecorView中的mContenParent用来盛放我们xml布局中解析加载出的所有View(不在本篇讨论)。

      通过上面的分析,我们知道了对于一个Activity来说,它内部持有了一个PhoneWindow,PhoneWindow内部又持有一个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也是同理的逻辑,也就是说三大绘制流程就是在这里依次触发的。

你可能感兴趣的:(View的层级结构和绘制流程)