幾個PFLAG的作用
-
PFLAG_DRAW_ANIMATION:表示當前view在做Animation動畫。
-
PFLAG_HAS_BOUNDS:表示此view是否layout過。
-
PFLAG_DRAWN :當invalidate時會把此標記刪除,當調用draw方法(包括軟件硬件兩個都設置了),
-
PFLAG_DRAWING_CACHE_VALID: 表示當前cache是否有效,
如果父view是soft layer,那么在把繪制分發到此view時,會用這個flag來判斷是否進行重建drawing cache,不管該view的layerType是hard/soft。
-
PFLAG_INVALIDATED :指示當前view明確是invalidated,而不只是因為其子view invalidate而dirty。該標志用於確定何時需要重新創建view的 display list(而不是僅僅返回對其現有 display list的引用)。
好像軟件繪制沒有判斷PFLAG_INVALIDATED的,但在硬件加速中view.mRecreateDisplayList是根據此標記來設置。
-
PFLAG_DIRTY:View flag indicating whether this view was invalidated (fully or partially.)
當前view調用了invalidate,或當前view的子view調用了invalidate,都會是的當前view加上此flag。
軟件繪制
是基於Android8.0的源碼來分析的。
前提是window是關閉了硬件加速。
實際閱讀源碼並實驗,得出通常情況下的軟件繪制刷新邏輯:
1. 默認情況下,View的clipChildren屬性為true,即每個View繪制區域不能超出其父View的范圍。如果設置一個頁面根布局的clipChildren屬性為false,則子View可以超出父View的繪制區域。
2. 當一個View觸發invalidate,且沒有播放動畫、沒有觸發layout的情況下:
2. clipChildren為false時,ViewGroup.invalidateChildInParent()中會把臟區擴大到自身整個區域,於是與這個區域重疊的所有View都會重繪。
硬件加速
是基於Android8.0的源碼來分析的。
硬件加速下LAYER_TYPE_NONE/LAYER_TYPE_HARDWARE
調用不會導致invalidate的方法流程
以setTranslationX為例
public void setTranslationX(float translationX) { if (translationX != getTranslationX()) { invalidateViewProperty(true, false); mRenderNode.setTranslationX(translationX); invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } }
可以看到setTranslationX中會把translationX設置到mRenderNode中。
void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) { if (!isHardwareAccelerated() || !mRenderNode.isValid() || (mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) { if (invalidateParent) { invalidateParentCaches(); } if (forceRedraw) { mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation } invalidate(false); } else { damageInParent(); } }
由於是在硬件加速並且沒有在做Animation動畫,所以會調用damageInParent()。
protected void damageInParent() { if (mParent != null && mAttachInfo != null) { mParent.onDescendantInvalidated(this, this); } }
onDescendantInvalidated的兩個參數:child表示包含target的當前view的直接子view,target表示引發向上遍歷的view。
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { /* * HW-only, Rect-ignoring damage codepath * * We don't deal with rectangles here, since RenderThread native code computes damage for * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area) */ // if set, combine the animation flag into the parent mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION); if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) { // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential // optimization in provides in a DisplayList world. mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; // simplified invalidateChildInParent behavior: clear cache validity to be safe... mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // ... and mark inval if in software layer that needs to repaint (hw handled in native) if (mLayerType == LAYER_TYPE_SOFTWARE) { // Layered parents should be invalidated. Escalate to a full invalidate (and note that // we do this after consuming any relevant flags from the originating descendant) mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY; target = this; } if (mParent != null) { mParent.onDescendantInvalidated(this, target); } }
由上可知target的祖先view的mPrivateFlags中都加上了PFLAG_DIRTY,去掉了PFLAG_DRAWING_CACHE_VALID。
最終調用到了ViewRootImpl中的onDescendantInvalidated,並安排了一次Traversals:
public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) { if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) { mIsAnimating = true; } invalidate(); } void invalidate() { mDirty.set(0, 0, mWidth, mHeight); if (!mWillDrawSoon) { scheduleTraversals(); } }
接下來就是draw的過程
ViewRootImpl.draw的過程會調用ThreadedRenderer.draw
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) { ... updateRootDisplayList(view, callbacks); ... } private void updateRootDisplayList(View view, DrawCallbacks callbacks) { updateViewTreeDisplayList(view); ... } private void updateViewTreeDisplayList(View view) { view.mPrivateFlags |= View.PFLAG_DRAWN; view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; view.updateDisplayListIfDirty(); view.mRecreateDisplayList = false; }
由於targetView的祖先view都加上了PFLAG_DIRTY,去掉了PFLAG_DRAWING_CACHE_VALID,也沒有加入View.PFLAG_INVALIDATED。所以view.mRecreateDisplayList為false。
public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) { // Don't need to recreate the display list, just need to tell our // children to restore/recreate theirs if (renderNode.isValid() && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); return renderNode; // no work needed } ... }
此時調用是處在target的祖先view的updateDisplayListIfDirty中。由於targetView的祖先都沒有PFLAG_DRAWING_CACHE_VALID,所以肯定是能進入的。
接着顯然於targetView的祖先view都滿足renderNode.isValid() && !mRecreateDisplayList,所以接着調用dispatchGetDisplayList后直接返回。
下邊看dispatchGetDisplayList:
protected void dispatchGetDisplayList() { final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { final View child = children[i]; if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) { recreateChildDisplayList(child); } } ... } private void recreateChildDisplayList(View child) { child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0; child.mPrivateFlags &= ~PFLAG_INVALIDATED; child.updateDisplayListIfDirty(); child.mRecreateDisplayList = false; }
如果此時child還是target的祖先view,那么child.mRecreateDisplayList還是false。
如果此時child就是target,那么他的mRecreateDisplayList也是false,因為target在setTranslationX過程中並沒有調用invalidate。
那么最終調用target的updateDisplayListIfDirty,
public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; if (!canHaveDisplayList()) { // can't populate RenderNode, don't try return renderNode; } if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) { // Don't need to recreate the display list, just need to tell our // children to restore/recreate theirs if (renderNode.isValid() && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); return renderNode; // no work needed } ... } else { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } return renderNode; }
由於target的PFLAG_DRAWING_CACHE_VALID並沒被去掉,且renderNode.isValid為true,mRecreateDisplayList也為false,所以會跳到下邊的else邏輯中,然后就直接返回。
可以看到整個過程是非常高效的,雖然進行了從上到下的遍歷,但並沒有進行任何的重繪或重建displaylist。
那既然什么都沒做,為什么要進行從上到下的遍歷一次?
調用會導致invalidate的方法流程
invalidate流程
public void invalidate() { invalidate(true); } public void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); } void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { ... if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque)) { if (fullInvalidate) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; } mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); p.invalidateChild(this, damage); } ... } }
由代碼可知:
mPrivateFlags |= PFLAG_DIRTY;
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWN;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
重繪之前多次調用的話,除了第一次有用,之后就不會做任何事。
接着調用parentView的invalidateChild
public final void invalidateChild(View child, final Rect dirty) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null && attachInfo.mHardwareAccelerated) { // HW accelerated fast path onDescendantInvalidated(child, child); return; } ... }
由於是硬件加速所以會直接進入onDescendantInvalidated邏輯
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { /* * HW-only, Rect-ignoring damage codepath * * We don't deal with rectangles here, since RenderThread native code computes damage for * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area) */ // if set, combine the animation flag into the parent mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION); if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) { // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential // optimization in provides in a DisplayList world. mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; // simplified invalidateChildInParent behavior: clear cache validity to be safe... mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // ... and mark inval if in software layer that needs to repaint (hw handled in native) if (mLayerType == LAYER_TYPE_SOFTWARE) { // Layered parents should be invalidated. Escalate to a full invalidate (and note that // we do this after consuming any relevant flags from the originating descendant) mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY; target = this; } if (mParent != null) { mParent.onDescendantInvalidated(this, target); } }
之后的流程 和 不會導致invalidate的流程一樣,target的祖先view的mPrivateFlags中都加上了PFLAG_DIRTY,去掉了PFLAG_DRAWING_CACHE_VALID。
直接看繪制流程
繪制流程
開始時和不會導致invalidate的流程一樣,直到調用到recreateChildDisplayList里的child為target時就開始重建流程:
private void recreateChildDisplayList(View child) { child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0; child.mPrivateFlags &= ~PFLAG_INVALIDATED; child.updateDisplayListIfDirty(); child.mRecreateDisplayList = false; }
由於target的mPrivateFlags:
-
mPrivateFlags |= PFLAG_DIRTY;
-
mPrivateFlags |= PFLAG_INVALIDATED;
-
mPrivateFlags &= ~PFLAG_DRAWN;
-
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
所以mRecreateDisplayList 為true。接着就會調用target的updateDisplayListIfDirty方法,會走下邊的流程,創建一個DisplayListCanvas,調用draw(canvas), 接着調用onDraw,來重建該target的displaylist。
public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; if (!canHaveDisplayList()) { // can't populate RenderNode, don't try return renderNode; } if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) { //由於mRecreateDisplayList 為true,所以直接跳過此步驟,進行重建displaylist // Don't need to recreate the display list, just need to tell our // children to restore/recreate theirs if (renderNode.isValid() && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); return renderNode; // no work needed } // If we got here, we're recreating it. Mark it as such to ensure that // we copy in child display lists into ours in drawChild() mRecreateDisplayList = true; int width = mRight - mLeft; int height = mBottom - mTop; int layerType = getLayerType(); final DisplayListCanvas canvas = renderNode.start(width, height); canvas.setHighContrastText(mAttachInfo.mHighContrastText); try { if (layerType == LAYER_TYPE_SOFTWARE) { buildDrawingCache(true); Bitmap cache = getDrawingCache(true); if (cache != null) { canvas.drawBitmap(cache, 0, 0, mLayerPaint); } } else { //由於是硬件加速非soft layer所以進入此處 computeScroll(); canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); drawAutofilledHighlight(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } if (debugDraw()) { debugDrawFocus(canvas); } } else { draw(canvas); } } } finally { renderNode.end(canvas); setDisplayListProperties(renderNode); } } else { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } return renderNode; }
可以看到整個過程是非常高效的,雖然進行了從上到下的遍歷,但只有target進行了重繪或重建displaylist,其他view不會執行任何繪制操作。
如果invalidate的target是ViewGroup的重繪流程
上邊的target是一個view,如果invalidate的target是ViewGroup的話,
接着上邊的updateDisplayListIfDirty,ViewGroup最終會調用dispatchDraw,會把繪制分發給子view
protected void dispatchDraw(Canvas canvas) { boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode); final int childrenCount = mChildrenCount; final View[] children = mChildren; int flags = mGroupFlags; ... int clipSaveCount = 0; final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; if (clipToPadding) { clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, mScrollX + mRight - mLeft - mPaddingRight, mScrollY + mBottom - mTop - mPaddingBottom); } // We will draw our child's animation, let's reset the flag mPrivateFlags &= ~PFLAG_DRAW_ANIMATION; mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; boolean more = false; final long drawingTime = getDrawingTime(); if (usingRenderNodeProperties) canvas.insertReorderBarrier(); final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size(); int transientIndex = transientCount != 0 ? 0 : -1; // Only use the preordered list if not HW accelerated, since the HW pipeline will do the // draw reordering internally final ArrayList<View> preorderedList = usingRenderNodeProperties ? null : buildOrderedChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); for (int i = 0; i < childrenCount; i++) { ... final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); } } ... }
protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this, drawingTime); }
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList. * * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't * HW accelerated, it can't handle drawing RenderNodes. */ boolean drawingWithRenderNode = mAttachInfo != null && mAttachInfo.mHardwareAccelerated && hardwareAcceleratedCanvas; boolean more = false; final boolean childHasIdentityMatrix = hasIdentityMatrix(); final int parentFlags = parent.mGroupFlags; if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) { parent.getChildTransformation().clear(); parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION; } ... if (hardwareAcceleratedCanvas) { // Clear INVALIDATED flag to allow invalidation to occur during rendering, but // retain the flag's value temporarily in the mRecreateDisplayList flag mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0; mPrivateFlags &= ~PFLAG_INVALIDATED; } RenderNode renderNode = null; Bitmap cache = null; //soft繪制 int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) { if (layerType != LAYER_TYPE_NONE) { // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true); } cache = getDrawingCache(true); } //硬件繪制 if (drawingWithRenderNode) { // Delay getting the display list until animation-driven alpha values are // set up and possibly passed on to the view renderNode = updateDisplayListIfDirty(); if (!renderNode.isValid()) { // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not // try to use it again. renderNode = null; drawingWithRenderNode = false; } } int sx = 0; int sy = 0; if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; } final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode; final boolean offsetForScroll = cache == null && !drawingWithRenderNode; int restoreTo = -1; if (!drawingWithRenderNode || transformToApply != null) { restoreTo = canvas.save(); } if (offsetForScroll) { canvas.translate(mLeft - sx, mTop - sy); } else { if (!drawingWithRenderNode) { canvas.translate(mLeft, mTop); } if (scalingRequired) { if (drawingWithRenderNode) { // TODO: Might not need this if we put everything inside the DL restoreTo = canvas.save(); } // mAttachInfo cannot be null, otherwise scalingRequired == false final float scale = 1.0f / mAttachInfo.mApplicationScale; canvas.scale(scale, scale); } } float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha()); ...alpah... ... if (!drawingWithDrawingCache) { if (drawingWithRenderNode) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ((DisplayListCanvas) canvas).drawRenderNode(renderNode); } else { // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); } } } else if (cache != null) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ... } if (restoreTo >= 0) { canvas.restoreToCount(restoreTo); } mRecreateDisplayList = false; return more; }
最后把子view的renderNode繪制到父view的DisplayListCanvas上。
如果在子view中調用了invalidateParent的方法,那么就只會讓父view進行重繪操作(當然前提是子view沒有invalidate)。
硬件加速下LAYER_TYPE_SOFTWARE
調用不會導致invalidate的方法流程
以setTranslationX為例
public void setTranslationX(float translationX) { if (translationX != getTranslationX()) { invalidateViewProperty(true, false); mRenderNode.setTranslationX(translationX); invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } }
可以看到setTranslationX中會把translationX設置到mRenderNode中。
void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) { if (!isHardwareAccelerated() || !mRenderNode.isValid() || (mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) { if (invalidateParent) { invalidateParentCaches(); } if (forceRedraw) { mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation } invalidate(false); } else { damageInParent(); } }
因為當前的window還是硬件加速,mRenderNode.isValid()也為true,且沒有處於Animation動畫中,所以還是會進入damageInParent()邏輯。
protected void damageInParent() { if (mParent != null && mAttachInfo != null) { mParent.onDescendantInvalidated(this, this); } } public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { /* * HW-only, Rect-ignoring damage codepath * * We don't deal with rectangles here, since RenderThread native code computes damage for * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area) */ // if set, combine the animation flag into the parent mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION); if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) { // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential // optimization in provides in a DisplayList world. mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; // simplified invalidateChildInParent behavior: clear cache validity to be safe... mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // ... and mark inval if in software layer that needs to repaint (hw handled in native) if (mLayerType == LAYER_TYPE_SOFTWARE) { // Layered parents should be invalidated. Escalate to a full invalidate (and note that // we do this after consuming any relevant flags from the originating descendant) mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY; target = this; } if (mParent != null) { mParent.onDescendantInvalidated(this, target); } }
由上可知祖先view的mPrivateFlags:
-
mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
-
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
同時如果祖先view中如果也有LAYER_TYPE_SOFTWARE,
-
那么就也會把此view的mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY,
-
同時把target變為該view。
之后的邏輯完全和調用不會導致invalidate的方法流程一樣。
最終調用到了ViewRootImpl中的onDescendantInvalidated,並安排了一次Traversals
繪制流程
和 硬件加速下調用不會導致invalidate的方法的 繪制流程 完全一樣。
雖然進行了從上到下的遍歷,但並沒有進行任何的重繪或重建displaylist。
調用會導致invalidate的方法流程
invalidate流程
和 硬件加速下調用會導致invalidate的方法流程 完全一樣。
繪制流程
開始時和不會導致invalidate的流程一樣,直到調用到recreateChildDisplayList里的child為target時就開始重建流程:
private void recreateChildDisplayList(View child) { child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0; child.mPrivateFlags &= ~PFLAG_INVALIDATED; child.updateDisplayListIfDirty(); child.mRecreateDisplayList = false; }
由於target的mPrivateFlags:
-
mPrivateFlags |= PFLAG_DIRTY;
-
mPrivateFlags |= PFLAG_INVALIDATED;
-
mPrivateFlags &= ~PFLAG_DRAWN;
-
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
所以mRecreateDisplayList 為true。
public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; if (!canHaveDisplayList()) { // can't populate RenderNode, don't try return renderNode; } if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) { //由於mRecreateDisplayList 為true,所以直接跳過此步驟,進行重建displaylist // Don't need to recreate the display list, just need to tell our // children to restore/recreate theirs if (renderNode.isValid() && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); return renderNode; // no work needed } // If we got here, we're recreating it. Mark it as such to ensure that // we copy in child display lists into ours in drawChild() mRecreateDisplayList = true; int width = mRight - mLeft; int height = mBottom - mTop; int layerType = getLayerType(); final DisplayListCanvas canvas = renderNode.start(width, height); canvas.setHighContrastText(mAttachInfo.mHighContrastText); try { if (layerType == LAYER_TYPE_SOFTWARE) { //由於是LAYER_TYPE_SOFTWARE所以進入此處 buildDrawingCache(true); Bitmap cache = getDrawingCache(true); if (cache != null) { canvas.drawBitmap(cache, 0, 0, mLayerPaint); } } else { ... } } finally { renderNode.end(canvas); setDisplayListProperties(renderNode); } } else { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } return renderNode; }
然后調用buildDrawingCache(true);來繪制軟件drawing cache,即繪制的內容都保存為bitmap上。
最后canvas.drawBitmap(cache, 0, 0, mLayerPaint);把bitmap cache繪制到了DisplayListCanvas 上。
可以看到整個過程是非常高效的,雖然進行了從上到下的遍歷,但只對target進行了重繪或重建drawing cache(bitmap)。
如果invalidate的target是ViewGroup的重繪流程
接着上邊的updateDisplayListIfDirty,里邊最后調用到buildDrawingCache(true);
public void buildDrawingCache(boolean autoScale) { if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null)) { try { buildDrawingCacheImpl(autoScale); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } } private void buildDrawingCacheImpl(boolean autoScale) { mCachingFailed = false; int width = mRight - mLeft; int height = mBottom - mTop; ... boolean clear = true; Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache; if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { Bitmap.Config quality; if (!opaque) { // Never pick ARGB_4444 because it looks awful // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) { case DRAWING_CACHE_QUALITY_AUTO: case DRAWING_CACHE_QUALITY_LOW: case DRAWING_CACHE_QUALITY_HIGH: default: quality = Bitmap.Config.ARGB_8888; break; } } else { // Optimization for translucent windows // If the window is translucent, use a 32 bits bitmap to benefit from memcpy() quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; } // Try to cleanup memory if (bitmap != null) bitmap.recycle(); try { bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(), width, height, quality); bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); if (autoScale) { mDrawingCache = bitmap; } else { mUnscaledDrawingCache = bitmap; } if (opaque && use32BitCache) bitmap.setHasAlpha(false); } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the // view hierarchy if (autoScale) { mDrawingCache = null; } else { mUnscaledDrawingCache = null; } mCachingFailed = true; return; } clear = drawingCacheBackgroundColor != 0; } Canvas canvas; if (attachInfo != null) { canvas = attachInfo.mCanvas; if (canvas == null) { canvas = new Canvas(); } canvas.setBitmap(bitmap); // Temporarily clobber the cached Canvas in case one of our children // is also using a drawing cache. Without this, the children would // steal the canvas by attaching their own bitmap to it and bad, bad // thing would happen (invisible views, corrupted drawings, etc.) attachInfo.mCanvas = null; } else { // This case should hopefully never or seldom happen canvas = new Canvas(bitmap); } if (clear) { bitmap.eraseColor(drawingCacheBackgroundColor); } computeScroll(); final int restoreCount = canvas.save(); if (autoScale && scalingRequired) { final float scale = attachInfo.mApplicationScale; canvas.scale(scale, scale); } canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= PFLAG_DRAWN; if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated || mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID; } // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); drawAutofilledHighlight(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } } else { draw(canvas); } canvas.restoreToCount(restoreCount); canvas.setBitmap(null); if (attachInfo != null) { // Restore the cached Canvas for our siblings attachInfo.mCanvas = canvas; } }
最后調用dispatchDraw(canvas);把繪制分發到子view。
protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this, drawingTime); } boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList. * * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't * HW accelerated, it can't handle drawing RenderNodes. */ boolean drawingWithRenderNode = mAttachInfo != null && mAttachInfo.mHardwareAccelerated && hardwareAcceleratedCanvas; ... if (hardwareAcceleratedCanvas) { // Clear INVALIDATED flag to allow invalidation to occur during rendering, but // retain the flag's value temporarily in the mRecreateDisplayList flag mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0; mPrivateFlags &= ~PFLAG_INVALIDATED; } RenderNode renderNode = null; Bitmap cache = null; int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local //soft繪制,如果子view並不是LAYER_TYPE_SOFTWARE,但drawingWithRenderNode為false,所以也會進入軟件繪制流程。 if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) { //當然也有可能子view的layerType為LAYER_TYPE_NONE,那么就無法創建drawing cache, //反而如果子view的layerType為LAYER_TYPE_HARDWARE,那么也會調用buildDrawingCache創建。 if (layerType != LAYER_TYPE_NONE) { // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true); } cache = getDrawingCache(true); } //硬件繪制 if (drawingWithRenderNode) { // Delay getting the display list until animation-driven alpha values are // set up and possibly passed on to the view renderNode = updateDisplayListIfDirty(); if (!renderNode.isValid()) { // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not // try to use it again. renderNode = null; drawingWithRenderNode = false; } } int sx = 0; int sy = 0; if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; } final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode; final boolean offsetForScroll = cache == null && !drawingWithRenderNode; ... if (!drawingWithDrawingCache) { if (drawingWithRenderNode) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ((DisplayListCanvas) canvas).drawRenderNode(renderNode); } else { //如果子view為LAYER_TYPE_NONE那么就不會創建drawing cache,那么就需要執行繪制代碼 // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); } } } else if (cache != null) { //soft layer進入此邏輯 mPrivateFlags &= ~PFLAG_DIRTY_MASK; if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) { // no layer paint, use temporary paint to draw bitmap Paint cachePaint = parent.mCachePaint; if (cachePaint == null) { cachePaint = new Paint(); cachePaint.setDither(false); parent.mCachePaint = cachePaint; } cachePaint.setAlpha((int) (alpha * 255)); canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); } else { // use layer paint to draw the bitmap, merging the two alphas, but also restore int layerPaintAlpha = mLayerPaint.getAlpha(); if (alpha < 1) { mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha)); } canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint); if (alpha < 1) { mLayerPaint.setAlpha(layerPaintAlpha); } } } ... mRecreateDisplayList = false; return more; }
如果子view並不是LAYER_TYPE_SOFTWARE,但根據判斷drawingWithRenderNode為false,所以也會進入軟件繪制流程,當然還需要根據子view的layerType來決定是否調用buildDrawingCache。
所以說如果父view是soft layer,那么他的子view的layerType為hardware也會用軟件繪制,如果子view是none就直接調用draw代碼(不管是否drawing cache有效)。
public void buildDrawingCache(boolean autoScale) { if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null)) { try { buildDrawingCacheImpl(autoScale); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } }
可以看到如果target的子view的cache有效的話就直接返回,不重建。然后在draw(Canvas canvas, ViewGroup parent, long drawingTime)最后把子view的cache繪制到target的canvas上。
父view的父view是soft layer,子view調用了invalidate/setTranslationX
當前view樹的部分層級結構。
soft layer
--hardware layer
----none
-
none的 view調用了invalidate方法,
mPrivateFlags |= PFLAG_DIRTY;
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWN;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
-
none的 view調用了setTranslationX方法
mPrivateFlags 不變
-
hardware layer的 view
mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
-
soft layer的 view
mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
target = this;
由於父view是soft layer,所以子view的繪制都是用soft繪制,有沒有cache取決於該子view是否設置了layer(不管hardware layer,還是software layer)。
之后在繪制hardware layer的view時,會去調用buildDrawingCache(boolean autoScale),由於沒有PFLAG_DRAWING_CACHE_VALID,所以就直接進入buildDrawingCacheImpl,
由此可以得出結論,子view調用了invalidate,那么soft layer、hardware layer、none都需要重建drawing cache。
子view調用了setTranslationX,那么soft layer、hardware layer都需要重建drawing cache,none的 view復用cache。