zoukankan      html  css  js  c++  java
  • Android8.0硬件加速的重绘流程

    几个PFLAG的作用

    • PFLAG_DRAW_ANIMATION:表示当前view在做Animation动画。
    • PFLAG_HAS_BOUNDS:表示此view是否layout过。
    • PFLAG_DRAWN :当invalidate时会把此标记删除,当调用draw方法(包括软件硬件两个都设置了),
    • PFLAG_DRAWING_CACHE_VALID: 表示当前cache是否有效,

    如果父view是soft layer,那么在把绘制分发到此view时,会用这个flag来判断是否进行重建drawing cache,不管该view的layerType是hard/soft。

    • PFLAG_INVALIDATED :指示当前view明确是invalidated,而不只是因为其子view invalidate而dirty。该标志用于确定何时需要重新创建view的 display list(而不是仅仅返回对其现有 display list的引用)。

    好像软件绘制没有判断PFLAG_INVALIDATED的,但在硬件加速中view.mRecreateDisplayList是根据此标记来设置。

    • PFLAG_DIRTY:View flag indicating whether this view was invalidated (fully or partially.)

    当前view调用了invalidate,或当前view的子view调用了invalidate,都会是的当前view加上此flag。

    软件绘制

    是基于Android8.0的源码来分析的。

    前提是window是关闭了硬件加速。

    实际阅读源码并实验,得出通常情况下的软件绘制刷新逻辑:

    1. 默认情况下,View的clipChildren属性为true,即每个View绘制区域不能超出其父View的范围。如果设置一个页面根布局的clipChildren属性为false,则子View可以超出父View的绘制区域。

    2. 当一个View触发invalidate,且没有播放动画、没有触发layout的情况下:

    1. 对于全不透明的View,其自身会设置标志位PFLAG_DIRTY,其父View会设置标志位PFLAG_DIRTY_OPAQUE。在draw(canvas)方法中,只有这个View自身重绘。
    2. 对于可能有透明区域的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。

     

     

     

     

     

  • 相关阅读:
    Python3.5 学习三
    心灵鸡汤20180727
    Python3.5 学习二
    spring心得4--setter注入集合(set、list、map、properties等多种集合,配有案例解析)@基本装(引用)
    drop user和drop user cascade的区别(转)
    数据库的导入 导出
    OracleDBConsole服务无法启动原因
    create XML
    C#里面Console.Write与Console.WriteLine有什么区别????
    将字符串 按照规定编码方式编码
  • 原文地址:https://www.cnblogs.com/muouren/p/11750156.html
Copyright © 2011-2022 走看看