美文网首页
Invalidate、postInvalidate、reques

Invalidate、postInvalidate、reques

作者: KIDNG_LGJ | 来源:发表于2018-09-20 09:56 被阅读0次

    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<View> 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<View> 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_LAYOUTPFLAG_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 = 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置1
    
                if (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();//下发需要重绘的view
    
                    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 {
                        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<View> 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、reques

          本文链接:https://www.haomeiwen.com/subject/zjbpnftx.html