HardwareRenderer drawing starts with a call to the HardwareRenderer class draw method for View drawing.

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent.View.AttachInfo.Callbacks.HardwareRenderer.HardwareDrawCallbacks {...private void draw(boolean fullRedrawNeeded) {...if(! dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {if(mAttachInfo.mHardwareRenderer ! =null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                ......
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
            } else{... }}... }... }Copy the code
  1. Update RootDisplayList (DisplayList is used to save properties and perform playback on the renderer)
  2. Registers the animation render node
  3. Synchronize and draw frames

Let’s focus on 1.

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {...@Override
    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
        attachInfo.mIgnoreDirtyState = true;

        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
        choreographer.mFrameInfo.markDrawStart();
        // 1. Update RootDisplayList
        updateRootDisplayList(view, callbacks);

        attachInfo.mIgnoreDirtyState = false;

        // 2. Register the animation render node
        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;
        }

        final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
        // 3. Synchronize and draw frames
        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
        if((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) ! =0) {
            setEnabled(false);
            attachInfo.mViewRootImpl.mSurface.release();
            // Invalid because drawing failed. If you still need it, you should get a Surface;
            // If no more drawing is done, no action is performed
            attachInfo.mViewRootImpl.invalidate();
        }
        if((syncResult & SYNC_INVALIDATE_REQUIRED) ! =0) { attachInfo.mViewRootImpl.invalidate(); }}... }Copy the code
  1. Call updateViewTreeDisplayList (…). Update the View tree DisplayList
  2. Call RenderNode start(…) Method to get the DisplayListCanvas object
  3. Call drawRenderNode (…). Draw render node
  4. Call the render node end method

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {...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);
                // Call the onHardwarePreDraw method before drawing
                callbacks.onHardwarePreDraw(canvas);
                // Insert the Reorder barrier
                canvas.insertReorderBarrier();
                // Draw the render node
                canvas.drawRenderNode(view.updateDisplayListIfDirty());
                // Insert the Inorder barrier
                canvas.insertInorderBarrier();
                // Call the onHardwarePostDraw method after drawing
                callbacks.onHardwarePostDraw(canvas);
                canvas.restoreToCount(saveCount);
                mRootNodeNeedsUpdate = false;
            } finally {
                // Call render node endmRootNode.end(canvas); } } Trace.traceEnd(Trace.TRACE_TAG_VIEW); }... }Copy the code

UpdateViewTreeDisplayList (…). Set and clear some flags, and then call the View class updateDisplayListIfDirty() to update the DisplayList if the View is dirty.

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {...private void updateViewTreeDisplayList(View view) {
        // Set the PFLAG_DRAWN flag
        view.mPrivateFlags |= View.PFLAG_DRAWN;
        // if the PFLAG_INVALIDATED flag is set, mRecreateDisplayList is assigned to true
        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                == View.PFLAG_INVALIDATED;
        // Clear the PFLAG_INVALIDATED flag
        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
        view.updateDisplayListIfDirty();
        view.mRecreateDisplayList = false; }... }Copy the code

UpdateDisplayListIfDirty () gets the View’s RenderNode and updates its DisplayList (if required and supported).

  1. Call dispatchGetDisplayList() to get the DisplayList distribution
  2. Call dispatchDraw (…). Dispatch a draw or call draw(…) directly. draw

tips:

LAYER_TYPE_SOFTWARE indicates that the View has a software layer. The software layer is supported by bitmaps, and views can be rendered using Android’s software rendering pipeline even with hardware acceleration enabled.

The software layer has a variety of uses

When applications do not use hardware acceleration, the software layer is useful to apply a specific color filter and/or blend modes and/or transitransparent to a View and all its sub-views.

When an application uses hardware acceleration, the software layer is used to render drawing primitives that are not supported by the hardware acceleration pipeline. It can also be used to cache complex view trees into textures and reduce the complexity of drawing operations. For example, when a complex view tree is being animated, a software layer can only be used to render the view tree once.

The software layer should be avoided when the affected view tree is frequently updated. Each update requires the software layer to be re-rendered, which can be slow (especially if hardware acceleration is turned on, since the software layer is uploaded to the hardware texture after each update).

frameworks/base/core/java/android/view/View.java

@UiThread
public class View implements Drawable.Callback.KeyEvent.Callback.AccessibilityEventSource {...@NonNull
    public RenderNode updateDisplayListIfDirty(a) {
        final RenderNode renderNode = mRenderNode;
        if(! canHaveDisplayList()) {// Cannot fill RenderNode
            return renderNode;
        }

        if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0| |! renderNode.isValid() || (mRecreateDisplayList)) {// No need to recreate the DisplayList, just tell the child to restore/recreate their
            if(renderNode.isValid() && ! mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList();return renderNode; // no work needed
            }

            // Recreate the DisplayList
            mRecreateDisplayList = true;

            int width = mRight - mLeft;
            int height = mBottom - mTop;
            // Get the layer type
            int layerType = getLayerType();
            // Call RenderNode class start(...) Method to get the DisplayListCanvas object
            final DisplayListCanvas canvas = renderNode.start(width, height);
            canvas.setHighContrastText(mAttachInfo.mHighContrastText);

            try {
                // Get the HardwareLayer object
                final HardwareLayer layer = getHardwareLayer();
                if(layer ! =null && layer.isValid()) {
                    // Call the DisplayListCanvas class drawHardwareLayer to draw the hardware layer
                    canvas.drawHardwareLayer(layer, 0.0, mLayerPaint);
                } else if (layerType == LAYER_TYPE_SOFTWARE) {
                    // If layer type is LAYER_TYPE_SOFTWARE
                    // Create a draw cache
                    buildDrawingCache(true);
                    Bitmap cache = getDrawingCache(true);
                    if(cache ! =null) {
                        // Draw a bitmap
                        canvas.drawBitmap(cache, 0.0, mLayerPaint); }}else {
                    // Generally enter this branch
                    // Calculate the scroll
                    computeScroll();
                    // Apply panning
                    canvas.translate(-mScrollX, -mScrollY);
                    mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;

                    // Fast path without background layout
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                        // Distribute the drawing
                        dispatchDraw(canvas);
                        if(mOverlay ! =null&&! mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); }}else{ draw(canvas); }}}finally {
                // Call render node endrenderNode.end(canvas); setDisplayListProperties(renderNode); }}else {
            mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
        }
        returnrenderNode; }... }Copy the code

DispatchGetDisplayList () is an empty method in the View. According to the previous analysis, we pass in a DecorView.

frameworks/base/core/java/android/view/View.java

@UiThread
public class View implements Drawable.Callback.KeyEvent.Callback.AccessibilityEventSource {...protected void dispatchGetDisplayList(a) {}... }Copy the code

Pass to the DecorView class dispatchGetDisplayList() method implementation. We don’t actually rewrite it, but we eventually find the corresponding implementation in the ViewGroup. DecorView inherits from FrameLayout, and FrameLayout inherits from the ViewGroup.

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {...private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {... }... }Copy the code

This method is used to make the child views of this ViewGroup restore or recreate their DisplayList. GetDisplayList () calls the parent ViewGroup when it doesn’t need to recreate its own DisplayList, which happens if it goes through the regular draw/dispatchDraw mechanism.

frameworks/base/core/java/android/view/ViewGroup.java

@UiThread
public abstract class ViewGroup extends View implements ViewParent.ViewManager {...@Override
    protected void dispatchGetDisplayList(a) {
        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)) {
                // Recreate the child View DisplayListrecreateChildDisplayList(child); }}... }... }Copy the code

The child View calls updateDisplayListIfDirty() to update its DisplayList.

frameworks/base/core/java/android/view/ViewGroup.java

@UiThread
public abstract class ViewGroup extends View implements ViewParent.ViewManager {...private void recreateChildDisplayList(View child) { child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) ! =0;
        child.mPrivateFlags &= ~PFLAG_INVALIDATED;
        child.updateDisplayListIfDirty();
        child.mRecreateDisplayList = false; }... }Copy the code

Now back to the updateDisplayListIfDirty() method. LayerType equals 0, which is LAYER_TYPE_NONE (indicating that the View has no layers). First call dispatchDraw(…) Start sending drawings. This method is implemented in a ViewGroup. Here we call drawChild(…) Draw children.

frameworks/base/core/java/android/view/ViewGroup.java

@UiThread
public abstract class ViewGroup extends View implements ViewParent.ViewManager {...@Override
    protected void dispatchDraw(Canvas canvas) {
        boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;
        intflags = mGroupFlags; .if (usingRenderNodeProperties) canvas.insertReorderBarrier();
        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
        inttransientIndex = transientCount ! =0 ? 0 : -1;
        // If there is no HW acceleration, only the pre-sorted list is used, because the HW pipeline will do the drawing reordering internally
        final ArrayList<View> preorderedList = usingRenderNodeProperties
                ? null : buildOrderedChildList();
        final boolean customOrder = preorderedList == null
                && isChildrenDrawingOrderEnabled();
        for (int i = 0; i < childrenCount; i++) {
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() ! =null) {
                    // Draw the child
                    more |= drawChild(canvas, transientChild, drawingTime);
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1; }}int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
            final View child = (preorderedList == null)? children[childIndex] : preorderedList.get(childIndex);if((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() ! =null) {
                // Draw the childmore |= drawChild(canvas, child, drawingTime); }}while (transientIndex >= 0) {
            // There may be other temporary views after the normal view
            final View transientChild = mTransientViews.get(transientIndex);
            if((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() ! =null) {
                // Draw the child
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            transientIndex++;
            if (transientIndex >= transientCount) {
                break; }}... }... }Copy the code

Draws a child View of this ViewGroup. This method is responsible for putting the canvas in the correct state. Its internal implementation calls the View’s draw(…) Methods.

frameworks/base/core/java/android/view/ViewGroup.java

@UiThread
public abstract class ViewGroup extends View implements ViewParent.ViewManager {...protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime); }... }Copy the code

The View will eventually draw with draw(Canvas Canvas).

Drawing performs several drawing steps, which must be performed in the proper order:

  1. Draw the background
  2. If necessary, save the layers of the canvas to prepare for FADING
  3. Draws the contents of the view
  4. Draw the children
  5. If necessary, paint the fading edges and restore the layers
  6. Draw decorations (such as scroll bars)

frameworks/base/core/java/android/view/View.java

@UiThread
public class View implements Drawable.Callback.KeyEvent.Callback.AccessibilityEventSource {...@CallSuper
    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;// 1. Draw the background
        int saveCount;

        if(! dirtyOpaque) { drawBackground(canvas); }// Skip steps 2 and 5 if possible (general)
        final int viewFlags = mViewFlags;
        booleanhorizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) ! =0;
        booleanverticalEdges = (viewFlags & FADING_EDGE_VERTICAL) ! =0;
        if(! verticalEdges && ! horizontalEdges) {// 3. Draw the contents of the View. Custom views usually implement onDraw methods
            if(! dirtyOpaque) onDraw(canvas);// 4. Draw the child
            dispatchDraw(canvas);

            // Overlay is the part of the content that is drawn below the foreground
            if(mOverlay ! =null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // 6. Draw decorations (foreground, scrollbar)
            onDrawForeground(canvas);

            // we're done...
            return;
        }

        /* * Here, we do the full routine...... * (Speed is not important in this case, which is why we repeat some of the tests we did above) */

        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0 f;
        float bottomFadeStrength = 0.0 f;
        float leftFadeStrength = 0.0 f;
        float rightFadeStrength = 0.0 f;

        // 2. Save the canvas layer
        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;
        }

        // You can also cut horizontal gradients if desired
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }

        if (verticalEdges) {
            topFadeStrength = Math.max(0.0 f, Math.min(1.0 f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0 f;
            bottomFadeStrength = Math.max(0.0 f, Math.min(1.0 f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0 f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0 f, Math.min(1.0 f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0 f;
            rightFadeStrength = Math.max(0.0 f, Math.min(1.0 f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0 f;
        }

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

        // 3. Draw content
        if(! dirtyOpaque) onDraw(canvas);// 4. Draw the child
        dispatchDraw(canvas);

        // 5. Draw the fade effect and restore the layer
        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 the part of the content that is drawn below the foreground
        if(mOverlay ! =null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // 6. Draw decorations (foreground, scrollbar)onDrawForeground(canvas); }... }Copy the code

Now proceed to the RootNode class start(…) Methods.

Start recording the DisplayList for the render node. All operations performed on the returned canvas are recorded and stored in this DisplayList. Calling this method invalidates the flag rendering node until end(DisplayListCanvas) is called. Only valid render nodes can be replayed.

  1. Call the DisplayListCanvas class static obtain(…) Method returns the DisplayListCanvas object
  2. Set the view port size
  3. Pre-draw action, called before any draw operation is performed. For DisplayList, the dirty area should always be null.

frameworks/base/core/java/android/view/RenderNode.java

public class RenderNode {...public DisplayListCanvas start(int width, int height) {
        DisplayListCanvas canvas = DisplayListCanvas.obtain(this);
        // Set the view port size
        canvas.setViewport(width, height);
        // For DisplayList, the dirty area should always be null.
        // Pre-draw action, called before performing any draw operation
        canvas.onPreDraw(null);
        returncanvas; }... }Copy the code

The DisplayListCanvas class records the implementation of the GL canvas for the drawing operation. It’s used for DisplayList. This class keeps a list of all drawn and bitmap objects it draws, preventing the bitmap’s backup memory from being freed while DisplayList still holds local references to memory.

Assuming there is no DisplayListCanvas object in the canvas pool, it will now have a new one.

frameworks/base/core/java/android/view/DisplayListCanvas.java

public class DisplayListCanvas extends Canvas {
    // The record canvas pool should be large enough to handle deeply nested view hierarchies because DisplayList is generated recursively.
    private static final int POOL_LIMIT = 25;

    private static final SynchronizedPool<DisplayListCanvas> sPool =
            new SynchronizedPool<DisplayListCanvas>(POOL_LIMIT);

    RenderNode mNode;
    private int mWidth;
    private int mHeight;

    static DisplayListCanvas obtain(@NonNull RenderNode node) {
        if (node == null) throw new IllegalArgumentException("node cannot be null");
        DisplayListCanvas canvas = sPool.acquire();
        if (canvas == null) {
            canvas = new DisplayListCanvas();
        }
        canvas.mNode = node;
        returncanvas; }... }Copy the code

Call the nCreateDisplayListCanvas() jni method and pass the return value as an input parameter to the parent Canvas constructor.

frameworks/base/core/java/android/view/DisplayListCanvas.java

public class DisplayListCanvas extends Canvas {...private DisplayListCanvas(a) {
        super(nCreateDisplayListCanvas());
        mDensity = 0; // Disable bitmap density scaling
    }

    private static native long nCreateDisplayListCanvas(a); . }Copy the code

In the Canvas constructor, only entries are stored in the member variable mNativeCanvasWrapper.

frameworks/base/graphics/java/android/graphics/Canvas.java

public class DisplayListCanvas extends Canvas {.../** * should only be allocated in the constructor (or in the software canvas call setBitmap) and released in the finalizer. *@hide* /
    protected longmNativeCanvasWrapper; .public Canvas(long nativeCanvas) {
        if (nativeCanvas == 0) {
            throw newIllegalStateException(); } mNativeCanvasWrapper = nativeCanvas; . }... }Copy the code

Let’s move on to the nCreateDisplayListCanvas() function’s native implementation. New the DisplayListCanvas object and then return it with a strong jLong, thus passing a reference to the Native layer DisplayListCanvas object back to the Java layer.

frameworks/base/core/jni/android_view_DisplayListCanvas.cpp

static jlong android_view_DisplayListCanvas_createDisplayListCanvas(JNIEnv* env, jobject clazz) {
    return reinterpret_cast<jlong>(new DisplayListCanvas);
}
Copy the code

The mDisplayListData member variable is initialized to a null pointer.

frameworks/base/libs/hwui/DisplayListCanvas.cpp

DisplayListCanvas::DisplayListCanvas()
    : mState(*this),mResourceCache(ResourceCache::getInstance()),mDisplayListData(nullptr),mTranslateX(0.0 f),mTranslateY(0.0 f),mHasDeferredTranslate(false),mDeferredBarrierType(kBarrier_None)
    , mHighContrastText(false),mRestoreSaveCount(- 1) {}Copy the code

OnPreDraw (…). The input parameter is NULL, so the else branch is taken.

frameworks/base/core/java/android/view/DisplayListCanvas.java

public class DisplayListCanvas extends Canvas {...public void onPreDraw(Rect dirty) {
        if(dirty ! =null) {... }else{ nPrepare(mNativeCanvasWrapper); }}private static native void nPrepare(long renderer); . }Copy the code
  1. Take out the DisplayListCanvas reference that the Java layer holds
  2. Call the prepare() method on the DisplayListCanvas object

frameworks/base/core/jni/android_view_DisplayListCanvas.cpp

static void android_view_DisplayListCanvas_prepare(JNIEnv* env, jobject clazz, jlong rendererPtr) {
    DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr);
    renderer->prepare(a); }Copy the code

Prepare() actually calls prepareDirty(…) internally. , but the entry is fixed (see the name know its meaning).

frameworks/base/libs/hwui/DisplayListCanvas.h

class ANDROID_API DisplayListCanvas: public Canvas, public CanvasStateClient {
public:...// ----------------------------------------------------------------------------
// HWUI frame state operation
// ----------------------------------------------------------------------------

    void prepareDirty(float left, float top, float right, float bottom);
    void prepare(a) { prepareDirty(0.0 f.0.0 f.width(), height()); }... }Copy the code

PrepareDirty (…). Method to create the DisplayListData object, which, to recap, is used to hold the actual data (the data structure that holds the list of commands used in the DisplayList stream).

frameworks/base/libs/hwui/DisplayListCanvas.cpp

void DisplayListCanvas::prepareDirty(float left, float top,
        float right, float bottom) {

    LOG_ALWAYS_FATAL_IF(mDisplayListData,
            "prepareDirty called a second time during a recording!");
    mDisplayListData = new DisplayListData(a); . }Copy the code

ProjectionReceiveIndex — The index of DisplayListOp.

frameworks/base/libs/hwui/DisplayList.cpp

DisplayListData::DisplayListData()
        : projectionReceiveIndex(- 1),hasDrawOps(false) {}Copy the code

Finally, the DisplayListCanvas class drawRenderNode(…) Methods. This method means to draw the specified DisplayList on this canvas. The DisplayList can only be drawn if Android.viet.renderNode# isValid() returns true.

frameworks/base/core/java/android/view/DisplayListCanvas.java

public class DisplayListCanvas extends Canvas {...public void drawRenderNode(RenderNode renderNode) {
        nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList());
    }

    private static native void nDrawRenderNode(long renderer, long renderNode); . }Copy the code
  1. Get the DisplayListCanvas object
  2. Get the RenderNode object
  3. Call the DisplayListCanvas class drawRenderNode method to draw the render node

frameworks/base/core/jni/android_view_DisplayListCanvas.cpp

static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env, jobject clazz, jlong rendererPtr, jlong renderNodePtr) {
    DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr);
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    renderer->drawRenderNode(renderNode);
}
Copy the code
  1. Create the DrawRenderNodeOp object
  2. Call addRenderNodeOp (…). Add DrawRenderNodeOp to the list

frameworks/base/libs/hwui/DisplayListCanvas.cpp

void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) {
    LOG_ALWAYS_FATAL_IF(! renderNode,"missing rendernode");
    DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(
            renderNode,
            *mState.currentTransform(),
            mState.clipIsSimple());
    addRenderNodeOp(op);
}
Copy the code

Add the DrawRenderNodeOp object to the DisplayListData class Vector<DrawRenderNodeOp*>.

frameworks/base/libs/hwui/DisplayListCanvas.cpp

size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) {
    int opIndex = addDrawOp(op);
    int childIndex = mDisplayListData->addChild(op);

    // Update the subindex of the block
    DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop(a); chunk.endChildIndex = childIndex +1;

    if (op->renderNode() - >stagingProperties().isProjectionReceiver()) {
        // Use the staging property because it is logged on the UI thread
        mDisplayListData->projectionReceiveIndex = opIndex;
    }
    return opIndex;
}
Copy the code

Finally, draw a sequence diagram to summarize.