• 你所不知道的WMS


    前面我们完成了Handler和Binder的学习,可以说基础已经打好了。如果有不理解的同学,可以看看我的Framework专栏的其他文章,这里默认大家都是有这方面基础的了。

    我们先来思考一个问题:invalidate会触发其他View的重绘吗?

    答案很明显是会,但是为什么会呢?可能很多人就回答不上来了。还是来看系统源码吧!

    打开我的sublime Text,搜索invalidate(),出来很多东西,然后右击,Goto definition。

    (备注:sublime Text全局搜索 Find->Find Files)

    我们最终会找到View的路径:frameworks/base/core/java/android/view/View.java

    首先我们来看一下invalidate的英文是什么意思,一开始可能大家都会去猜这应该是刷新的意思,但是实际却是废弃的意思。

    step1:

    1. public void invalidate() {
    2. invalidate(true);
    3. }

    step2:

    invalidateCache设置为true表示完全废弃缓存

    1. public void invalidate(boolean invalidateCache) {
    2. invalidateInternal(0, 0, mRight - mLeft,
    3. mBottom - mTop, invalidateCache, true);
    4. }

    step3:

    1. void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
    2. boolean fullInvalidate) {
    3. if (mGhostView != null) {
    4. mGhostView.invalidate(true);
    5. return;
    6. }
    7. if (skipInvalidate()) {
    8. return;
    9. }
    10. if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
    11. || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
    12. || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
    13. || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
    14. if (fullInvalidate) {
    15. mLastIsOpaque = isOpaque();
    16. mPrivateFlags &= ~PFLAG_DRAWN;
    17. }
    18. mPrivateFlags |= PFLAG_DIRTY;
    19. if (invalidateCache) {
    20. mPrivateFlags |= PFLAG_INVALIDATED;
    21. mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
    22. }
    23. // Propagate the damage rectangle to the parent view.
    24. final AttachInfo ai = mAttachInfo;
    25. final ViewParent p = mParent;
    26. if (p != null && ai != null && l < r && t < b) {
    27. final Rect damage = ai.mTmpInvalRect;
    28. damage.set(l, t, r, b);
    29. p.invalidateChild(this, damage);
    30. }
    31. // Damage the entire projection receiver, if necessary.
    32. if (mBackground != null && mBackground.isProjected()) {
    33. final View receiver = getProjectionReceiver();
    34. if (receiver != null) {
    35. receiver.damageInParent();
    36. }
    37. }
    38. }
    39. }

    核心代码:

    1. damage.set(l, t, r, b);
    2. p.invalidateChild(this, damage);

    意思就是把这块区域交给父布局去处理。

    step4:

    1. @Deprecated
    2. @Override
    3. public final void invalidateChild(View child, final Rect dirty) {
    4. final AttachInfo attachInfo = mAttachInfo;
    5. if (attachInfo != null && attachInfo.mHardwareAccelerated) {
    6. // HW accelerated fast path
    7. onDescendantInvalidated(child, child);
    8. return;
    9. }
    10. ViewParent parent = this;
    11. if (attachInfo != null) {
    12. // If the child is drawing an animation, we want to copy this flag onto
    13. // ourselves and the parent to make sure the invalidate request goes
    14. // through
    15. final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
    16. // Check whether the child that requests the invalidate is fully opaque
    17. // Views being animated or transformed are not considered opaque because we may
    18. // be invalidating their old position and need the parent to paint behind them.
    19. Matrix childMatrix = child.getMatrix();
    20. final boolean isOpaque = child.isOpaque() && !drawAnimation &&
    21. child.getAnimation() == null && childMatrix.isIdentity();
    22. // Mark the child as dirty, using the appropriate flag
    23. // Make sure we do not set both flags at the same time
    24. int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
    25. if (child.mLayerType != LAYER_TYPE_NONE) {
    26. mPrivateFlags |= PFLAG_INVALIDATED;
    27. mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
    28. }
    29. final int[] location = attachInfo.mInvalidateChildLocation;
    30. location[CHILD_LEFT_INDEX] = child.mLeft;
    31. location[CHILD_TOP_INDEX] = child.mTop;
    32. if (!childMatrix.isIdentity() ||
    33. (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
    34. RectF boundingRect = attachInfo.mTmpTransformRect;
    35. boundingRect.set(dirty);
    36. Matrix transformMatrix;
    37. if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
    38. Transformation t = attachInfo.mTmpTransformation;
    39. boolean transformed = getChildStaticTransformation(child, t);
    40. if (transformed) {
    41. transformMatrix = attachInfo.mTmpMatrix;
    42. transformMatrix.set(t.getMatrix());
    43. if (!childMatrix.isIdentity()) {
    44. transformMatrix.preConcat(childMatrix);
    45. }
    46. } else {
    47. transformMatrix = childMatrix;
    48. }
    49. } else {
    50. transformMatrix = childMatrix;
    51. }
    52. transformMatrix.mapRect(boundingRect);
    53. dirty.set((int) Math.floor(boundingRect.left),
    54. (int) Math.floor(boundingRect.top),
    55. (int) Math.ceil(boundingRect.right),
    56. (int) Math.ceil(boundingRect.bottom));
    57. }
    58. do {
    59. View view = null;
    60. if (parent instanceof View) {
    61. view = (View) parent;
    62. }
    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. // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
    71. // flag coming from the child that initiated the invalidate
    72. if (view != null) {
    73. if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
    74. view.getSolidColor() == 0) {
    75. opaqueFlag = PFLAG_DIRTY;
    76. }
    77. if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
    78. view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
    79. }
    80. }
    81. parent = parent.invalidateChildInParent(location, dirty);//1
    82. if (view != null) {
    83. // Account for transform on current parent
    84. Matrix m = view.getMatrix();
    85. if (!m.isIdentity()) {
    86. RectF boundingRect = attachInfo.mTmpTransformRect;
    87. boundingRect.set(dirty);
    88. m.mapRect(boundingRect);
    89. dirty.set((int) Math.floor(boundingRect.left),
    90. (int) Math.floor(boundingRect.top),
    91. (int) Math.ceil(boundingRect.right),
    92. (int) Math.ceil(boundingRect.bottom));
    93. }
    94. }
    95. } while (parent != null);
    96. }

    这里面是一个循环,就是如果还有父布局,就一直会调用注释1这个方法。最终会走到decorView。

    step5:

    这个方法是最上层ViewRootImpl里面的

    1. @Override
    2. public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    3. checkThread();
    4. if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
    5. if (dirty == null) {
    6. invalidate();
    7. return null;
    8. } else if (dirty.isEmpty() && !mIsAnimating) {
    9. return null;
    10. }
    11. if (mCurScrollY != 0 || mTranslator != null) {
    12. mTempRect.set(dirty);
    13. dirty = mTempRect;
    14. if (mCurScrollY != 0) {
    15. dirty.offset(0, -mCurScrollY);
    16. }
    17. if (mTranslator != null) {
    18. mTranslator.translateRectInAppWindowToScreen(dirty);
    19. }
    20. if (mAttachInfo.mScalingRequired) {
    21. dirty.inset(-1, -1);
    22. }
    23. }
    24. invalidateRectOnScreen(dirty);//1
    25. return null;
    26. }

    step6:

    1. private void invalidateRectOnScreen(Rect dirty) {
    2. final Rect localDirty = mDirty;
    3. if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
    4. mAttachInfo.mSetIgnoreDirtyState = true;
    5. mAttachInfo.mIgnoreDirtyState = true;
    6. }
    7. // Add the new dirty rect to the current one
    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;
    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. if (!mWillDrawSoon && (intersected || mIsAnimating)) {
    18. scheduleTraversals();//1
    19. }
    20. }

    step7:

    1. void scheduleTraversals() {
    2. if (!mTraversalScheduled) {
    3. mTraversalScheduled = true;
    4. mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
    5. mChoreographer.postCallback(
    6. Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable//1, null);
    7. if (!mUnbufferedInputDispatch) {
    8. scheduleConsumeBatchedInput();
    9. }
    10. notifyRendererOfFramePending();
    11. pokeDrawLockIfNeeded();
    12. }
    13. }

    注意注释1处

    step8:

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

    step9:

    1. void doTraversal() {
    2. if (mTraversalScheduled) {
    3. mTraversalScheduled = false;
    4. mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    5. if (mProfile) {
    6. Debug.startMethodTracing("ViewAncestor");
    7. }
    8. performTraversals();//1
    9. if (mProfile) {
    10. Debug.stopMethodTracing();
    11. mProfile = false;
    12. }
    13. }
    14. }

    step10:

    1. private void performTraversals() {
    2. ···
    3. performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
    4. ···
    5. performLayout(lp, mWidth, mHeight);
    6. ···
    7. performDraw();
    8. }

    这个方法代码非常长,是不是似曾相识了!

    总结一下:某一个View调用invalidate方法,回逐级向上调用直到ViewRootimpl,然后重新走绘制流程。

    相信通过前面的开胃菜已经对View的invalidate有了一定的理解,那么我们再抛出一个问题:Activity如何如window与view进行分工合作的?

    我们知道activity是通过window来管理view的,activity是管理生命周期的,view负责具体显示。

    未完待续!

  • 相关阅读:
    Android11编译第五弹:开启VPN权限
    【LIN总线测试】——LIN主节点调度表测试
    C++ Reference: Standard C++ Library reference: C Library: cwchar: wmemcpy
    学习UI第一天
    selenium多窗口、多iframe切换、alert切换
    信钰证券:积极因素不断积累 机构看多中长期市场
    淘宝/天猫:畅销榜 API 返回值说明
    ABAP 面试题:如何使用 ABAP 编程语言的 System CALL 接口,直接执行 ABAP 服务器所在操作系统的 shell 命令?
    【JavaScript保姆级教程】输出函数和初识变量
    2.MySQL 基础知识
  • 原文地址:https://blog.csdn.net/qq_36428821/article/details/126003744