本文梳理了最简单的情况,在子View(非ViewGroup)调用invalidate的流程,且忽略绘图缓存,即mLayerType == LAYER_TYPE_NONE
。
如有错误请指正。
invalidate软件绘制流程
从代码可知:
- View如果不可见&&无动画,ViewGroup不可见 && 无动画 && 无过渡动画不会执行invalidate
- 软件绘制会触发与dirty区域相交的所有View(硬件加速优化点)
- 根据第一点,ViewGroup#invalidate时会触发其包括自身和所有子View重绘
- 同一个draw时序内连续调用同一View的invalidate时,会被Flag阻挡,不再向下走
- 同一个draw时序内不同View调用invalidate时只会调为一个,不会重复执行
invalidate硬件加速流程
- View如果不可见&&无动画,ViewGroup不可见 && 无动画 && 无过渡动画不会执行invalidate
- 硬件绘制只会触发标记了PFLAG_INVALIDATED的View的draw()或dispatchDraw()
- View#invalidate时(非ViewGroup),因为被dispatchGetDisplayList接管了,不会调用dispatchDraw
- 同一个draw时序内连续调用同一View的invalidate时,会被Flag阻挡,不再向下走
- 同一个draw时序内不同View调用invalidate时只会调为一个,不会重复执行
requestLayout流程
- 清除measureCache,标记PFLAG_FORCE_LAYOUT,递归向上调用,整条路径都被标记,整条路径都会重薪measure、layout
- 可能会重新触发draw(),layout时大小变化后会触发invalidate
- 同一个layout时序内连续调用同一View的requestLayout时,会被isLayoutRequested阻挡,不再向上走
- 同一个layout时序内不同View调用requestLayout时只会调为一个,不会重复执行
相关代码如下:
View#invalidate
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {
if (skipInvalidate()) {
// View不可见 && 无动画,ViewGroup不可见 && 无动画 && 无过渡动画
return;
}
/*
PFLAG_DRAWN:表示绘制好了,fullInvalidate后表示没绘制好,已经要重绘了,在child.draw()时置为1
PFLAG_HAS_BOUNDS: 已经layout完成,onLayout#setFrame中赋值
PFLAG_DRAWING_CACHE_VALID:buildDrawingCache()中赋值,表示此View对象的cache是否也需要被invalidate
PFLAG_INVALIDATED:是否需要重建View的display list
*/
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); // 父控件重绘dirty区域
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
}
}
ViewGroup#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;
}
do {
parent = parent.invalidateChildInParent(location, dirty); // 最终到ViewRootImpl
} while (parent != null);
}
ViewRootImpl
// 硬件加速
public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
mIsAnimating = true;
}
invalidate();
}
// 软件绘制
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
// 不用重绘
return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
// Add the new dirty rect to the current one
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); // 并集
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); // 是否相交
if (!intersected) {
localDirty.setEmpty();
}
// 正在绘制中 或者 不相交&&不在动画中 即重绘
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals(); // 等待VSync信息触发performTraversals
}
}
private boolean draw(boolean fullRedrawNeeded) {
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
// 硬件绘制
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback); // updateViewTreeDisplayList,定点更新标记的View
} else {
// 软件绘制
drawSoftware(); // --> mView.draw(canvas); 执行绘制流程
}
}
}
ThreadedRenderer
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks,
FrameDrawingCallback frameDrawingCallback) {
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;
}
View
// 更新dirtyDisplayList
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) {
// 当前View没有被标记为PFLAG_INVALIDATED,直接dispatch
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList(); // 代替dispatchDraw
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);
try {
if (layerType == LAYER_TYPE_SOFTWARE) {
// 开启了位图缓存
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
}
} else {
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) {
// 不需要绘制,直接dispatch下去
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
// 重绘
draw(canvas);
}
}
} finally {
renderNode.end(canvas);
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
ViewGroup
ViewRootImpl分发draw,到view#draw走到viewGroup的dispatchDraw,继续分发。
protected void dispatchDraw(Canvas canvas) {
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) {
// Sets the flag as early as possible to allow draw() implementations
// to call invalidate() successfully when doing animations
mPrivateFlags |= PFLAG_DRAWN; // draw时序结束,表示已经一次invalidate完成
if (!concatMatrix
&& (parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS | ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN
// 判断当前view的矩形是否与canvas相交,与脏视图相交
&& canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW)
&& (mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;
return more;
}
int layerType = getLayerType();
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); // 会触发view.draw(canvas)
}
cache = getDrawingCache(true);
}
final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;
// 一般情况下false
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((DisplayListCanvas) canvas).drawRenderNode(renderNode); // 直接绘制子View,不调用draw(canvas)
} else {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas); // ViewGroup自己不需要绘制,直接dispatch下去
} else {
draw(canvas); // view绘制自己
}
}
} else if (cache != null) {
// LAYER_TYPE_SOFTWARE走到这里
}
}
public void buildDrawingCache(boolean autoScale) {
// flag标记了,别的view在invalidate时,自己不会走进去了
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null)) {
try {
buildDrawingCacheImpl(autoScale);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
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;
}
参考: