本文主要探讨能够触发performTraversals()执行的invalidate()、postInvalidate()和requestLayout()方法的流程。在调用这三个方法到最后执行到performTraversals()方法,涉及到到通过Choroegrapher请求Vsync信号,实现按帧绘制的流程,所以还会介绍Choroegrapher类的工作流程。

一、requestLayout()流程

  invalidate()和postInvalidate()能够触发View的重画,这两个方法最终会调用到performTraversals()中的performDraw()来完成重绘制,但是是否会执行onMeasure()和onLayout()过程要根据标志位的状况来决定;requesetLayout()方法也会调用到performTraversals()方法,但是只会执行measure和layout流程,不会调用到draw流程来触发重画动作。直接来看View.requestLayout()代码。

  1. @CallSuper
  2. public void requestLayout() {
  3. if (mMeasureCache != null) mMeasureCache.clear();
  4.      
         //如果当前的整个View树在进行布局流程的话,则会调用requestLayoutDuringLayout()
    //让这次的布局延时执行
  5. if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
  6. // Only trigger request-during-layout logic if this is the view requesting it,
  7. // not the views in its parent hierarchy
  8. ViewRootImpl viewRoot = getViewRootImpl();
  9. if (viewRoot != null && viewRoot.isInLayout()) {
  10. if (!viewRoot.requestLayoutDuringLayout(this)) {
  11. return;
  12. }
  13. }
  14. mAttachInfo.mViewRequestingLayout = this;
  15. }
  16.   
         //PFLAG_FORCE_LAYOUT会在执行View的measure()和layout()方法时判断
    //只有设置过该标志位,才会执行measure()和layout()流程
  17. mPrivateFlags |= PFLAG_FORCE_LAYOUT;
  18. mPrivateFlags |= PFLAG_INVALIDATED;
  19.  
  20. if (mParent != null && !mParent.isLayoutRequested()) {
  21. mParent.requestLayout();
  22. }
  23. if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
  24. mAttachInfo.mViewRequestingLayout = null;
  25. }
  26. }

  该方法主要是设置了PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED到当前View的Flag中,然后调用到当前View(当前View可能是一个控件View,也可能是一个布局View,因为对于这两类View都能调用requestLayout()方法)的父布局View的requestLayout()方法,父布局View是ViewGroup类型,没有重写该requestLayout()方法,所以实际还是调回到View.requestLayout()方法的这套逻辑。这个过程,就是设置当前View标志位后,就不断的向上调用父布局View的requestLayout(),最后调用到根View即DecorView的requestLayout(),而DecorView的mParent变量指向的是当前窗口对应的ViewRootImpl对象,最后一次设置完DecorView标志位后,调用到ViewRootImpl.requestLayout()方法,进入该代码。

  1. @Override
  2. public void requestLayout() {
         //该boolean变量会在ViewRootImpl.performLayout()开始时置为ture,结束置false
    //表示当前不处于Layout过程
         if (!mHandlingLayoutInLayoutRequest) {
  3. checkThread();
  4. mLayoutRequested = true;
  5. scheduleTraversals();
  6. }
  7. }

  如果当前不是正在执行layout过程,则会调用scheduleTraversals()方法,进入ViewRootImpl.scheduleTraversals()。

  1. void scheduleTraversals() {
  2. if (!mTraversalScheduled) {
           //在下一段代码处会置回false
           //表示在排好这次绘制请求前,不再排其它的绘制请求
  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. }

  这里主要是调用到了ViewRootImpl的另一个重要的变量mChoreographer,它是Choreographer类型的,这个对象会请求Vsync信号来控制绘制的进行,实现了按帧进行绘制的机制,这个类会在后文进行介绍。该方法对于绘制的请求经过了Choreographer的编排后,最终会调用回ViewRootImpl.doTraversal()方法。

  1. void doTraversal() {
  2. if (mTraversalScheduled) {
  3. mTraversalScheduled = false;
  4. mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
  5.         
           ... //用于调试相关代码
  6.  
  7. performTraversals();
  8. ... //用于调试相关代码
  9. }
  10. }

  然后调用到ViewRootImpl.performTraversals()方法。

二、invalidate()与postInvalidate()流程 

  invalidate()与postInvalidate()都是用于被调用来触发View的更新(重画)动作,区别在于invalidate()方法是在UI线程自身中使用,而postInvalidate()是非UI线程中使用。 首先来看View.postInvalidate()。

  1.   public void postInvalidate() {
  2. postInvalidateDelayed(0);
  3. }
  4.  
  5. public void postInvalidateDelayed(long delayMilliseconds) {
  6. // We try only with the AttachInfo because there's no point in invalidating
  7. // if we are not attached to our window
  8. final AttachInfo attachInfo = mAttachInfo;
  9. if (attachInfo != null) {
  10. attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
  11. }
  12. }

  调用到了对应的ViewRootImpl对象的dispatchInvalidateDelayed()方法,进入该代码。

  1. public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
  2. Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
  3. mHandler.sendMessageDelayed(msg, delayMilliseconds);
  4. }

  这里实现了一个消息机制,发送了MSG_INVSLIDSTE。进入处理消息的ViewRootImpl.handleMessage()方法。

  1. @Override
  2. public void handleMessage(Message msg) {
  3. switch (msg.what) {
  4. case MSG_INVALIDATE:
  5. ((View) msg.obj).invalidate();
  6. break;
  7. ...
  8. }

  这里实际上就是调回了调用postInvalidate()方法的View的invalidate()方法。由于invalidate()方法只能在UI线程执行,所以postInvalidate只是实现了一个消息机制,让用户能够在非UI线程使用,最终还是调用到invalidate()方法来触发重画,实现界面更新动作。继续来看View.invalidate()方法,该方法逻辑的实际实际上时调用到invalidateInternal()方法来实现的。

  1. public void invalidate() {
  2. invalidate(true);
  3. }
  4.  
  5. void invalidate(boolean invalidateCache) {
         //mLeft、mRigth、mTop、mBottom记录的是当前View边界距离其父布局View边界的距离
  6. invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
  7. }
  8.  
  9. void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
  10. boolean fullInvalidate) {
  11. if (mGhostView != null) {
  12. mGhostView.invalidate(true);
  13. return;
  14. }

  15.      //如果当前视图为不可见状态且没有动画正在执行,且其父布局也没有过渡动画执行,则跳过
  16. if (skipInvalidate()) {
  17. return;
  18. }

  19.      //当前View没有正在执行该方法
         //或绘制缓存可用或未重绘过或透明度发生改变
         //PFLAG_DRAWN会在该方法内去改标志位
         //PFLAG_INVALIDATED会在View.draw()方法执行时去掉该标志位

         if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
  20. || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
  21. || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
  22. || (fullInvalidate && isOpaque() != mLastIsOpaque)) {

  23.        //如果需要全部重绘,invalidate()未传参调用时默认为true
           if (fullInvalidate) {
  24. mLastIsOpaque = isOpaque();
  25. mPrivateFlags &= ~PFLAG_DRAWN;
  26. }
  27.  
  28. mPrivateFlags |= PFLAG_DIRTY;
  29.  
  30. if (invalidateCache) {
  31. mPrivateFlags |= PFLAG_INVALIDATED;
  32. mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
  33. }
  34.  
  35. // Propagate the damage rectangle to the parent view.
           //damage记录的区域是需要更新的dirty区域,当前的坐标时相对于自身来设置的
           //通过不断调用到父类的invalidateChild()方法,来不断更新dirty区域的相对坐标
           final AttachInfo ai = mAttachInfo;
  36. final ViewParent p = mParent;
  37. if (p != null && ai != null && l < r && t < b) {
  38. final Rect damage = ai.mTmpInvalRect;
  39. damage.set(l, t, r, b);
  40. p.invalidateChild(this, damage);
  41. }
  42.  
  43. // Damage the entire projection receiver, if necessary.
  44. if (mBackground != null && mBackground.isProjected()) {
  45. final View receiver = getProjectionReceiver();
  46. if (receiver != null) {
  47. receiver.damageInParent();
  48. }
  49. }
  50.  
  51. // Damage the entire IsolatedZVolume receiving this view's shadow.
  52. if (isHardwareAccelerated() && getZ() != 0) {
  53. damageShadowReceiver();
  54. }
  55. }
  56. }

  这里会通过调用mParent的invalidateChild()方法,来触发父类对于dirty区域的调整(可能会调整可能还是原区域)及改区域相对坐标的调整。进入ViewGroup.invalidateChild()方法。

  1. @Override
  2. public final void invalidateChild(View child, final Rect dirty) {
  3. ViewParent parent = this;
  4.  
  5. final AttachInfo attachInfo = mAttachInfo;
  6. if (attachInfo != null) {
  7. // If the child is drawing an animation, we want to copy this flag onto
  8. // ourselves and the parent to make sure the invalidate request goes
  9. // through
           //drawAnimation记录调用该方法的子View是否正在执行动画
  10. final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
  11. == PFLAG_DRAW_ANIMATION;
  12.  
  13. // Check whether the child that requests the invalidate is fully opaque
  14. // Views being animated or transformed are not considered opaque because we may
  15. // be invalidating their old position and need the parent to paint behind them.
           //调用该方法的子View是否不透明:处于不透明状态且没有在执行动画且变化矩阵没有变化
           //Matrix可以用于View的平移、缩放、扩放、旋转等操作,比如某些应用上的双指缩放功能

  16. Matrix childMatrix = child.getMatrix();
  17. final boolean isOpaque = child.isOpaque() && !drawAnimation &&
  18. child.getAnimation() == null && childMatrix.isIdentity();
  19. // Mark the child as dirty, using the appropriate flag
  20. // Make sure we do not set both flags at the same time
  21. int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
  22.  
  23. if (child.mLayerType != LAYER_TYPE_NONE) {
  24. mPrivateFlags |= PFLAG_INVALIDATED;
  25. mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
  26. }

  27.        final int[] location = attachInfo.mInvalidateChildLocation;
    //记录子View边界距离父View左边界和上边界的距离到Location中,用于下一段代码中的计算
  28. location[CHILD_LEFT_INDEX] = child.mLeft;
  29. location[CHILD_TOP_INDEX] = child.mTop;
           //如果子View设置了变换矩阵,则根据变换矩阵调整dirty区域
  30. if (!childMatrix.isIdentity() ||
  31. (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
  32. RectF boundingRect = attachInfo.mTmpTransformRect;
  33. boundingRect.set(dirty);
  34. Matrix transformMatrix;
  35. if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
  36. Transformation t = attachInfo.mTmpTransformation;
  37. boolean transformed = getChildStaticTransformation(child, t);
  38. if (transformed) {
  39. transformMatrix = attachInfo.mTmpMatrix;
  40. transformMatrix.set(t.getMatrix());
  41. if (!childMatrix.isIdentity()) {
  42. transformMatrix.preConcat(childMatrix);
  43. }
  44. } else {
  45. transformMatrix = childMatrix;
  46. }
  47. } else {
  48. transformMatrix = childMatrix;
  49. }
  50. transformMatrix.mapRect(boundingRect);
  51. dirty.set((int) Math.floor(boundingRect.left),
  52. (int) Math.floor(boundingRect.top),
  53. (int) Math.ceil(boundingRect.right),
  54. (int) Math.ceil(boundingRect.bottom));
  55. }

  56.        //这是一个从当前的布局View向上不断遍历当前布局View的父布局,最后遍历到ViewRootImpl的循环
  57. do {
  58. View view = null;
              //parent可能为ViewGroup类型,也可能为ViewRootImpl类型
    //最后一次循环执行时为ViewRootImpl类型
  59. if (parent instanceof View) {
  60. view = (View) parent;
  61. }

  62.           //如果子View正在执行动画,设置遍历的父布局View的动画标识
  63. if (drawAnimation) {
  64. if (view != null) {
  65. view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
  66. } else if (parent instanceof ViewRootImpl) {
  67. ((ViewRootImpl) parent).mIsAnimating = true;
  68. }
  69. }
  70.  
  71. // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
  72. // flag coming from the child that initiated the invalidate
              //设置当前ViewGroup的Dirty标识,表示当前的ViewGroup需要重绘
  73. if (view != null) {
  74. if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
  75. view.getSolidColor() == 0) {
  76. opaqueFlag = PFLAG_DIRTY;
  77. }
  78. if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
  79. view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
  80. }
  81. }

  82.           //调用当前布局View的invalidateChildParent()方法,返回的值为当前布局View的父布局
              //通过循环向上调用,最后返回的根布局是ViewRootImpl对象
              parent = parent.invalidateChildInParent(location, dirty);
  83. if (view != null) {
  84. // Account for transform on current parent
  85. Matrix m = view.getMatrix();
  86. if (!m.isIdentity()) {
  87. RectF boundingRect = attachInfo.mTmpTransformRect;
  88. boundingRect.set(dirty);
  89. m.mapRect(boundingRect);
  90. dirty.set((int) Math.floor(boundingRect.left),
  91. (int) Math.floor(boundingRect.top),
  92. (int) Math.ceil(boundingRect.right),
  93. (int) Math.ceil(boundingRect.bottom));
  94. }
  95. }
  96. } while (parent != null);
  97. }
  98. }

  在do-while循环中会调用到parent = parent.invalidateChildInParent(location, dirty),这里执行到ViewGroup.invalidateChildInParent()方法。

  1. @Override
  2. public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
         //
  3. if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
  4. (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
            //如果ViewGroup有没有动画执行或者动画已经完成
  5. if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
  6. FLAG_OPTIMIZE_INVALIDATE) {
              //dirty记录的是最开始调到invalidate()的View的区域
    //dirty的四个坐标值值在执行下面代码是相对于当前循环到上一个ViewGroup来确定的
              //这里做了一个偏移动作,偏移的量是当前上一个ViewGroup相对于现在ViewGroup的偏移值
              //做完下面的偏移操作后,dirty的四个坐标就是想对于当前ViewGroup的坐标值了
  7. dirty.offset([CHILD_LEFT_INDEX] - mScrollX,
  8. location[CHILD_TOP_INDEX] - mScrollY);
              //如果当前ViewGroup需要裁剪View
              //则将当前ViewGroup的区域与View的区域做求并集的操作
  9. if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
  10. dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
  11. }
  12.  
  13. final int left = mLeft;
  14. final int top = mTop;

  15.           //如果当前ViewGroup需要裁剪View,且ViewGroup区域与View区域没有并集,则dirty置空
  16. if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
  17. if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
  18. dirty.setEmpty();
  19. }
  20. }
  21. mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
  22.     
              //用于循环到下一个ViewGroup时做offset操作
  23. location[CHILD_LEFT_INDEX] = left;
  24. location[CHILD_TOP_INDEX] = top;
  25.  
  26. if (mLayerType != LAYER_TYPE_NONE) {
  27. mPrivateFlags |= PFLAG_INVALIDATED;
  28. }
  29.  
  30. return mParent;
  31.  
  32. } else {//如果当前ViewGroup中有动画要执行
  33. mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
  34.  
  35. location[CHILD_LEFT_INDEX] = mLeft;
  36. location[CHILD_TOP_INDEX] = mTop;
              //如果需要对子View裁剪则设置dirty为当前ViewGroup区域
    //如果不需要则求当前ViewGroup区域与原ditry区域并集
              if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
  37. dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
  38. } else {
  39. // in case the dirty rect extends outside the bounds of this container
  40. dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
  41. }
  42.  
  43. if (mLayerType != LAYER_TYPE_NONE) {
  44. mPrivateFlags |= PFLAG_INVALIDATED;
  45. }
  46.  
  47. return mParent;
  48. }
  49. }
  50.  
  51. return null;
  52. }

  invalidateChildInParent()主要是完成了dirty区域在调用该方法的ViewGroup中的更新,dirty指示的区域就是需要重绘制的区域。如果ViewGroup没有动画在执行,则dirty区域还是原来的区域,只需要通过偏移操作更改该区域的坐标值从相对于上一个ViewGroup(父ViewGroup),到相对于当前ViewGroup;如果有动画要执行,则表示当前整个ViewGroup都需要重绘,更改dirty值为当前ViewGroup 区域。

  do-while最后一次循环最后会调用到ViewRootImpl.invalidateChildInParent()方法,进入该代码。

  1. @Override
  2. public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
  3. checkThread();
  4. if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);

  5.      //如果传入一个null drity,则表示要重绘当前ViewRootImpl指示的整个区域
         //如果传入一个empty dirty,则表示经过计算需要重绘的区域不需要绘制
         if (dirty == null) {
  6. invalidate();
  7. return null;
  8. } else if (dirty.isEmpty() && !mIsAnimating) {
  9. return null;
  10. }

  11.      ...
  12. invalidateRectOnScreen(dirty);
  13. return null;
  14. }

  调用到了ViewRootImpl.invalidateRectOnScreen()方法,进入该代码。

  1. private void invalidateRectOnScreen(Rect dirty) {
         //mDirty记录的是当前ViewRootImpl里还未进行重绘需要重绘的区域
    //mDirty会在ViewRootImpl.draw()方法结尾处设置为empty
         final Rect localDirty = mDirty;
  2. if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
  3. mAttachInfo.mSetIgnoreDirtyState = true;
  4. mAttachInfo.mIgnoreDirtyState = true;
  5. }
  6.  
  7. // Add the new dirty rect to the current one
    //当前已有的dirty区域与此次dirty区域做并集
  8. localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
  9. // Intersect with the bounds of the window to skip
  10. // updates that lie outside of the visible region
  11. final float appScale = mAttachInfo.mApplicationScale;
         //处理窗口缩放与做完并集的localDirty做交集
  12. final boolean intersected = localDirty.intersect(0, 0,
  13. (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
  14. //如果没有交集
         if (!intersected) {
  15. localDirty.setEmpty();
  16. }
  17.  
  18.      //mWillDrawSoon在performTraversals()方法开始时置为true,结束时置false
         //如果没有在执行performTraversals &&(intersected || 正在执行动画)
  19. if (!mWillDrawSoon && (intersected || mIsAnimating)) {
  20. scheduleTraversals();
  21. }
  22. }

  最后会调用到scheduleTraversals()方法,后续在请求到Vsync信号后,便会调用到peformTraversals()方法。

三、Choreographer类分析

  “编舞类”Choreoprapher的作用是编排输入事件、动画事件和绘制事件的执行,通过调用Choreoprapher.postCallback()方法,向Choreoprapher加入需要编排的事件,而Choreoprapher则通过请求Vsync信号,来控制这些事件按照屏幕刷新周期有规律的执行,即是实现了按帧绘制的机制。

  在ViewRootImpl中,会调用mChoreographer = Choreographer.getInstance()来初始化一个Choreographer变量。进入Choreographer.getInstance()代码。

  1. private static final ThreadLocal<Choreographer> sThreadInstance =
  2. new ThreadLocal<Choreographer>() {
  3. @Override
  4. protected Choreographer initialValue() {
  5. Looper looper = Looper.myLooper();
  6. if (looper == null) {
  7. throw new IllegalStateException("The current thread must have a looper!");
  8. }
  9. return new Choreographer(looper);
  10. }
  11. };
  12.  
  13. public static Choreographer getInstance() {
  14. return sThreadInstance.get();
  15. }

  这里实际调用了ThreadLocal类型的静态常量的get()方法,ThreadLocal中保存的类型是Choreographer类。根据ThreadLocal机制,sThreadInstance.get()方法会调用到上面代码中实现的initialValue()方法,该方法返回一个Choregrapher类型对象,返回的该对象即作为getInstance()方法的返回,也是最后赋值给了ViewRootImpl中的mChoreogropher变量。在initialValue()方法中会new一个Choreographer对象,进入构建方法。

  1. private Choreographer(Looper looper) {
  2.      //调用该方法的源头是UI线程,所有looper为UI线程的looper
  3. mLooper = looper;
  4. mHandler = new FrameHandler(looper);
  5.      //如果系统使用Vsync机制,则创建一个Vsync信号的接收器FrameDisplayEventReceiver类
  6. mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
  7. mLastFrameTimeNanos = Long.MIN_VALUE;
  8.  
  9. mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
  10.  
  11.      //创建回调数组,CALLBAKCK_LAST=3,后文详解
  12. mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
  13. for (int i = 0; i <= CALLBACK_LAST; i++) {
  14. mCallbackQueues[i] = new CallbackQueue();
  15. }
  16. }

  首先来说mCallbackQueues,这是一个长度为4的CallbackQueue类型的数组,即保存了四个回调队列。每个回调队列能够保存多个CallbackRecord,即是回调事件。这四个队列分别保存四类回调事件:Input事件、Animation事件、Draw事件,还有一种是用来解决动画启动问题的事件。在ViewRootImpl.scheduleTraversals()方法中,便会调用相关方法向队列中添加一个Draw事件,并触发后续到请求信号来处理事件的动作。

  1. void scheduleTraversals() {
  2. ...
  3.  
  4. mChoreographer.postCallback(
  5. Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
  6. ...
  7. }

  继续来看Choreographer.postCallback()方法,该方法是调用到postCallbackDelayedInternal()方法来实现主要逻辑。

  1.    public void postCallback(int callbackType, Runnable action, Object token) {
  2. postCallbackDelayed(callbackType, action, token, 0);
  3. }
  4.  
  5. public void postCallbackDelayed(int callbackType,
  6. Runnable action, Object token, long delayMillis) {
  7. ... //异常情况判断
  8. postCallbackDelayedInternal(callbackType, action, token, delayMillis);
  9. }
  10.  
  11. private void postCallbackDelayedInternal(int callbackType,
  12. Object action, Object token, long delayMillis) {
  13. ... // Debug log
  14.  
  15. synchronized (mLock) {
  16. final long now = SystemClock.uptimeMillis();
  17. final long dueTime = now + delayMillis;
  18.        //将此次回调事件添加到对应类型的事件队列
  19. mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
  20.  
  21. if (dueTime <= now) {
  22.           //立刻安排执行
  23. scheduleFrameLocked(now);
  24. } else {
  25.           //延时处理,还是会调用到scheduleFrameLocked()方法
  26. Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
  27. msg.arg1 = callbackType;
  28. msg.setAsynchronous(true);
  29. mHandler.sendMessageAtTime(msg, dueTime);
  30. }
  31. }
  32. }

  调用addCallbackLock()方法,会根据本次事件信息生成一个CallbackRecord,添加到队列中,但并不一定添加在队列到尾部。队列中所有事件的排列是按照dueTime的值由小到大排列大,即越快要求执行的事件排列得越前,所以在添加事件到队列时会根据dueTime插入到对应的位置。

  插入队列操作完成后,会调用scheduleFrameLoacked()方法。

  1.   private void scheduleFrameLocked(long now) {
  2. if (!mFrameScheduled) {
  3. mFrameScheduled = true;
  4. if (USE_VSYNC) { //如果使用了Vsync机制
  5. if (DEBUG_FRAMES) {
  6. Log.d(TAG, "Scheduling next frame on vsync.");
  7. }
  8.  
  9. // If running on the Looper thread, then schedule the vsync immediately,
  10. // otherwise post a message to schedule the vsync from the UI thread
  11. // as soon as possible.
  12.           //如果前线程开启了Looper,则调用scheduleVsyncLocked()请求Vsync信号
  13. if (isRunningOnLooperThreadLocked()) {
  14. scheduleVsyncLocked();
  15. } else {//如果当前线程未启动Looper
  16.             //则发消息到调用创建Choreographer的线程来请求Vsync信号
  17. Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
  18. msg.setAsynchronous(true);
  19. mHandler.sendMessageAtFrontOfQueue(msg);
  20. }
  21. } else {//如果未使用Vsync机制,则手动计算下一次绘制时间,使用延时消息来控制
  22. final long nextFrameTime = Math.max(
  23. mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
  24. if (DEBUG_FRAMES) {
  25. Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
  26. }
  27. Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
  28. msg.setAsynchronous(true);
  29. mHandler.sendMessageAtTime(msg, nextFrameTime);
  30. }
  31. }
  32. }

  一般情况下是实用Vsync机制的,且scheduleFrameLocked()也是被UI线程调用执行的,所以直接调用到Choreographer.scheduleVsyncLocked()方法,进入该代码。

  1. private void scheduleVsyncLocked() {
  2. mDisplayEventReceiver.scheduleVsync();
  3. }

  这里直接调用到mDisplayEventReceiver的scheduleVsync()方法,该变量是FrameDisplayEventReceiver类型的,该类继承自DisplayEventReceiver类。scheduleVsync()相当于发起了一次Vsync请求,这样在请求之后下一个Vsync信号发出时,FrameDisplayEventReceiver类便能接收到这词Vsync信号,会调用到FrameDisplayEventReceiver类的onVsync()方法,在onVsync()方法中会发送消息到UI线程,调用到doFrame()方法,Frame是帧的意思,doFrame则表示这次接收到Vsync信号的这一帧内要做的事,进入FrameDisplayEventReceiver.doFrame()方法(FrameDisplayEventReceiver类时Choreographer内部类),

  1. void doFrame(long frameTimeNanos, int frame) {
  2. final long startNanos;
  3. synchronized (mLock) {
           //该变量会在scheduleFrameLocked()方法开始时设置为true,本方法结束置为false
           //表示有callback事件需要安排执行
  4. if (!mFrameScheduled) {
  5. return; // no work to do
  6. }
  7.  
  8. if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
  9. mDebugPrintNextFrameTimeDelta = false;
  10. Log.d(TAG, "Frame time delta: "
  11. + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
  12. }

  13.         //frameTimeNanos表示Vsync信号发出的时间或者帧开始的时间
  14. long intendedFrameTimeNanos = frameTimeNanos;
            //当前时间
  15. startNanos = System.nanoTime();
  16. final long jitterNanos = startNanos - frameTimeNanos;
            //当前时间距离Vsync信号时间超过了屏幕的刷新周期,即一帧16ms的时间
  17. if (jitterNanos >= mFrameIntervalNanos) {
  18. final long skippedFrames = jitterNanos / mFrameIntervalNanos;
              //如果超过太多,即跳过了太多帧,则打出Log提示跳过了太多帧,可能是主线程做了太多事了
  19. if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
  20. Log.i(TAG, "Skipped " + skippedFrames + " frames! "
  21. + "The application may be doing too much work on its main thread.");
  22. }
  23. final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
  24. if (DEBUG_JANK) {
  25. Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
  26. + "which is more than the frame interval of "
  27. + (mFrameIntervalNanos * 0.000001f) + " ms! "
  28. + "Skipping " + skippedFrames + " frames and setting frame "
  29. + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
  30. }
  31. frameTimeNanos = startNanos - lastFrameOffset;
  32. }
  33.   
            //如果距离最后一帧时间未超过屏幕刷新周期,则重新请求Vsync信号
  34. if (frameTimeNanos < mLastFrameTimeNanos) {
  35. if (DEBUG_JANK) {
  36. Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
  37. + "previously skipped frame. Waiting for next vsync.");
  38. }
  39. scheduleVsyncLocked();
  40. return;
  41. }
  42.  
  43. mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
  44. mFrameScheduled = false;
           //设置本次帧的执行时间为最后一次的帧执行时间
  45. mLastFrameTimeNanos = frameTimeNanos;
  46. }
  47.  
  48. try {
  49. Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
  50. AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

  51.        //依次从队列中取出这四类事件进行执行
           //但不一定都会执行这四类事件,要看队列中是否有post过且符合这一帧执行到条件的事件

  52. mFrameInfo.markInputHandlingStart();
  53. doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

  54. mFrameInfo.markAnimationsStart();
  55. doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
  56.  
  57. mFrameInfo.markPerformTraversalsStart();
  58. doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

  59. doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
  60. } finally {
  61. AnimationUtils.unlockAnimationClock();
  62. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  63. }
  64.  
  65. if (DEBUG_FRAMES) {
  66. final long endNanos = System.nanoTime();
  67. Log.d(TAG, "Frame " + frame + ": Finished, took "
  68. + (endNanos - startNanos) * 0.000001f + " ms, latency "
  69. + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
  70. }
  71. }

  该方法会调用doCallbacks方法来依次执行当前时间对应的四类事件。由于CALLBACK_COMMIT是一种修正属性动画启动事件过长导致掉帧问题的一种机制,并不是真正会执行在主线程的流程,这里不做详解。所以在执行事件时,主要是依次执行了input、animation和traversal事件。我们可以抓一个systrace来直观的了解这个过程,以UC浏览器双指扩放页面的绘制过程中的某一帧为例。

  doFrame()方法中首先执行来input事件的处理,然后后面有个很短的矩形体条,执行的是animation事件;之后便是执行到了traversal事件,在执行traversal流程中执行了draw流程,但并没有执行measure和layout流程,因为本次绘制不需要重新测量和布局;在执行draw流程过程中实际调用到了View的draw()方法。

  继续来看Choroegrapher.doCallbacks()方法的实现。

  1. void doCallbacks(int callbackType, long frameTimeNanos) {
  2. CallbackRecord callbacks;
  3. synchronized (mLock) {
  4. // We use "now" to determine when callbacks become due because it's possible
  5. // for earlier processing phases in a frame to post callbacks that should run
  6. // in a following phase, such as an input event that causes an animation to start.
  7. final long now = System.nanoTime();
           //根据帧开始的时间,取出当前该类型队列中的一个callback事件
  8. callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
  9. now / TimeUtils.NANOS_PER_MS);
  10. if (callbacks == null) {
  11. return;
  12. }
  13. mCallbacksRunning = true;
  14.  
  15. ... //CALLBACK_COMMIT事件的处理try {
  16. Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
  17. for (CallbackRecord c = callbacks; c != null; c = c.next) {
  18. if (DEBUG_FRAMES) {
  19. Log.d(TAG, "RunCallback: type=" + callbackType
  20. + ", action=" + c.action + ", token=" + c.token
  21. + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
  22. }
  23. c.run(frameTimeNanos);
  24. }
  25. } finally {
  26. synchronized (mLock) {
  27. mCallbacksRunning = false;
  28. do {
  29. final CallbackRecord next = callbacks.next;
  30. recycleCallbackLocked(callbacks);
  31. callbacks = next;
  32. } while (callbacks != null);
  33. }
  34. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  35. }
  36. }

  首先来看下CallbackQueue.extractDueCallbacksLocked()方法,了解队列取事件执行的机制。

  1. public CallbackRecord extractDueCallbacksLocked(long now) {
           //返回队列头事件,即要求最快要执行的事件
  2. CallbackRecord callbacks = mHead;
  3. if (callbacks == null || callbacks.dueTime > now) {
  4. return null;
  5. }

  6.        //把头回调事件后面所有执行时间已经到了事件全部舍弃
           CallbackRecord last = callbacks;
  7. CallbackRecord next = last.next;
  8. while (next != null) {
  9. if (next.dueTime > now) {
  10. last.next = null;
  11. break;
  12. }
  13. last = next;
  14. next = next.next;
  15. }
           //next表示的是未到执行时间且要求执行到时间最早的事件
  16. mHead = next;
  17. return callbacks;
  18. }

  取出当前帧需要执行的回调事件后,便会执行到该事件的run()方法,在使用这里会调用到CallbackRecord的run()方法。

  1. private static final class CallbackRecord {
  2. public CallbackRecord next;
  3. public long dueTime;
  4. public Object action; // Runnable or FrameCallback
  5. public Object token;
  6.  
  7. public void run(long frameTimeNanos) {
  8. if (token == FRAME_CALLBACK_TOKEN) {
  9. ((FrameCallback)action).doFrame(frameTimeNanos);
  10. } else {
  11. ((Runnable)action).run();
  12. }
  13. }
  14. }

  回想我们在ViewRootImpl中调用postCallback()方法的三个参数值,第一个事件类型为Choreographer.CALLBACK_TRAVERSAL,表示是绘制事件,用于指示该事件放入对应队列,第二个则是一个TraversalRunnable类型的Runnable,则赋值给了这里的action,第三个是null,所以上面代码的run()方法,实际执行到了TraversalRunnable的run()方法。

  1. final class TraversalRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. doTraversal();
  5. }
  6. }

  该方法则调用到了doTraversal()方法,后续则调用到了ViewRootImpl.performTraversals()方法。由于run在了UI线程,所以后续到绘制动作也是在UI线程执行到。至此完成了Choroegrapher类的分析。

源码分析篇 - Android绘制流程(三)requestLayout()与invalidate()流程及Choroegrapher类分析的更多相关文章

  1. 源码分析篇 - Android绘制流程(二)measure、layout、draw流程

    performTraversals方法会经过measure.layout和draw三个流程才能将一帧View需要显示的内容绘制到屏幕上,用最简化的方式看ViewRootImpl.performTrav ...

  2. 源码分析篇 - Android绘制流程(一)窗口启动流程分析

    Activity.View.Window之间的关系可以用以下的简要UML关系图表示,在这里贴出来,比较能够帮组后面流程分析部分的阅读. 一.Activity的启动流程 在startActivity() ...

  3. 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02

    百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...

  4. 64位下Hook NtOpenProcess的实现进程保护 + 源码 (升级篇 )

    64位下Hook NtOpenProcess的实现进程保护 + 源码 (升级篇 ) [PS: 如果在64位系统下,出现调用测试demo,返回false的情况下,请修改Hook Dll的代码] glhH ...

  5. 转载---- 使用opencv源码自己编制android so库的过程

    http://blog.csdn.net/lantishua/article/details/21182965 工作需要,在Android上使用OpenCV.opencv当前的版本(2.4.8)已经有 ...

  6. Mybati源码解析篇之六剑客!!!

    目录 前言 环境版本 Mybatis的六剑客 SqlSession 有何方法 语句执行方法 立即批量更新方法 事务控制方法 本地缓存方法 获取映射方法 有何实现类? Executor 实现类 Base ...

  7. 开源项目Telegram源码 Telegram for Android Source

    背景介绍 Telegram 是一款跨平台的即时通信软件,它的客户端是自由及开放源代码软件.用户可以相互交换加密与自毁消息,发送照片.影片等所有类型文件.官方提供手机版.桌面版和网页版等多种平台客户端. ...

  8. Android view中的requestLayout和invalidate方法

    Android view中的requestLayout和invalidate方法 requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent v ...

  9. lua源码学习篇三:赋值表达式解析的流程

    上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式.lua语言支持多变量赋值.本文先从单变量赋值表达式讲起. a = b = c = a + b 对于简单的两个数的求和过程,lu ...

随机推荐

  1. 微信小程序之弹框modal

    官方文档 <modal hidden="{{hidden}}" title="这里是title" confirm-text="自定义确定按钮&q ...

  2. 学习node.js的C++扩展

    本想买本书,可是太贵,了一下作者可惜没有回应,不然也会去支持一下.于是自己baidu罗.先是从这个入手 安装好环境 https://github.com/nodejs/node-gyp#install ...

  3. rhel5.4+oracle 10g rac

    各种报错各种愁啊 ... 1> 不知道什么原因,在节点2执行root.sh 报错 .无解 . 还原虚拟机,重新安装 .唯一与以前不同的是,执行orainroot.sh后 接着在节点2执行.再去分 ...

  4. 1071 Speech Patterns

    People often have a preference among synonyms of the same word. For example, some may prefer "t ...

  5. sdut 3916

    这道题就是二分枚举加贪心,小蓝书上一开始就讲的,但是我给忘了,很难受 #include <iostream> #include <cstdio> #include <cs ...

  6. cxGrid的FilterRow默认自动匹配左边%而不是右边%

    /==============================================================================// 修改cxGrid的FilterRow ...

  7. CentOS使用vsftpd开启FTP服务以及配置用户

    1.安装服务 #yum install vsftpd 2.配置 #vi /etc/vsftpd/vsftpd.conf # 禁止匿名访问 anonymous_enable=NO # 允许本地用户登录F ...

  8. #loj3090 [BJOI2019] 勘破神机

    简单线性代数练习题 首先翻开具体数学生成函数一章,可以发现\(F(n),G(n)\)满足以下递推式 \[F(n)=F(n-1)+F(n-2),F(0)=1,F(1)=1\] \[G(n)=4G(n-2 ...

  9. Linux巩固记录(4) 运行hadoop 2.7.4自带demo程序验证环境

    本节主要使用hadoop自带的程序运行demo来确认环境是否正常 1.首先创建一个input.txt文件,里面任意输入些单词,有部分重复单词 2.将input文件拷贝到hdfs 3.执行hadoop程 ...

  10. 跟踪spring MVC的请求

    当我们点击一个超链接时,spring MVC在后台都做了些什么呢,今天就来看看后台都干了啥 首先需要在web.xml里配置一下: