Android8.0硬件加速的重绘流程

戏子无情 提交于 2019-12-02 13:50:46

几个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的绘制区域。

 

  1. 当一个View触发invalidate,且没有播放动画、没有触发layout的情况下:
    1. 对于全不透明的View,其自身会设置标志位PFLAG_DIRTY,其父View会设置标志位PFLAG_DIRTY_OPAQUE。在draw(canvas)方法中,只有这个View自身重绘。

 

  1. 对于可能有透明区域的View,其自身和父View都会设置标志位PFLAG_DIRTY。
    1. clipChildren为true时,脏区会被转换成ViewRoot中的Rect,刷新时层层向下判断,当View与脏区有重叠则重绘。如果一个View超出父View范围且与脏区重叠,但其父View不与脏区重叠,这个子View不会重绘。
    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。

 

 

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!