几个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。