Invalidate、postInvalidate、requestLayout

requestLayout 当前我们对View位置、大小进行操作后会调用requestLayout通知窗口。

public void requestLayout() { if (mMeasureCache != null) mMeasureCache.clear(); if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { //viewRoot 已经处于Layout过程则添加如等待队列 if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; } //设置标志位,需要布局及重绘 mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); //责任链模式,上报requestLayout } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } }

从代码中可以看出requestLayout通过责任链模式上报到可以处理的实现中去。这样我门大概可以整理出大概执行流程
  1. View.requestLayout
  2. ViewRootImpl.requestLayout会判断是否已经在进行layout过程,已在进行则忽视
  3. ViewRootImpl.scheduleTraversals
  4. ViewRootImpl.doTraversal scheduleTraversals()通过Choreographer在下一帧调用方式回调
  5. ViewRootImpl.performTraversals
ViewRootImpl.performTraversals的分析就不再累述了,大概就是requestLayout使mLayoutRequested标志位置为true,performTraversals将走完整套流程。稍微需要说在requestLayout中对mHandlingLayoutInLayoutRequest标志位的判断原因。
boolean requestLayoutDuringLayout(final View view) { if (view.mParent == null || view.mAttachInfo == null) { return true; } if (!mLayoutRequesters.contains(view)) { mLayoutRequesters.add(view); } if (!mHandlingLayoutInLayoutRequest) { return true; } else { return false; } }private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { .... Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); try { host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); mInLayout = false; int numViewsRequestingLayout = mLayoutRequesters.size(); if (numViewsRequestingLayout > 0) {//仍有requestLayout未处理 ArrayList validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, false); if (validLayoutRequesters != null) { // 阻止二次传递,将新的requestLayout放到下一帧处理 mHandlingLayoutInLayoutRequest = true; // 刷新 validLayoutRequesters 中的view,view.requestLayout()重新measure及layout .....mInLayout = true; host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); mHandlingLayoutInLayoutRequest = false; // 仍未处理的requestLayout在下一帧处理 validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true); if (validLayoutRequesters != null) { final ArrayList finalRequesters = validLayoutRequesters; //通过getRunQueue.post,在下一帧继续进行requestLayout ..... } }} } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } mInLayout = false; }

可以看到如果ViewRootImpl已经在处理layout过程了,这时View.requestLayout将会调用ViewRootImpl.requestLayoutDuringLayout让ViewRootImpl在performLayout中处理这些View的请求。
View.requestLayout设置的标志位PFLAG_FORCE_LAYOUT、PFLAG_INVALIDATED将在measure、layout、draw流程中判断时候执行对应流程。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... //判断是否需要强制布局,requestLayout时mPrivateFlags添加标志位 PFLAG_FORCE_LAYOUT final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; .... if (forceLayout || needsLayout) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; ... if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = https://www.it610.com/article/mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value>> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }.....mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; //请求layout流程 } .... }public void layout(int l, int t, int r, int b) { if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }..... //setFrame可能导致重绘 boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); //measure完成后mPrivateFlags添加标志位 PFLAG_LAYOUT_REQUIRED if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); ..... //layout完成,取消layout标志位 mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; ..... } //measure、layout完成,取消requestLayout的forceLayout标志位 mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) { mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; notifyEnterOrExitForAutoFillIfNeeded(true); } }protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { ....// Invalidate our old position invalidate(sizeChanged); mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); ....if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) { // If we are visible, force the DRAWN bit to on so that // this invalidate will go through (at least to our parent). // This is because someone may have invalidated this view // before this call to setFrame came in, thereby clearing // the DRAWN bit. mPrivateFlags |= PFLAG_DRAWN; invalidate(sizeChanged); // parent display list may need to be recreated based on a change in the bounds // of any child invalidateParentCaches(); }..... } return changed; }

从源码可以看出,requestLayout必然导致measure、layout过程(measure完成添加标志位PFLAG_LAYOUT_REQUIRED),是否发生重绘由setFrame中invalidate(sizeChanged)决定。
postInvalidate、Invalidate postInvalidate通过ViewRootImpl.dispatchInvalidateDelayed发送MSG_INVALIDATE的Message,最后起调View.invalidate。而invalidate调用的invalidateInternal()。
因为postInvalidate是通过Handler封装的,所以可以在异步线程中调用。
invalidateInternal
//--------View.invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, true, 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; //设置标志位PFLAG_DIRTY置1if (invalidateCache) {//invalidate调用时为true mPrivateFlags |= PFLAG_INVALIDATED; //标志位PFLAG_INVALIDATED置1 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; //标志位PFLAG_DRAWING_CACHE_VALID置0 }// 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); //转递给父布局 }....... } }

invalidateInternal方法主要标志PFLAG_DIRTY标志位后,将重绘区域传给parent处理,ViewGroup#invalidateChild处理接下来逻辑。
//-----------ViewGroup#invalidateChild public final void invalidateChild(View child, final Rect dirty) { final AttachInfo attachInfo = mAttachInfo; ...... ViewParent parent = this; if (attachInfo != null) { ...... // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY; if (child.mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } //保存child左上角坐标 final int[] location = attachInfo.mInvalidateChildLocation; location[CHILD_LEFT_INDEX] = child.mLeft; location[CHILD_TOP_INDEX] = child.mTop; ........do { View view = null; if (parent instanceof View) { view = (View) parent; } .......// If the parent is dirty opaque or not dirty, mark it dirty with the opaque // flag coming from the child that initiated the invalidate if (view != null) { if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && view.getSolidColor() == 0) { opaqueFlag = PFLAG_DIRTY; } if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) { //对当前View的标记位进行设置 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag; } } //责任链模式上报,最终ViewRootImpl停止 parent = parent.invalidateChildInParent(location, dirty); if (view != null) { // Account for transform on current parent Matrix m = view.getMatrix(); if (!m.isIdentity()) { RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); //更新dirty信息区域信息 dirty.set((int) Math.floor(boundingRect.left), (int) Math.floor(boundingRect.top), (int) Math.ceil(boundingRect.right), (int) Math.ceil(boundingRect.bottom)); } } } while (parent != null); } }

这里主要是先标志子view位置,向上回溯父容器,然后求得父容器和子View需要重绘的区域的并集(dirty),最后在ViewRootImpl#invalidateChildInParent中停止。
//----------ViewGroup#invalidateChildInParent public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) { // either DRAWN, or DRAWING_CACHE_VALID if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE) { //将dirty中的坐标转化为父容器中的坐标,考虑mScrollX和mScrollY的影响 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY); if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) { //求并集,结果是把子视图的dirty区域转化为父容器的dirty区域 dirty.union(0, 0, mRight - mLeft, mBottom - mTop); }final int left = mLeft; final int top = mTop; if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) { dirty.setEmpty(); } } //记录当前视图的mLeft和mTop值,在下一次循环中会把当前值再向父容器的坐标转化 location[CHILD_LEFT_INDEX] = left; location[CHILD_TOP_INDEX] = top; } else {if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { dirty.set(0, 0, mRight - mLeft, mBottom - mTop); } else { // in case the dirty rect extends outside the bounds of this container dirty.union(0, 0, mRight - mLeft, mBottom - mTop); } location[CHILD_LEFT_INDEX] = mLeft; location[CHILD_TOP_INDEX] = mTop; mPrivateFlags &= ~PFLAG_DRAWN; } //设置PFLAG_DRAWING_CACHE_VALID标志位置0 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; if (mLayerType != LAYER_TYPE_NONE) {//viewGroup需要重绘PFLAG_INVALIDATED标志位置1 mPrivateFlags |= PFLAG_INVALIDATED; }return mParent; }return null; } //---------ViewRootImpl#invalidateChildInParent public ViewParent invalidateChildInParent(int[] location, Rect dirty) { checkThread(); if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty); 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; }

可以看到ViewGroup#invalidateChildInParent主要的作用为通过offset方法,把当前dirty区域的坐标转化为父容器中的坐标,接着调用union方法,把子dirty区域与父容器的区域求并集。即dirty区域转化为父容器区域并返回当前视图的父容器,以便进行下一次循环。
最后在ViewRootImpl#invalidateChildInParent所做工作与ViewGroup差不多,调整dirty区域并保存在mDirty中,最后调用了scheduleTraversals方法,触发View的工作流程。因为没有添加measure和layout的标记位,因此对应流程不会执行,而是直接从draw流程开始。
private void draw(boolean fullRedrawNeeded) { ....... if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { ...... mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); } else { ........ if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { return; } } } ......... }

在draw流程中,因为!dirty.isEmpty()通过mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
最终会调用ThreadedRenderer#updateViewTreeDisplayList(View view),而此时的view为RootView。
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#updateDisplayListIfDirty()下发找到需要重绘的View。
因为需要invalidate的View的标志位PFLAG_DRAWING_CACHE_VALID 为0,PFLAG_INVALIDATED为1,而该view的父族ViewGroup的PFLAG_DRAWING_CACHE_VALID为0,PFLAG_INVALIDATED为0。所以只有需要重绘的view的mRecreateDisplayList==true,其父族mRecreateDisplayList==false。
最终下发找到需要重绘的view/viewgroup
public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; ..... //与需要invalidate的view同族的ViewGreoup的标志PFLAG_DRAWING_CACHE_VALID都为0 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(); //下发需要重绘的viewreturn 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 { 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); //-------ViewGroup一般为PFLAG_SKIP_DRAW,直接下发 drawAutofilledHighlight(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } if (debugDraw()) { debugDrawFocus(canvas); } } else { draw(canvas); //-------view起调draw流程 } } } finally { renderNode.end(canvas); setDisplayListProperties(renderNode); } } else { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } return renderNode; }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); //重置标志位并下发 } } if (mOverlay != null) { View overlayView = mOverlay.getOverlayView(); recreateChildDisplayList(overlayView); } if (mDisappearingChildren != null) { final ArrayList disappearingChildren = mDisappearingChildren; final int disappearingCount = disappearingChildren.size(); for (int i = 0; i < disappearingCount; ++i) { final View child = disappearingChildren.get(i); recreateChildDisplayList(child); } } }private void recreateChildDisplayList(View child) { child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0; //需要重绘的view为true child.mPrivateFlags &= ~PFLAG_INVALIDATED; child.updateDisplayListIfDirty(); child.mRecreateDisplayList = false; }

【Invalidate、postInvalidate、requestLayout】

    推荐阅读