美文网首页
View的绘制过程,显示列表的更新

View的绘制过程,显示列表的更新

作者: 老码w | 来源:发表于2019-12-18 17:24 被阅读0次

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://www.jianshu.com/p/4e00ccfd4d66
    接着文章https://www.jianshu.com/p/2dbe8f5df49e说起,本篇讲View的Draw过程。Draw分为softwareDraw和HardwareRenderer(硬件加速)。
    我们先讲softwareDraw再讲HardwareRenderer。

    从performDraw说起。

    ViewRootImpl的performTraversals中刷新UI的时候,当完成了View的measure和layout之后会对View进行Draw。performTraversals会调用到performDraw开始执行绘制操作,我们先看看performDraw的代码。

    private void performDraw() {
            if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
                return;
            }
    
            final boolean fullRedrawNeeded = mFullRedrawNeeded;
            mFullRedrawNeeded = false;
    
            mIsDrawing = true;
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
            try {
                draw(fullRedrawNeeded);
            } finally {
                mIsDrawing = false;
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
    
            // For whatever reason we didn't create a HardwareRenderer, end any
            // hardware animations that are now dangling
            if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
                final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
                for (int i = 0; i < count; i++) {
                    mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
                }
                mAttachInfo.mPendingAnimatingRenderNodes.clear();
            }
    
            if (mReportNextDraw) {
                mReportNextDraw = false;
    
                // if we're using multi-thread renderer, wait for the window frame draws
                if (mWindowDrawCountDown != null) {
                    try {
                        mWindowDrawCountDown.await();
                    } catch (InterruptedException e) {
                        Log.e(mTag, "Window redraw count down interruped!");
                    }
                    mWindowDrawCountDown = null;
                }
    
                if (mAttachInfo.mHardwareRenderer != null) {
                    mAttachInfo.mHardwareRenderer.fence();
                    mAttachInfo.mHardwareRenderer.setStopped(mStopped);
                }
    
                if (LOCAL_LOGV) {
                    Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
                }
                
                try {
                    mWindowSession.finishDrawing(mWindow);
                } catch (RemoteException e) {
                }
            }
        }
    

    performDraw的主要过程是调用draw,draw主要是判断如果支持硬件加速,则调用硬件加速draw,否则调用software draw。那来看看draw。

    private void draw(boolean fullRedrawNeeded) {
            final Rect dirty = mDirty;
            if (fullRedrawNeeded) {
                mAttachInfo.mIgnoreDirtyState = true;
                dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
            }
            mAttachInfo.mTreeObserver.dispatchOnDraw();
            int xOffset = -mCanvasOffsetX;
            int yOffset = -mCanvasOffsetY + curScrollY;
            final WindowManager.LayoutParams params = mWindowAttributes;
            final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
            if (surfaceInsets != null) {
                xOffset -= surfaceInsets.left;
                yOffset -= surfaceInsets.top;
    
                // Offset dirty rect for surface insets.
                dirty.offset(surfaceInsets.left, surfaceInsets.right);
            }
    
            mAttachInfo.mDrawingTime =
                    mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
    
    //根据!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty等判断是否是否需要绘制
            if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
                //支持硬件绘制则用HardwareRenderer
                if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                    // If accessibility focus moved, always invalidate the root.
                    boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
                    mInvalidateRootRequested = false;
    
                    // Draw with hardware renderer.
                    mIsAnimating = false;
    
                    if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
                        mHardwareYOffset = yOffset;
                        mHardwareXOffset = xOffset;
                        invalidateRoot = true;
                    }
    
                    if (invalidateRoot) {
                        mAttachInfo.mHardwareRenderer.invalidateRoot();
                    }
    
                    dirty.setEmpty();
    
                    // Stage the content drawn size now. It will be transferred to the renderer
                    // shortly before the draw commands get send to the renderer.
                    final boolean updated = updateContentDrawBounds();
    
                    if (mReportNextDraw) {
                        // report next draw overrides setStopped()
                        // This value is re-sync'd to the value of mStopped
                        // in the handling of mReportNextDraw post-draw.
                        mAttachInfo.mHardwareRenderer.setStopped(false);
                    }
    
                    if (updated) {
                        requestDrawWindow();
                    }
    
                    mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
                } else {
                    //否则不支持硬件绘制,但是硬件绘制是需要的,尝试初始化硬件绘制,成功之后调用scheduleTraversals(),重新Traversals
                    if (mAttachInfo.mHardwareRenderer != null &&
                            !mAttachInfo.mHardwareRenderer.isEnabled() &&
                            mAttachInfo.mHardwareRenderer.isRequested()) {
    
                        try {
                            mAttachInfo.mHardwareRenderer.initializeIfNeeded(
                                    mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
                        } catch (OutOfResourcesException e) {
                            handleOutOfResourcesException(e);
                            return;
                        }
    
                        mFullRedrawNeeded = true;
                        scheduleTraversals();
                        return;
                    }
    //否则不支持硬件绘制,同时硬件绘制不被需求,则drawSoftware
                    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                        return;
                    }
                }
            }
    
            if (animating) {
                mFullRedrawNeeded = true;
                scheduleTraversals();
            }
    }
    

    draw的逻辑比较简单,先判断是否支持硬件绘制,如果支持。则调用硬件绘制mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this)。如果不支持硬件绘制,但是硬件绘制是需要的,则尝试初始化硬件绘制,成功则从新Traversal。否则进行软件绘制。
    drawSoftware也比较简单,通过mSurface得到Canvas,mSurface是ViewRootImpl·的Surface类型变量。这里我们先看看drawSoftware。

    // These can be accessed by any thread, must be protected with a lock.
        // Surface can never be reassigned or cleared (use Surface.clear()).
        final Surface mSurface = new Surface();
    
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
                boolean scalingRequired, Rect dirty) {
    
            // Draw with software renderer.
            final Canvas canvas;
            try {
                final int left = dirty.left;
                final int top = dirty.top;
                final int right = dirty.right;
                final int bottom = dirty.bottom;
    
                canvas = mSurface.lockCanvas(dirty);
    
                // The dirty rectangle can be modified by Surface.lockCanvas()
                //noinspection ConstantConditions
                if (left != dirty.left || top != dirty.top || right != dirty.right
                        || bottom != dirty.bottom) {
                    attachInfo.mIgnoreDirtyState = true;
                }
    
                // TODO: Do this in native
                canvas.setDensity(mDensity);
            } catch (Surface.OutOfResourcesException e) {
                handleOutOfResourcesException(e);
                return false;
            } catch (IllegalArgumentException e) {
                Log.e(mTag, "Could not lock surface", e);
                // Don't assume this is due to out of memory, it could be
                // something else, and if it is something else then we could
                // kill stuff (or ourself) for no reason.
                mLayoutRequested = true;    // ask wm for a new surface next time.
                return false;
            }
    
            try {
                if (DEBUG_ORIENTATION || DEBUG_DRAW) {
                    Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                            + canvas.getWidth() + ", h=" + canvas.getHeight());
                    //canvas.drawARGB(255, 255, 0, 0);
                }
    
                // If this bitmap's format includes an alpha channel, we
                // need to clear it before drawing so that the child will
                // properly re-composite its drawing on a transparent
                // background. This automatically respects the clip/dirty region
                // or
                // If we are applying an offset, we need to clear the area
                // where the offset doesn't appear to avoid having garbage
                // left in the blank areas.
                if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                    canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                }
    
                dirty.setEmpty();
                mIsAnimating = false;
                mView.mPrivateFlags |= View.PFLAG_DRAWN;
    
                if (DEBUG_DRAW) {
                    Context cxt = mView.getContext();
                    Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
                            ", metrics=" + cxt.getResources().getDisplayMetrics() +
                            ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
                }
                try {
                    canvas.translate(-xoff, -yoff);
                    if (mTranslator != null) {
                        mTranslator.translateCanvas(canvas);
                    }
                    canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                    attachInfo.mSetIgnoreDirtyState = false;
    
                    mView.draw(canvas);
    
                    drawAccessibilityFocusedDrawableIfNeeded(canvas);
                } finally {
                    if (!attachInfo.mSetIgnoreDirtyState) {
                        // Only clear the flag if it was not set during the mView.draw() call
                        attachInfo.mIgnoreDirtyState = false;
                    }
                }
            } finally {
                try {
                    surface.unlockCanvasAndPost(canvas);
                } catch (IllegalArgumentException e) {
                    Log.e(mTag, "Could not unlock surface", e);
                    mLayoutRequested = true;    // ask wm for a new surface next time.
                    //noinspection ReturnInsideFinallyBlock
                    return false;
                }
    
                if (LOCAL_LOGV) {
                    Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
                }
            }
            return true;
        }
    

    核心就是调用View的draw,这个跟View的measure和layout调用一样。最终会调用OnDraw,进行每个控件自身的Draw,我们自定义控件重绘时也是重写OnDraw函数。但是View的draw又复杂点,它会绘制View的背景和前景。同时如果有垂直或者水平边需要绘制,又会绘制垂直或者水平边。

    public void draw(Canvas canvas) {
            final int privateFlags = mPrivateFlags;
            final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                    (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
            mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
    
            /*
             * Draw traversal performs several drawing steps which must be executed
             * in the appropriate order:
             *
             *      1. Draw the background
             *      2. If necessary, save the canvas' layers to prepare for fading
             *      3. Draw view's content
             *      4. Draw children
             *      5. If necessary, draw the fading edges and restore layers
             *      6. Draw decorations (scrollbars for instance)
             */
    
            // Step 1, draw the background, if needed
            int saveCount;
    
            if (!dirtyOpaque) {
                drawBackground(canvas);
            }
    
            // skip step 2 & 5 if possible (common case)
            final int viewFlags = mViewFlags;
            boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
            boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
            if (!verticalEdges && !horizontalEdges) {
                // Step 3, draw the content
                if (!dirtyOpaque) onDraw(canvas);
    
                // Step 4, draw the children
                dispatchDraw(canvas);
    
                // Overlay is part of the content and draws beneath Foreground
                if (mOverlay != null && !mOverlay.isEmpty()) {
                    mOverlay.getOverlayView().dispatchDraw(canvas);
                }
    
                // Step 6, draw decorations (foreground, scrollbars)
                onDrawForeground(canvas);
    
                // we're done...
                return;
            }
    
            //有垂直或者水平edge需要绘制的情况
            /*
             * Here we do the full fledged routine...
             * (this is an uncommon case where speed matters less,
             * this is why we repeat some of the tests that have been
             * done above)
             */
    
            boolean drawTop = false;
            boolean drawBottom = false;
            boolean drawLeft = false;
            boolean drawRight = false;
    
            float topFadeStrength = 0.0f;
            float bottomFadeStrength = 0.0f;
            float leftFadeStrength = 0.0f;
            float rightFadeStrength = 0.0f;
    
            // Step 2, save the canvas' layers
            int paddingLeft = mPaddingLeft;
    
            final boolean offsetRequired = isPaddingOffsetRequired();
            if (offsetRequired) {
                paddingLeft += getLeftPaddingOffset();
            }
    
            int left = mScrollX + paddingLeft;
            int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
            int top = mScrollY + getFadeTop(offsetRequired);
            int bottom = top + getFadeHeight(offsetRequired);
    
            if (offsetRequired) {
                right += getRightPaddingOffset();
                bottom += getBottomPaddingOffset();
            }
    
            final ScrollabilityCache scrollabilityCache = mScrollCache;
            final float fadeHeight = scrollabilityCache.fadingEdgeLength;
            int length = (int) fadeHeight;
    
            // clip the fade length if top and bottom fades overlap
            // overlapping fades produce odd-looking artifacts
            if (verticalEdges && (top + length > bottom - length)) {
                length = (bottom - top) / 2;
            }
    
            // also clip horizontal fades if necessary
            if (horizontalEdges && (left + length > right - length)) {
                length = (right - left) / 2;
            }
    
            if (verticalEdges) {
                topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
                drawTop = topFadeStrength * fadeHeight > 1.0f;
                bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
                drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
            }
    
            if (horizontalEdges) {
                leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
                drawLeft = leftFadeStrength * fadeHeight > 1.0f;
                rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
                drawRight = rightFadeStrength * fadeHeight > 1.0f;
            }
    
            saveCount = canvas.getSaveCount();
    
            int solidColor = getSolidColor();
            if (solidColor == 0) {
                final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
    
                if (drawTop) {
                    canvas.saveLayer(left, top, right, top + length, null, flags);
                }
    
                if (drawBottom) {
                    canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
                }
    
                if (drawLeft) {
                    canvas.saveLayer(left, top, left + length, bottom, null, flags);
                }
    
                if (drawRight) {
                    canvas.saveLayer(right - length, top, right, bottom, null, flags);
                }
            } else {
                scrollabilityCache.setFadeColor(solidColor);
            }
    
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);
    
            // Step 4, draw the children
            dispatchDraw(canvas);
    
            // Step 5, draw the fade effect and restore layers
            final Paint p = scrollabilityCache.paint;
            final Matrix matrix = scrollabilityCache.matrix;
            final Shader fade = scrollabilityCache.shader;
    
            if (drawTop) {
                matrix.setScale(1, fadeHeight * topFadeStrength);
                matrix.postTranslate(left, top);
                fade.setLocalMatrix(matrix);
                p.setShader(fade);
                canvas.drawRect(left, top, right, top + length, p);
            }
    
            if (drawBottom) {
                matrix.setScale(1, fadeHeight * bottomFadeStrength);
                matrix.postRotate(180);
                matrix.postTranslate(left, bottom);
                fade.setLocalMatrix(matrix);
                p.setShader(fade);
                canvas.drawRect(left, bottom - length, right, bottom, p);
            }
    
            if (drawLeft) {
                matrix.setScale(1, fadeHeight * leftFadeStrength);
                matrix.postRotate(-90);
                matrix.postTranslate(left, top);
                fade.setLocalMatrix(matrix);
                p.setShader(fade);
                canvas.drawRect(left, top, left + length, bottom, p);
            }
    
            if (drawRight) {
                matrix.setScale(1, fadeHeight * rightFadeStrength);
                matrix.postRotate(90);
                matrix.postTranslate(right, top);
                fade.setLocalMatrix(matrix);
                p.setShader(fade);
                canvas.drawRect(right - length, top, right, bottom, p);
            }
    
            canvas.restoreToCount(saveCount);
    
            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }
    
            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);
    }
    

    view的draw,注释也写的比较清楚,1:先绘制background。2:如有必要绘制fading。3:调用onDraw绘制View自身的Content。4:绘制children。5:如有必要绘制fading edges。6:最后绘制Foreground。
    在绘制背景和自身内容的时候,有一个条件dirtyOpaque,它的值为

    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                    (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
    

    所以设置PFLAG_DIRTY_OPAQUE属性可以使dirtyOpaque为true,导致不用绘制背景和内容,这对一些父布局设置设置是可以减少绘制次数,提高绘制性能的。
    我们来看看drawBackground

    private void drawBackground(Canvas canvas) {
            final Drawable background = mBackground;
            if (background == null) {
                return;
            }
    
            setBackgroundBounds();
    
            // Attempt to use a display list if requested.
            if (canvas.isHardwareAccelerated() && mAttachInfo != null
                    && mAttachInfo.mHardwareRenderer != null) {
                mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
    
                final RenderNode renderNode = mBackgroundRenderNode;
                if (renderNode != null && renderNode.isValid()) {
                    setBackgroundRenderNodeProperties(renderNode);
                    ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
                    return;
                }
            }
    
            final int scrollX = mScrollX;
            final int scrollY = mScrollY;
            if ((scrollX | scrollY) == 0) {
                background.draw(canvas);
            } else {
                canvas.translate(scrollX, scrollY);
                background.draw(canvas);
                canvas.translate(-scrollX, -scrollY);
            }
        }
    

    先判断canvas是否支持硬件绘制,如果支持,则获取mBackgroundRenderNode,进行硬件绘制。如果是canvas支持硬件绘制的canvas是DisplayListCanvas类型的对象,否则就是一般的canvas对象。
    绘制自身的onDraw,View中的定义是空的,每个控件集成View的时候会重写它,实现自己的绘制。dispatchDraw这个主要是针对容器类控件循环调用子View的draw,又进入了一个递归绘制过程。

    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
            return child.draw(canvas, this, drawingTime);
        }
    

    注意此时调用的是View的另外一个draw函数,它含有3个参数,在其里面,它也会进行一些是否支持硬件加速的判断,如果支持则会调用updateDisplayListIfDirty更新显示列表,否则会调用到draw(含一个canvas参数的)进行绘制。

    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;
            RenderNode renderNode = null;
            Bitmap cache = null;
            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);
            }
    
    //硬件绘制更新显示列表updateDisplayListIfDirty
            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;
                }
            }
    //drawingWithDrawingCache不支持硬件绘制同时cache != null 
         final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;
            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 (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;
        }
    

    上面就是draw(三个参数)的主要过程。支持硬件绘制则调用updateDisplayListIfDirty更新RenderNode,然后Draw RenderNode。

    ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
    

    software绘制,最终调用到draw(canvas)。
    以上就是绘制,主要是software绘制的大致过程。在这个过程中,我们也将了些硬件绘制的东西,比如updateDisplayListIfDirty,drawRenderNode等,那么接下来我们就来讲讲硬件绘制。

    硬件绘制

    我们又从ViewRootImpl的draw(private void draw(boolean fullRedrawNeeded))函数开始说起。回到上文ViewRootImpl的draw中的硬件绘制调用

    private void draw(boolean fullRedrawNeeded) {
         if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
                    if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                    // If accessibility focus moved, always invalidate the root.
                    boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
                    mInvalidateRootRequested = false;
    
                    // Draw with hardware renderer.
                    mIsAnimating = false;
    
                    if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
                        mHardwareYOffset = yOffset;
                        mHardwareXOffset = xOffset;
                        invalidateRoot = true;
                    }
    
                    if (invalidateRoot) {
                        mAttachInfo.mHardwareRenderer.invalidateRoot();
                    }
    
                    dirty.setEmpty();
    
                    // Stage the content drawn size now. It will be transferred to the renderer
                    // shortly before the draw commands get send to the renderer.
                    final boolean updated = updateContentDrawBounds();
    
                    if (mReportNextDraw) {
                        // report next draw overrides setStopped()
                        // This value is re-sync'd to the value of mStopped
                        // in the handling of mReportNextDraw post-draw.
                        mAttachInfo.mHardwareRenderer.setStopped(false);
                    }
    
                    if (updated) {
                        requestDrawWindow();
                    }
    
                    mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
                }
    }
    

    可以看到主要是调用了mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this),mAttachInfo是一个View.AttachInfo对象,mHardwareRenderer是什么呢,它是ThreadedRenderer 。还记得ViewRootImpl的的构造函数中有一段代码吗?它就是在这里构造的。

    if (mSurfaceHolder == null) {
                        enableHardwareAcceleration(attrs);
                    }
    
    private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
            mAttachInfo.mHardwareAccelerated = false;
            mAttachInfo.mHardwareAccelerationRequested = false;
            mAttachInfo.mHardwareRenderer = ThreadedRenderer.create(mContext, translucent);         
        }
    

    可以看到调用了ThreadedRenderer的静态方法create创建的

    public static ThreadedRenderer create(Context context, boolean translucent) {
            ThreadedRenderer renderer = null;
            if (DisplayListCanvas.isAvailable()) {
                renderer = new ThreadedRenderer(context, translucent);
            }
            return renderer;
        }
    

    再来看看ThreadedRenderer

    private long mNativeProxy;//native的Proxy代理
    private RenderNode mRootNode;//根RenderNode
    ThreadedRenderer(Context context, boolean translucent) {
            final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
            mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
            mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
            mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
            mAmbientShadowAlpha =
                    (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
            mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
            a.recycle();
    
            long rootNodePtr = nCreateRootRenderNode();
            mRootNode = RenderNode.adopt(rootNodePtr);
            mRootNode.setClipToBounds(false);
            mNativeProxy = nCreateProxy(translucent, rootNodePtr);
    
            ProcessInitializer.sInstance.init(context, mNativeProxy);
    
            loadSystemProperties();
        }
    

    那我们在来看看RenderNode

    public class RenderNode {
    
        private boolean mValid;
        // Do not access directly unless you are ThreadedRenderer
        final long mNativeRenderNode;
        private final View mOwningView;
    
        private RenderNode(String name, View owningView) {
            mNativeRenderNode = nCreate(name);
            mOwningView = owningView;
            if (mOwningView instanceof SurfaceView) {
                nRequestPositionUpdates(mNativeRenderNode, (SurfaceView) mOwningView);
            }
        }
    
        /**
         * @see RenderNode#adopt(long)
         */
        private RenderNode(long nativePtr) {
            mNativeRenderNode = nativePtr;
            mOwningView = null;
        }
    
        /**
         * Creates a new RenderNode that can be used to record batches of
         * drawing operations, and store / apply render properties when drawn.
         *
         * @param name The name of the RenderNode, used for debugging purpose. May be null.
         *
         * @return A new RenderNode.
         */
        public static RenderNode create(String name, @Nullable View owningView) {
            return new RenderNode(name, owningView);
        }
    
        /**
         * Adopts an existing native render node.
         *
         * Note: This will *NOT* incRef() on the native object, however it will
         * decRef() when it is destroyed. The caller should have already incRef'd it
         */
        public static RenderNode adopt(long nativePtr) {
            return new RenderNode(nativePtr);
        }
    }
    

    还有一个DisplayListCanvas,它继承Canvas。
    ···
    public class DisplayListCanvas extends Canvas
    ···
    硬件渲染java层涉及的类大概就这几个,它们的关系是ThreadedRenderer负责draw。draw什么呢。draw的是RenderNode。或者Text或者Circle,Bitmap等。而又在哪里draw呢,在DisplayListCanvas中draw。每个View都有一个RenderNode,它在View的构造函数中初始化。而每个RenderNode又会和一个DisplayListCanvas关联。以上就是java层的相关类。

    final RenderNode mRenderNode;
    public View(Context context) {
            mContext = context;
            mResources = context != null ? context.getResources() : null;
            mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
            mRenderNode = RenderNode.create(getClass().getName(), this);
    }
    View() {
            mResources = null;
            mRenderNode = RenderNode.create(getClass().getName(), this);
        }
    //RenderNode的start函数
    public DisplayListCanvas start(int width, int height) {
            return DisplayListCanvas.obtain(this, width, height);
        }
    

    这样我们明白了每个View有一个RenderNode,每个RenderNode会获取到一个对应的DisplayListCanvas。而我们是在DisplayListCanvas上draw的。所以对一个View的Draw来说。在之前的draw中也说过draw分为6部分

    public void draw(Canvas canvas) {
            /*
             * Draw traversal performs several drawing steps which must be executed
             * in the appropriate order:
             *
             *      1. Draw the background
             *      2. If necessary, save the canvas' layers to prepare for fading
             *      3. Draw view's content
             *      4. Draw children
             *      5. If necessary, draw the fading edges and restore layers
             *      6. Draw decorations (scrollbars for instance)
             */
    }
    

    那就是在DisplayListCanvas依次draw了6个部分。比如background是一个bitmap或者是一个RenderNode。Content,我们可以举例假设是一个Circle。Draw children。child是一个View或者多个View。每个View会对应一个RenderNode,假设只有一个View,那就是Draw了一个RenderNode。这样View的RenderNode树形图我们可以理解成这样。


    企业微信截图_15768086242030.png

    下面看看Native层,Native层比较重要的和DisplayListCanvas对应的类有Canvas,RecordingCanvas类,RecordingCanvas继承Canvas。在DisplayListCanvas中调的绘制,比如绘制Circle或者RenderNode等,最终都是调用到RecordingCanvas中去。
    来看看Native层的RecordingCanvas和它的drawRenderNode以及drawCircle

    class ANDROID_API RecordingCanvas: public Canvas, public CanvasStateClient {
        enum class DeferredBarrierType {
            None,
            InOrder,
            OutOfOrder,
        };
    public:
        RecordingCanvas(size_t width, size_t height);
    
    void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
        auto&& stagingProps = renderNode->stagingProperties();
        RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
                Rect(stagingProps.getWidth(), stagingProps.getHeight()),
                *(mState.currentSnapshot()->transform),
                getRecordedClip(),
                renderNode);
        int opIndex = addOp(op);
        if (CC_LIKELY(opIndex >= 0)) {
            int childIndex = mDisplayList->addChild(op);
    
            // update the chunk's child indices
            DisplayList::Chunk& chunk = mDisplayList->chunks.back();
            chunk.endChildIndex = childIndex + 1;
    
            if (renderNode->stagingProperties().isProjectionReceiver()) {
                // use staging property, since recording on UI thread
                mDisplayList->projectionReceiveIndex = opIndex;
            }
        }
    }
    
    void RecordingCanvas::drawCircle(
            CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
            CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
        mDisplayList->ref(x);
        mDisplayList->ref(y);
        mDisplayList->ref(radius);
        mDisplayList->ref(paint);
        refBitmapsInShader(paint->value.getShader());
        addOp(alloc().create_trivial<CirclePropsOp>(
                *(mState.currentSnapshot()->transform),
                getRecordedClip(),
                &paint->value,
                &x->value, &y->value, &radius->value));
    }
    

    可以看到无论是drawCircle还是drawRenderNode。到了Native层,最终是形成一个CirclePropsOp或者RenderNodeOp。然后将其加入到mDisplayList中。
    看看CirclePropsOp,RenderNodeOp等,他们都继承RecordedOp

    struct CirclePropsOp : RecordedOp {
        CirclePropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
                float* x, float* y, float* radius)
                : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClip, paint)
                , x(x)
                , y(y)
                , radius(radius) {}
        const float* x;
        const float* y;
        const float* radius;
    };
    
    struct RenderNodeOp : RecordedOp {
        RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode)
                : SUPER_PAINTLESS(RenderNodeOp)
                , renderNode(renderNode) {}
        RenderNode * renderNode; // not const, since drawing modifies it
    
        /**
         * Holds the transformation between the projection surface ViewGroup and this RenderNode
         * drawing instance. Represents any translations / transformations done within the drawing of
         * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
         * DisplayList draw instance.
         *
         * Note: doesn't include transformation within the RenderNode, or its properties.
         */
        Matrix4 transformFromCompositingAncestor;
        bool skipInOrderDraw = false;
    };
    

    包括其它的如BitmapOp,BitmapMeshOp,BitmapRectOp,LinesOp(参考frameworks\base\libs\hwui\RecordOp.h文件)等等都是继承RecordedOp。至此,我们明白了所有的这一切,无论是draw circle还是draw line,bitmap,RecordNode等,最终是形成一个RecordedOp,然后add到RecordingCanvas的mDisplayList中。看看mDisplayList。

    DisplayList* mDisplayList = nullptr;
    
    typedef RecordedOp BaseOpType;
    typedef RenderNodeOp NodeOpType;
    class DisplayList {
        friend class RecordingCanvas;
    public:
        struct Chunk {
            // range of included ops in DisplayList::ops()
            size_t beginOpIndex;
            size_t endOpIndex;
    
            // range of included children in DisplayList::children()
            size_t beginChildIndex;
            size_t endChildIndex;
    
            // whether children with non-zero Z in the chunk should be reordered
            bool reorderChildren;
    
            // clip at the beginning of a reorder section, applied to reordered children
            const ClipBase* reorderClip;
        };
    
        DisplayList();
        virtual ~DisplayList();
        LsaVector<BaseOpType*> ops;
    

    结合java层的图,我们最终可以理解成这样,所有的操作最终在LsaVector<BaseOpType*> ops中增加一条RecordedOp。接下来我们来看看DisplayList是如何更新的


    15768243921064.png

    更新显示列表

    调用
    mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
    
    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
        updateRootDisplayList(view, callbacks);
    }
    
    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
            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的updateDisplayListIfDirty,这个我们在前面讲过,现在系统梳理一遍。

    public RenderNode updateDisplayListIfDirty() {
            final RenderNode renderNode = mRenderNode;
                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);
                            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;
        }
    

    关键过程是通过View自身的mRenderNode变量,通过调用start函数,获得一个DisplayListCanvas canvas,然后将canvas作为参数传给draw,进行绘制。

    final DisplayListCanvas canvas = renderNode.start(width, height);
    public DisplayListCanvas start(int width, int height) {
            return DisplayListCanvas.obtain(this, width, height);
        }
    

    所以在draw中就有draw背景,draw Content,Children等。而draw Children的时候调用的是dispatchDraw(canvas),注意参数是canvas,这个canvas是父View的RenderNode对应的Canvas,而dispatchDraw中最终会调用到drawChild,drawChild又调用到了child.draw(三个参数,第一个为Canvas,为父View的canvas)

    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);
        }
    

    所以这里就进入到了子View的draw(三个参数),同时参数传递的Canvas是父View的。
    来看看draw(三个参数)的主要过程

    //此时进入了子View的draw调用,参数canvas为父View的
    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 (drawingWithRenderNode) {
                // Delay getting the display list until animation-driven alpha values are
                // set up and possibly passed on to the view
    //关键步骤1:递归调用updateDisplayListIfDirty更新自身显示列表,将自身的内容draw在
    //自身的Canvas上
                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;
                }
            }
    
    if (!drawingWithDrawingCache) {
                if (drawingWithRenderNode) {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
    //关键步骤2:将自身的RenderNode和父View的Canvas关联
                    ((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);
                    }
                }
            }
    

    上面注释已经写的比较清楚了,draw(三个)时递归调用updateDisplayListIfDirty更新自身显示列表返回RenderNode,然后将返回的RenderNode draw在父View的Canvas上,这样就一层层完成了显示列表的更新。
    最后回到ThreadedRenderer的updateRootDisplayList,将跟View,即DecorView的RenderNode draw在ThreadedRenderer的canvas上

    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
            updateViewTreeDisplayList(view);
    
            if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
                DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
                try {
                    final int saveCount = canvas.save();
                    canvas.translate(mInsetLeft, mInsetTop);
                    callbacks.onHardwarePreDraw(canvas);
    
                    canvas.insertReorderBarrier();
                    canvas.drawRenderNode(view.updateDisplayListIfDirty());
                    canvas.insertInorderBarrier();
    
                    callbacks.onHardwarePostDraw(canvas);
                    canvas.restoreToCount(saveCount);
                    mRootNodeNeedsUpdate = false;
                } finally {
                    mRootNode.end(canvas);
                }
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    

    这样就完成了显示列表的更新,最后又回到mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this)

    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
            attachInfo.mIgnoreDirtyState = true;
    
            final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
            choreographer.mFrameInfo.markDrawStart();
    
            //更新显示列表
            updateRootDisplayList(view, callbacks);
    
            attachInfo.mIgnoreDirtyState = false;
    
            // register animating rendernodes which started animating prior to renderer
            // creation, which is typical for animators started prior to first draw
            if (attachInfo.mPendingAnimatingRenderNodes != null) {
                final int count = attachInfo.mPendingAnimatingRenderNodes.size();
                for (int i = 0; i < count; i++) {
                    registerAnimatingRenderNode(
                            attachInfo.mPendingAnimatingRenderNodes.get(i));
                }
                attachInfo.mPendingAnimatingRenderNodes.clear();
                // We don't need this anymore as subsequent calls to
                // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
                attachInfo.mPendingAnimatingRenderNodes = null;
            }
            //更新完显示列表之后调用nSyncAndDrawFrame进行渲染
            final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
            int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
            if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
                setEnabled(false);
                attachInfo.mViewRootImpl.mSurface.release();
                // Invalidate since we failed to draw. This should fetch a Surface
                // if it is still needed or do nothing if we are no longer drawing
                attachInfo.mViewRootImpl.invalidate();
            }
            if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
                attachInfo.mViewRootImpl.invalidate();
            }
        }
    

    至此终于讲完了java层UI绘制的相关内容和过程,但是具体的Native层的绘制,调用nSyncAndDrawFrame到底如何绘制的,后面继续。

    相关文章

      网友评论

          本文标题:View的绘制过程,显示列表的更新

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