Android应用程序UI硬件加速渲染的Display List构建过程分析

发表于 5年以前  | 总阅读数:3389 次

在硬件加速渲染环境中,Android应用程序窗口的UI渲染是分两步进行的。第一步是构建Display List,发生在应用程序进程的Main Thread中;第二步是渲染Display List,发生在应用程序进程的Render Thread中。Display List是以视图为单位进行构建的,因此每一个视图都对应有一个Display List。本文详细分析这些Display List的构建过程。

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

这里说的Display List与Open GL里面的Display List在概念上是类似的,不过是两个不同的实现。Display List的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。这些绘制命令最终会转化为Open GL命令由GPU执行。这意味着我们在调用Canvas API绘制UI时,实际上只是将Canvas API调用及其参数记录在Display List中,然后等到下一个Vsync信号到来时,记录在Display List里面的绘制命令才会转化为Open GL命令由GPU执行。与直接执行绘制命令相比,先将绘制命令记录在Display List中然后再执行有两个好处。第一个好处是在绘制窗口的下一帧时,若某一个视图的UI没有发生变化,那么就不必执行与它相关的Canvas API,即不用执行它的成员函数onDraw,而是直接复用上次构建的Display List即可。第二个好处是在绘制窗口的下一帧时,若某一个视图的UI发生了变化,但是只是一些简单属性发生了变化,例如位置和透明度等简单属性,那么也不必重建它的Display List,而是直接修改上次构建的Display List的相关属性即可,这样也可以省去执行它的成员函数onDraw。

Android应用程序窗口视图是树形结构的,因此它们的Display List是从根视图开始构建的,并且子视图的Display List包含在父视图的Display List中。这意味着根视图的Display List包含了Android应用程序窗口UI所有的绘制命令,因此最后我们只需要对根视图的Display List进行渲染即可得到Android应用程序窗口的UI,如图1所示:

图1 Android应用程序窗口的Display List构建示意图

Android应用程序窗口的根视图是虚拟的,抽象为一个Root Render Node。此外,一个视图如果设置有Background,那么这个Background也会抽象为一个Background Render Node。Root Render Node、Background Render Node和其它真实的子视图,除了TextureView和软件渲染的子视图之外,都具有Display List,并且是通过一个称为Display List Renderer的对象进行构建的。TextureView不具有Display List,它们是通过一个称为Layer Renderer的对象以Open GL纹理的形式来绘制的,不过这个纹理也不是直接就进行渲染的,而是先记录在父视图的Display List中以后再进行渲染的。同样,软件渲染的子视图也不具有Display List,它们先绘制在一个Bitmap上,然后这个Bitmap再记录在父视图的Display List中以后再进行渲染的。

最后,Root Render Node的Display List被一个称为Open GL Renderer的对象进行渲染,就得到Android应用程序窗口的UI了。接下来我们就结合源代码来分析Android应用程序窗口视图的Display List的构建过程。

在前面Android应用程序UI硬件加速渲染环境初始化过程分析一文提到,Android应用程序窗口UI的绘制过程是从ViewRootImpl类的成员函数performDraw开始的,它的实现如下所示:

public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
        ......

        private void performDraw() {
            ......

            try {
                draw(fullRedrawNeeded);
            } finally {
                ......
            }

            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。

ViewRootImpl类的成员函数performDraw主要是调用了另外一个成员函数draw执行UI绘制工作,后者的实现如下所示:

public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
        ......

        private void draw(boolean fullRedrawNeeded) {
            ......

            final Rect dirty = mDirty;
            ......

            if (!dirty.isEmpty() || mIsAnimating) {
                ......

                if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                    ......

                    mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
                } else {
                    ......
                    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                        return;
                    }
                }
            }

            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。

经过一些滚动相关的处理之后,在两种情况下,需要真正地重绘窗口的下一帧。第一种情况是当前需要更新的区域,即ViewRootImpl类的成员变量mDirty描述的脏区域不为空。第二种情况下窗口当前有动画需要执行,即ViewRootImpl类的成员变量mIsAnimating的值等于true。

在上述两种情况下,如果ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值不为null,并且调用它指向的一个HardwareRenderer对象的成员函数isEnabled的返回值为true,那么就调用这个HardwareRenderer对象的另外一个成员函数draw执行渲染工作。从前面Android应用程序UI硬件加速渲染环境初始化过程分析一文可以知道,当使用硬件加速渲染时,ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值不为null,并且它指向的是一个ThreadedRenderer对象。如果该ThreadedRenderer对象也设置了支持硬件加速渲染,那么调用它的成员函数isEnabled的返回值就为true。这意味着当使用硬件加速渲染时,ViewRootImpl类的成员函数draw调用的是ThreadedRenderer类的成员函数draw。另一方面,当使用软件渲染时,ViewRootImpl类的成员函数draw调用的是另外一个成员函数drawSoftware。

软件渲染的执行过程可以参考前面Android应用程序窗口(Activity)的测量(Measure)、布局(Layout)和绘制(Draw)过程分析一文。这里我们只关注硬件渲染的执行过程,因此接下来我们继续分析ThreadedRenderer类的成员函数draw的实现,如下所示:

public class ThreadedRenderer extends HardwareRenderer {
        ......

        @Override
        void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
            ......

            updateRootDisplayList(view, callbacks);
            ......

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

            int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
                    recordDuration, view.getResources().getDisplayMetrics().density);
            if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
                attachInfo.mViewRootImpl.invalidate();
            }
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。

ThreadedRenderer类的成员函数draw主要是完成以下四件事情:

1. 调用成员函数updateRootDisplayList构建参数view描述的视图的Display List,该视图即为图1所示的Decor View。

2. 调用成员函数registerAnimatingRenderNode将保存在参数attachInfo指向的一个AttachInfo对象的成员变量mPendingAnimatingRenderNodes描述的一个列表中的Render Node注册到Native层中去。这些Render Node描述的是当前窗口设置的动画。

3. 调用成员函数nSyncAndDrawFrame通知Render Thread绘制下一帧。

4. 如果成员函数nSyncAndDrawFrame的返回值syncResult的SYNC_INVALIDATE_REQUIRED位不等于0,就表明Render Thread可能需要与Main Thread进行信息同步,这时候就时候向Main Thread发送一个INVALIDATE消息,以便Main Thread可以进行信息同步。这种情况一般发生在当前绘制的一帧包含有同步动画时。例如,同步动画显示到一半,需要中止,这个中止的操作就是由Main Thread发出的,然后由Render Thread检测到这个中止操作。

这里我们只关注第一件事情,其余三件事情在接下来的两篇文章中再详细分析。

ThreadedRenderer类的成员函数updateRootDisplayList的实现如下所示:

public class ThreadedRenderer extends HardwareRenderer {
        ......

        private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
            ......
            updateViewTreeDisplayList(view);

            if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
                HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
                try {
                    final int saveCount = canvas.save();
                    .......

                    canvas.insertReorderBarrier();
                    canvas.drawRenderNode(view.getDisplayList());
                    canvas.insertInorderBarrier();

                    ......

                    canvas.restoreToCount(saveCount);
                    mRootNodeNeedsUpdate = false;
                } finally {
                    mRootNode.end(canvas);
                }
            }
            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。

ThreadedRenderer类的成员函数updateRootDisplayList通过调用另一个成员函数updateViewTreeDisplayList来构建参数view描述的视图的Display List,即图1中的Decor View的Display List。构建好的这个Display List可以通过调用参数view描述的视图的成员函数getDisplayList获得的一个Render Node来描述。

ThreadedRenderer类的成员变量mRootNodeNeedsUpdate是一个布尔变量,当它的值等于true的时候,就表示要更新另外一个成员变量mRootNode描述的一个Render Node的Display List。另外,如果ThreadedRenderer类的成员变量mRootNode描述的Render Node还未构建过Display List,那么这时候调用它的成员函数isValid的返回值为true,这种情况也表示要更新它的Display List。

从前面Android应用程序UI硬件加速渲染环境初始化过程分析一文可以知道,ThreadedRenderer类的成员变量mRootNode描述的Render Node即为即为当前窗口的Root Node,更新它的Display List实际上就是要将参数view描述的视图的Display List记录到它里面去,具体方法如下所示:

1. 调用ThreadedRenderer类的成员变量mRootNode描述的Render Node的成员函数start获得一个Hardware Canvas。

2. 调用上面获得的Hardware Canvas的成员函数drawRenderNode将参数view描述的视图的Display List绘制在它里面。在绘制参数view描述的视图的Display List的前后,会调用Hardware Canvas的成员函数insertReorderBarrier和insertInorderBarrier分别设置一个Reorder Barrier和一个Inorder Barrier。后面我们在分析Display List绘制在Hardware Canvas的过程时就会看到,插入这些Barrier是为了将一个View的所有的Draw Op及其子View对应的Draw Op记录在一个Chunk中。其中,Reorder Barrier表明在真正渲染这些Chunck记录的Draw Op时,需要考虑按照Z轴坐标值重新排列子View的渲染顺序。

3. 调用ThreadedRenderer类的成员变量mRootNode描述的Render Node的成员函数end取出上述已经绘制好的Hardware Canvas的数据,并且作为上述Render Node的新的Display List。

接下来,我们首先分析ThreadedRenderer类的成员变量mRootNode描述的Render Node的Display List的更新过程,即RootNode类的成员函数start、HardwareCanvas类的成员函数drawRenderNode和RootNode类的成员函数end的实现,然后再回过头来分析参数view描述的视图的Display List的构建过程,即ThreadedRenderer类的成员函数updateViewTreeDisplayList的实现。

RootNode类的成员函数start的实现如下所示:

public class RenderNode {
        ......

        public HardwareCanvas start(int width, int height) {
            HardwareCanvas canvas = GLES20RecordingCanvas.obtain(this);
            canvas.setViewport(width, height);
            ......
            return canvas;
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/RenderNode.java中。

RootNode类的成员函数start的核心是调用GLES20RecordingCanvas类的静态成员函数obtain一个类型为GLES20RecordingCanvas的Hardware Canvas,然后在设置了该Hardware Canvas的View Port之后,返回给调用者。

GLES20RecordingCanvas类的静态成员函数obtain的实现如下所示:

class GLES20RecordingCanvas extends GLES20Canvas {
        ......

        private static final int POOL_LIMIT = 25;

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

        RenderNode mNode;

        private GLES20RecordingCanvas() {
            super();
        }

        static GLES20RecordingCanvas obtain(@NonNull RenderNode node) {
            ......
            GLES20RecordingCanvas canvas = sPool.acquire();
            if (canvas == null) {
                canvas = new GLES20RecordingCanvas();
            }
            canvas.mNode = node;
            return canvas;
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/GLES20RecordingCanvas.java中。

GLES20RecordingCanvas类的静态成员函数obtain首先是从一个GLES20RecordingCanvas对象池中请求一个GLES20RecordingCanvas对象。如果获取失败,再直接创建一个GLES20RecordingCanvas对象。在将获取到的GLES20RecordingCanvas对象返回给调用者之前,还会将参数node描述的Render Node保存在其成员变量mNode中。

接下来我们继续关注GLES20RecordingCanvas对象的创建过程,即GLES20RecordingCanvas类的构造函数的实现。GLES20RecordingCanvas类的构造函数只是简单调用了父类GLES20Canvas的构造函数,它的实现如下所示:

class GLES20Canvas extends HardwareCanvas {
        ......

        protected long mRenderer;
        ......

        protected GLES20Canvas() {
            ......
            mRenderer = nCreateDisplayListRenderer();
            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/GLES20Canvas.java中。

GLES20Canvas类的构造函数最主要做的事情就是调用另外一个成员函数nCreateDisplayListRenderer在Native层创建了一个Display List Renderer,并且将它的地址保存在成员变量mRenderer中。

GLES20Canvas类的成员函数nCreateDisplayListRenderer是一个JNI函数,由Native层的函数android_view_GLES20Canvas_createDisplayListRenderer实现,如下所示:

static jlong android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) {
        return reinterpret_cast<jlong>(new DisplayListRenderer);
    }

这个函数定义在文件frameworks/base/core/jni/android_view_GLES20Canvas.cpp中。

从这里就可以看到,函数android_view_GLES20Canvas_createDisplayListRenderer创建了一个DisplayListRenderer对象之后,就将它的地址返回给调用者。

从上面分析的过程就可以知道,调用RenderNode类的成员函数start获得的一个Hardware Canvas的具体类型为GLES20RecordingCanvas,它通过父类GLES20Canvas在Native层创建了一个DisplayListRenderer对象。

因此当我们调用上述Hardware Canvas的成员函数drawRenderNode绘制一个Display List时,调用的实际上是GLES20RecordingCanvas类的成员函数drawRenderNode,不过这个成员函数是从其父类GLES20Canvas继承下来的,它的实现如下:

class GLES20Canvas extends HardwareCanvas {
        ......

        @Override
        public int drawRenderNode(RenderNode renderNode, Rect dirty, int flags) {
            return nDrawRenderNode(mRenderer, renderNode.getNativeDisplayList(), dirty, flags);
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/GLES20Canvas.java中。

GLES20Canvas类的成员函数drawRenderNode首先是调用参数renderNode指向的一个RenderNode对象的成员函数getNativeDisplayList获得其在Native层对应的Display List,接着再调用另外一个成员函数nDrawRenderNode将获得的Native Display List通过成员变量mRenderer描述的一个在Native层的Display List Renderer绘制在当前的GLES20 Canvas中。Native层的Display List实际上是通过Native层的Render Node来描述的。

GLES20Canvas类的成员函数nDrawRenderNode是一个JNI函数,由Native层的函数android_view_GLES20Canvas_drawRenderNode实现,如下所示:

static jint android_view_GLES20Canvas_drawRenderNode(JNIEnv* env,
            jobject clazz, jlong rendererPtr, jlong renderNodePtr,
            jobject dirty, jint flags) {
        DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
        RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
        android::uirenderer::Rect bounds;
        status_t status = renderer->drawRenderNode(renderNode, bounds, flags);
        ......
        return status;
    }

这个函数定义在文件frameworks/base/core/jni/android_view_GLES20Canvas.cpp中。

由前面的分析可以知道,参数rendererPtr和renderNodePtr描述的分别是Native层的一个DisplayListRenderer和RenderNode对象, 现在要做的事情就是将参数renderNodePtr描述的Render Node绘制在参数rendererPtr的Display List Renderer的内部,这是通过调用DisplayListRenderer类的成员函数drawRenderNode实现的。

DisplayListRenderer类的成员函数drawRenderNode的实现如下所示:

status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
        ......

        // dirty is an out parameter and should not be recorded,
        // it matters only when replaying the display list
        DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform());
        addRenderNodeOp(op);

        return DrawGlInfo::kStatusDone;
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

DisplayListRenderer类的成员函数drawRenderNode首先是将参数renderNode描述的Render Node封装成一个DrawRenderNodeOp,然后再通过调用另外一个成员函数addRenderNode将上述DrawRenderNodeOp记录在内部维护的Display List Data中。

DisplayListRenderer类的成员函数addRenderNodeOp的实现如下所示:

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

        // update the chunk's child indices
        DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop();
        chunk.endChildIndex = childIndex + 1;

        if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
            // use staging property, since recording on UI thread
            mDisplayListData->projectionReceiveIndex = opIndex;
        }
        return opIndex;
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

DisplayListRenderer类有一个成员变量mDisplayListData,它指向的是一个DisplayListData对象,用来记录当前正在处理的DisplayListRenderer对应的Render Node的绘制命令。DisplayListData类通过三个向量来记录一个Render Node的绘制命令,如图2所示:

图2 Display List Data结构示意图

这三个向量分别是一个Display List Op Vector、Chunk Vector和Draw Render Node Op Vector,其中:

  1. Display List Op Vector包含了一个Render Node的所有绘制命令,每一个绘制命令用一个Display List Op来描述。

2. Draw Render Node Op Vector包含了一个Render Node的所有子Render Node,相当于包含了一个View的所有子View绘制命令,每一个子View绘制命令用一个Draw Render Node Op来描述。

3. Chunk Vector将一个Render Node的所有Display List Op和Draw Render Node Op划分成为Chunk来管理。一个Chunk通过一个begin op index和一个end op index来记录一组Display List Op,并且通过begin child index和end child index来记录一组Draw Render Node Op。

在渲染一个Render Node的时候,是按照Chunk Vector保存的Chunk顺序来渲染所有的Display List Op和Draw Render Node Op的。前面提到,Draw Render Node Op描述的是一个View的子View绘制命令。子View的Z轴坐标有可能是负的,这意味着子View要先于父View绘制。因此在渲染一个Chunk对应的Display List Op和Draw Render Node Op之前,需要对Draw Render Node Op按照其对应的子View的Z轴坐标由小到大进行排序。排序完成之后,先渲染Z轴坐标为负的Draw Render Node Op,接着再渲染Display List Op,最后渲染Z轴坐标为0或者正数的Draw Render Node Op。

从上面的分析就可以推断出,Chunk的存在意义就是将一个View自身的绘制命令及其子View绘制命令组织在一起。这样在渲染一个View的UI时,就可以很容易地处理子View的Z轴坐标为负数的情况。这同时也意味着在构建一个View的Display List的时候,记录的绘制命令有可能是乱序的。这就要求在渲染这些绘制命令的时候,需要对它们按照Z轴坐标进行排序。

有了上述的背景知识之后,我们再来看DisplayListRenderer类的成员函数addRenderNodeOp的实现,它在向成员变量mDisplayListData描述的一个Display List Data添加一个Draw Render Node Op时,需要操作该Display List Data里面的三个Vector。

其中,Display List Op和Chunk这两个Vector的操作是通过调用DisplayListRenderer类的成员函数addDrawOp来实现的,如下所示

size_t DisplayListRenderer::addDrawOp(DrawOp* op) {
        Rect localBounds;
        if (op->getLocalBounds(localBounds)) {
            bool rejected = quickRejectConservative(localBounds.left, localBounds.top,
                    localBounds.right, localBounds.bottom);
            op->setQuickRejected(rejected);
        }

        mDisplayListData->hasDrawOps = true;
        return flushAndAddOp(op);
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

DisplayListRenderer类的成员函数addDrawOp首先是判断要添加到Display List Data去的Draw Op是否设置了绘制区间。如果设置了,就再调用DisplayListRenderer类从父类StatefulBaseRenderer继承下来的成员函数quickRejectConservative判断该绘制区间是否不在当前正在处理的DisplayListRenderer对象的裁剪区间之内。如果不在,就将该Draw Op设置为Quick Rejected,也就是意味着该Draw Op无需要渲染。

DisplayListRenderer类的成员函数addDrawOp最后调用另外一个成员函数flushAndAddOp将参数op描述的DrawOp添加到成员变量mDisplayListData描述的一个Display List Data的Display List Op Vector中,并且修改对应的Chunk的信息,如下所示:

size_t DisplayListRenderer::flushAndAddOp(DisplayListOp* op) {
        flushRestoreToCount();
        flushTranslate();
        return addOpAndUpdateChunk(op);
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

DisplayListRenderer类的成员函数flushAndAddOp首先是调用成员函数flushRestoreToCount和flushTranslate之前是否对当前正在处理的DisplayListRenderer对象对应的Hardware Canvas调用过成员函数restoreToCount和translate。如果有,并且还没将它们转化为相应的Draw Op记录在Display List Data中,那么现在就是时候将它们转化为相应的Draw Op记录在Display List Data中了。

DisplayListRenderer类的成员函数flushAndAddOp最后调用另外一个成员函数addOpAndUpdateChunk来处理参数op描述的DisplayListOp,如下所示:

size_t DisplayListRenderer::addOpAndUpdateChunk(DisplayListOp* op) {
        int insertIndex = mDisplayListData->displayListOps.add(op);
        if (mDeferredBarrierType != kBarrier_None) {
            // op is first in new chunk
            mDisplayListData->chunks.push();
            DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop();
            newChunk.beginOpIndex = insertIndex;
            newChunk.endOpIndex = insertIndex + 1;
            newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);

            int nextChildIndex = mDisplayListData->children().size();
            newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
            mDeferredBarrierType = kBarrier_None;
        } else {
            // standard case - append to existing chunk
            mDisplayListData->chunks.editTop().endOpIndex = insertIndex + 1;
        }
        return insertIndex;
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

从这里就可以看到,DisplayListRenderer类的成员函数flushAndAddOp首先是将参数op描述的DisplayListOp添加到成员变量mDisplayListData描述的一个Display List Data的Display List Op Vector中,接下来再修改对应的Chunk的信息。

如果DisplayListRenderer类的成员变量mDeferredBarrierType的值不等于kBarrier_None,那么要将参数op描述的DisplayListOp记录在一个新的Chunk中。在这种情况下,DisplayListRenderer类的成员函数flushAndAddOp就会在成员变量mDisplayListData描述的一个Display List Data的Chunk Vector中增加一个新的Chunk,并且相应地修改该Chunk的成员变量beginOpIndex、endOpIndex、beginChildIndex、endChildIndex和reorderChildren。其中,Chunk类的成员变量beginOpIndex、endOpIndex、beginChildIndex和endChildIndex的含义可以参考图2所示的begin op index、end op index、begin child index和end child index。另外,Chunk类的成员变量reorderChildren表示一个Chunk包含的Draw Render Node Op在渲染的时候是否需要排序。

回忆前面分析的ThreadedRenderer类的成员函数updateRootDisplayList,它将应用程序窗口的Decor View的Display List绘制在Root Render Node的Hardware Canvas的前后,会分别调用该Hardware Canvas的成员函数insertReorderBarrier和insertInorderBarrier分别设置一个Reorder Barrier和一个Inorder Barrier。设置Reorder Barrier和Inorder Barrier就相当于将Hardware Canvas对应的DisplayListRenderer对象的成员变量mDeferredBarrierType设置为kBarrier_OutOfOrder和kBarrier_InOrder。

因此,我们就可以看出,Reorder Barrier和Inorder Barrier的设置是发生在将一个子View的Display List绘制在父View的Hardware Canvas的前后的,作用就是为了能够在父View对应的DisplayListRenderer对象按Chunk来管理绘制命令。同样的,如果我们去分析ViewGroup的Display List的构建过程,就会发现在将一个ViewGroup的子View的Display List绘制在该ViewGroup对应的Hardware Canvas的前后,也会分别在该Hardware Canvas上设置一个Reorder Barrier和一个Inorder Barrier。

最后,如果DisplayListRenderer类的成员变量mDeferredBarrierType的值等于kBarrier_None,那么就说明参数op描述的DisplayListOp与前面已经添加的DisplayListOp是位于同一个Chunk中的,因此这时候就不需要往成员变量mDisplayListData描述的一个Display List Data的Chunk Vector增中一个新的Chunk,而只需要修改保存在Chunk Vector的最后一个Chunk的成员变量endOpIndex即可。

这一步执行完成之后,回到DisplayListRenderer类的成员函数addRenderNodeOp中,这时候参数op描述DrawRenderNodeOp就添加到成员变量mDisplayListData描述的Display List Data的Display List Op Vector中去了,并且对应的Chunk信息也已经设置好,接下来要做的事情就是再将该DrawRenderNodeOp添加到成员变量mDisplayListData描述的Display List Data的Draw Render Node Op Vector中去。

本来DisplayListRenderer类的成员函数addRenderNodeOp执行到这里,就已经完成任务了。但是在Android 5.0中,增加了一个新的API----RippleDrawable。RippleDrawable有一个属性,当它没有包含任何的Layer时,它将被投影到当前视图的设置有Background的最近的一个父视图的Background去。这一点可以参考官方文档:http://developer.android.com/reference/android/graphics/drawable/RippleDrawable.html

为了达到上述目的,每一个Render Node都具有三个属性:Projection Receive Index、Projection Receiver和Projection Backwards。其中,Projection Receive Index是一个整型变量,而Projection Receiver和Projection Backwards是两个布尔变量。注意,在一个应用程序窗口的视图结构中,每一个View及其设置的Background都对应一个Render Node。上述三个属性构成了Render Node里面的一个Projection Nodes的概念,如图3所示:

图3 Projection Nodes

图3示意的是一个应用程序窗口的视图结构信息,其中:

1. View-1设置了Background Drawable-1,并且包含有View-2和View-3两个子View。

2. View-2没有设置Background Drawable,但是包含有View-4这个子View。

  1. View-4设置有Background Drawable-3,没有包含子View。

4. View-3设置有Background Drawable-2,并且包含有View-5这个子View。

5. View-5设置有Background Drawable-4,没有包含子View。

对于所有的Background Drawable,它对应的Render Node的Projection Receiver属性值均为true。如果一个Background Drawable同时还是一个没有包含任何Layer的Ripple Drawable,那么对应的Render Node的Projection Backwards属性值也为true;否则的话,对应的Render Node的Projection Backwards属性值就为false。

由于对于一个View来说,它的Background Drawable和子View对应的Render Node均是它对应的Render Node的子Render Node,但是又由于Ripple Drawable有存在,使得Background Drawable有特殊的含义,因此我们需要给Render Node增加一个Projection Receiver Index属性,当它的值大于等于0时,就表示该属性值描述的就是一个Background Drawable对应的Display List Op在一个Render Node的Display List Data中的Display List Op Vector的位置。反过来说,就是通过Projection Receiver Index属性,我们可以在一个Render Node的Display List Data中的Display List Op Vector中找到一个Display List Op,该Display List Op对应的就是一个Background Drawable的绘制命令。

从图3可以看到,Background Drawable-3和Background Drawable-4对应的Render Node的Projection Backwards属性值为true。这意味着它们将会投影到最近的设置有Background的最近的一个父视图的Background去。这意味着Background Drawable-3和Background Drawable-4会被分别投影到Background Drawable-1和Background Drawable-2去。这时候View-1和View-3的Projection Nodes就不为空,而是分别记录了Background Drawable-3和Background Drawable-4这两个Background对应的Render Node。

这样在渲染View-1和View-3时,通过它们的Projection Nodes,就可以知道Background Drawable-3和Background Drawable-4是要提前进行处理的,而不是等到View-4和View-5被渲染时才处理。

有了这个背景知识之后,回到DisplayListRenderer类的成员函数addRenderNodeOp中,我们就会看到,当参数op描述的DrawRenderNodeOp对应的Render Node的Projection Receiver属性为true时,就会将它在Display List Op Vector的位置记录在对应的DisplayListData对象的成员变量projectionReceiverIndex中。后面在渲染应用程序窗口的Display List的时候,就会用到这个Projection Receiver Index值。

这一步执行完成之后,回到Java层的ThreadedRenderer类的成员函数updateRootDisplayList中,这时候应用程序窗口的Decor View的Display List就绘制在Root Render Node对应的Hardware Canvas去了。

接下来,ThreadedRenderer类的成员函数updateRootDisplayList就从Root Render Node对应的Hardware Canvas获得Display List Data,并且将设置为Root Render Node的Display List Data。这是通过调用RenderNode类的成员函数end实现的,如下所示:

public class RenderNode {
        ......

        public void end(HardwareCanvas endCanvas) {
            ......

            GLES20RecordingCanvas canvas = (GLES20RecordingCanvas) endCanvas;
            ......
            long renderNodeData = canvas.finishRecording();
            nSetDisplayListData(mNativeRenderNode, renderNodeData);
            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/RenderNode.java中。

从前面的分析可以知道,参数endCanvas指向的实际上是一个GLES20RecordingCanvas对象,因此这里就直接将参数endCanvas描述的一个HardwareCanvas对象转化为一个GLES20RecordingCanvas对象,然后再调用该GLES20RecordingCanvas对象的成员函数finishRecording获得保存在它里面的Display List Data,最后调用ThreadedRenderer类的成员函数nSetDisplayListData将该Display List Data设置当前正在处理的Render Node的Display List Data。

接下来,我们首先分析GLES20RecordingCanvas类的成员函数finishRecording的实现,然后再分析ThreadedRenderer类的成员函数nSetDisplayListData的实现。

GLES20RecordingCanvas类的成员函数finishRecording的实现如下所示:

class GLES20RecordingCanvas extends GLES20Canvas {
        ......

        long finishRecording() {
            return nFinishRecording(mRenderer);
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/GLES20RecordingCanvas.java中。

GLES20RecordingCanvas类的成员函数finishRecording调用另外一个成员函数nFinishRecording获得保存在当前正在处理的GLES20RecordingCanvas对象中的Display List Data。

GLES20RecordingCanvas类的成员函数nFinishRecording是一个JNI函数,它是从父类GLES20Canvas继承下来的,由Native层的函数android_view_GLES20Canvas_finishRecording实现,如下所示:

static jlong android_view_GLES20Canvas_finishRecording(JNIEnv* env,
            jobject clazz, jlong rendererPtr) {
        DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
        return reinterpret_cast<jlong>(renderer->finishRecording());
    }

这个函数定义在文件frameworks/base/core/jni/android_view_GLES20Canvas.cpp中。

参数rendererPtr描述的是一个Native层的DisplayListRenderer对象,这个DisplayListRenderer对象是在GLES20Canvas类的构造函数中创建的。在我们这个情景中,这个DisplayListRenderer对象就是用来负责构建应用程序窗口的Root Render Node的Display List的。现在我们就通过调用这个DisplayListRenderer对象的成员函数finishRecording获得保存在它内部的Display List Data。

DisplayListRenderer类的成员函数finishRecording的实现如下所示:

DisplayListData* DisplayListRenderer::finishRecording() {
        ......
        DisplayListData* data = mDisplayListData;
        mDisplayListData = 0;
        return data;
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

DisplayListRenderer类的成员变量mDisplayListData描述的Display List Data已经记录了一系列的绘制命令。在我们这个情景中,记录的绘制命令就是一个DrawRenderNodeOp,并且这个被绘制的Render Node对应的View就是应用程序窗口的Decor View。

在将成员变量mDisplayListData描述的Display List Data返回给调用者之前,DisplayListRenderer类的成员函数finishRecording会将成员变量mDisplayListData的值设置为NULL,这样就相当于是将它内部的Display List Data的所有权转移给调用者。

从上面的分析过程就可以知道,上述的调用者即为Root Render Node,RenderNode类的成员函数end获得了它对应的Hardware Canvas的Display List Data之后,就将该Display List Data设置为自己的Display List Data,这是通过调用RenderNode类的成员函数nSetDisplayListData实现的。

RenderNode类的成员函数nSetDisplayListData是一个JNI函数,由Native层的函数android_view_RenderNode_setDisplayListData实现,如下所示:

static void android_view_RenderNode_setDisplayListData(JNIEnv* env,
            jobject clazz, jlong renderNodePtr, jlong newDataPtr) {
        RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
        DisplayListData* newData = reinterpret_cast<DisplayListData*>(newDataPtr);
        renderNode->setStagingDisplayList(newData);
    }

这个函数定义文件frameworks/base/core/jni/android_view_RenderNode.cpp中。

参数renderNodePtr描述的是一个Native层的Render Node。在我们这个情景中,这个Render Node即为应用程序窗口的Root Render Node。另外一个参数newDataPtr描述的是一个Native层的Display List Data。在我们这个情景中,这个Display List Data就是刚才我们从Root Render Node对应的Hardware Canvas获得的。这里将该Display List Data设置为Root Render Node的Display List Data。这是通过调用Native层的RenderNode类的成员函数setStagingDisplayList实现的。

Native层的RenderNode类的成员函数setStagingDisplayList的实现如下所示:

void RenderNode::setStagingDisplayList(DisplayListData* data) {
        mNeedsDisplayListDataSync = true;
        delete mStagingDisplayListData;
        mStagingDisplayListData = data;
        ......
    }

这个函数定义在文件frameworks/base/libs/hwui/RenderNode.cpp中。

RenderNode类的成员函数setStagingDisplayList将参数data描述的Display List Data保存在成员变量mStagingDisplayListData中,并且会将另外一个成员变量mNeedsDisplayListDataSync的值设置为true,表示当前正在处理的Render Node有了一个新的Display List Data,这意味着在绘制应用程序窗口的下一帧时,需执行该新的Display List Data中的绘制命令。

这一步执行完成之后,应用程序窗口的Decor View的Display List就记录在Root Render Node的Display List中了。接下来我们还需要继续分析应用程序窗口的Decor View的Display List的构建过程。前面在分析ThreadedRenderer类的成员函数updateRootDisplayList时提到,应用程序窗口的Decor View的Display List是通过调用ThreadedRenderer类的成员函数updateViewTreeDisplayList构建的。

ThreadedRenderer类的成员函数updateViewTreeDisplayList的实现如下所示:

public class ThreadedRenderer extends HardwareRenderer {
        ......

        private void updateViewTreeDisplayList(View view) {
            ......
            view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                    == View.PFLAG_INVALIDATED;
            view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
            view.getDisplayList();
            view.mRecreateDisplayList = false;
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。

参数view描述的View即为应用程序窗口的Decor View,当它的成员变量mPrivateFlags的值的PFLAG_INVALIDATED位被设置为1时,就表示该Decor View的Display List是需要重新构建的。这时候就会将它的成员变量mRecreateDisplayList设置为true,以便接下来调用该Decor View的成员函数getDisplayList时,可以通过成员变量mRecreateDisplayList快速判断出需要重新构建Display List。

Decor View的成员函数getDisplayList是从父类View继承下来的,因此接下来我们就继续分析View类的成员函数getDisplayList的实现,如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
        ......

        public RenderNode getDisplayList() {
            updateDisplayListIfDirty();
            return mRenderNode;
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

View类的成员函数getDisplayList调用另外一个成员函数updateDisplayListIfDirty判断是否需要为当前处理的View重新构建Display List。如果需要的话,就进行重新构建;否则的话,就复用上次构建的Display List。这个Display List就保存在View类的成员变量mRenderNode描述的一个Render Node中。因此,将该Render Node返回给调用者,调用者就可以获得对应的Display List。

View类的成员函数updateDisplayListIfDirty的实现如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
        ......

        private void 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) {
                    ......
                    dispatchGetDisplayList();

                    return; // no work needed
                }

                ......

                int width = mRight - mLeft;
                int height = mBottom - mTop;
                int layerType = getLayerType();

                final HardwareCanvas canvas = renderNode.start(width, height); 
                .......

                try {
                    final HardwareLayer layer = getHardwareLayer();
                    if (layer != null && layer.isValid()) {
                        canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
                    } else if (layerType == LAYER_TYPE_SOFTWARE) {
                        buildDrawingCache(true);
                        Bitmap cache = getDrawingCache(true);
                        if (cache != null) {
                            canvas.drawBitmap(cache, 0, 0, mLayerPaint);
                        }
                    } else {
                        ......

                        // Fast path for layouts with no backgrounds
                        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                            dispatchDraw(canvas);
                            ......
                        } else {
                            draw(canvas);
                        }
                        .......
                    }
                } finally {
                    renderNode.end(canvas);
                    .....
                }
            }

            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

在三种情况下,View类的成员函数updateDisplayListIfDirty需要重新构建当前正在处理的View或者其子View的Display List:

1. View类的成员变量mPrivateFlags的值的PFLAG_DRAWING_CACHE_VALID位等于0,这表明上次构建的Display List已经失效。

2. View类的成员变量mRenderNode描述的Render Node内部维护的Display List Data还没有设置或者已经被销毁,这种情况调用该Render Node的成员函数isValid的返回值为false。

  1. View类的成员变量mRecreateDisplayList的值等于true,这直接表明需要重新构建Display List。

其中,如果View类的成员变量mPrivateFlags的值的PFLAG_DRAWING_CACHE_VALID位不等于0,并且成员变量mRenderNode描述的Render Node内部维护的Display List Data也是有效的,那么就表明上次为当前正在处理的View的UI没有发生变化。但是如果在这种情况下,View类的成员变量mRecreateDisplayList等于false,就说明虽然当前正在处理的View的UI没有发生变化,但是它的子View的UI发生了变化。这时候就需要对这些子View的Display List进行重新构建,并且更新到当前正在处理的View的Display List去。这是通过调用View类的成员函数dispatchGetDisplayList来完成的。

除了上述这种情况,其余情况均表明需要重新构建当前正在处理的View及其子View的Display List。这些Display List的构建过程如下所示:

1. 从当前正在处理的View关联的Render Node获得一个Hardware Canvas。

2. 将当前正在处理的View及其子View的UI绘制命令记录在上面获得的Hardware Canvas中。

3. 将前面已经绘制好的Hardware Canvas的Display List Data提取出来,并且设置为当前正在处理的View关联的Render Node里面去。

其中,第1步和第3步是通过调用RenderNode类的成员函数start和end完成的。这两个成员函数我们在前面已经分析过了,因此接下来我们主要分析第2步的实现。

当前正在处理的View可能是:

1. TextureView。这时候调用View类的成员函数getHardwareLayer将会获得一个HardwareLayer,这个HardwareLayer通过Open GL纹理的方式绘制TextureView的UI。这样直接将该HardwareLayer记录在当前正在处理的TextureView关联的Render Node对应的Hardware Canvas即可。

2. 使用软件渲染的普通View。这时候调用View类的成员函数getLayerType获得的Layer Type为LAYER_TYPE_SOFTWARE。这时候首先调用View类的成员函数buildDrawingCache将当前正在处理的View的UI绘制在一个Bitmap上,接着再调用View类的成员函数getDrawingCache获得该Bitmap,最后再将该Bitmap记录在当前正在处理的View关联的Render Node对应的Hardware Canvas上即可。

3. 使用硬件渲染的普通View。这时候当前正在处理的View的成员变量mPrivateFlags的值的PFLAG_SKIP_DRAW位设置为1,就表明当前正在处理的View是一个Layout,并且没有设置Background,这时候就可以走一个捷径,即直接调用View类的成员函数dispatchDraw将该Layout的子View的UI绘制在当前正在处理的View关联的Render Node对应的Hardware Canvas即可。另一方面,如果当前正在处理的View的成员变量mPrivateFlags的值的PFLAG_SKIP_DRAW位设置为0,那么就不能走捷径,而是走一个慢路径,规规矩矩地调用View 类的成员函数draw将当前正在处理的View及其可能存在的子View的UI绘制在关联的Render Node对应的Hardware Canvas上。

接下来我们分别分析TextureView、使用软件渲染的View和使用硬件渲染的View的UI绘制过程,也就是它们的Display List的构建过程。

TextureView的UI是通过一个HardwareLayer来描述的,该HardwareLayer可以通过TextureView类的成员函数getHardwareLayer获得,如下所示:

public class TextureView extends View {
        ......

        @Override
        HardwareLayer getHardwareLayer() {
            ......

            if (mLayer == null) {
                ......

                mLayer = mAttachInfo.mHardwareRenderer.createTextureLayer();
                ......

                if (!mUpdateSurface) {
                    // Create a new SurfaceTexture for the layer.
                    mSurface = new SurfaceTexture(false);
                    mLayer.setSurfaceTexture(mSurface);
                }

                ......

                mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
                ......
            }

            if (mUpdateSurface) {
                ......
                mUpdateSurface = false;

                ......
                updateLayer();
                ......

                mLayer.setSurfaceTexture(mSurface);
                ......
            }

            applyUpdate();
            applyTransformMatrix();

            return mLayer;
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/TextureView.java中。

TextureView类的成员变量mLayer描述的是一个HardwareLayer,这个HardwareLayer是通过调用成员变量mAttachInfo描述的一个AttachInfo对象的成员变量mHardwareRenderer指向的一个ThreadedRenderer对象的成员函数createTextureLayer创建的,如下所示:

public class ThreadedRenderer extends HardwareRenderer {
        ......

        @Override
        HardwareLayer createTextureLayer() {
            long layer = nCreateTextureLayer(mNativeProxy);
            return HardwareLayer.adoptTextureLayer(this, layer);
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。

ThreadedRenderer类的成员函数createTextureLayer首先是调用另外一个成员函数nCreateTextureLayer在Native层创建一个Texture Layer,然后通过调用HardwareLayer类的静态成员函数adoptTextureLayer将该Native层的Texture Layer封装成一个Java层的Hardware Layer。

ThreadedRenderer类的成员函数nCreateTextureLayer是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_createTextureLayer实现,如下所示:

static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz,
            jlong proxyPtr) {
        RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
        DeferredLayerUpdater* layer = proxy->createTextureLayer();
        return reinterpret_cast<jlong>(layer);
    }

这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。

参数proxyPtr描述的是一个Native层的RenderProxy对象。从前面Android应用程序UI硬件加速渲染环境初始化过程分析一文可以知道, 应用程序进程的Main Thread通过该RenderProxy对象可以与Render Thread通信。这里通过调用参数proxyPtr描述的RenderProxy对象的成员函数createTextureLayer请求创建一个DeferredLayerUpdater对象。

RenderProxy对象的成员函数createTextureLayer的实现如下所示:

CREATE_BRIDGE2(createTextureLayer, RenderThread* thread, CanvasContext* context) {
        Layer* layer = args->context->createTextureLayer();
        if (!layer) return 0;
        return new DeferredLayerUpdater(*args->thread, layer);
    }

    DeferredLayerUpdater* RenderProxy::createTextureLayer() {
        SETUP_TASK(createTextureLayer);
        args->context = mContext;
        args->thread = &mRenderThread;
        void* retval = postAndWait(task);
        DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
        return layer;
    }

这个函数定义在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。

RenderProxy类的成员函数createTextureLayer创建了一个Task,这个Task被发送到Render Thread去执行,执行的时候调用的就是由宏CREATE_BRIDGE2定义的函数createTextureLayer。

宏CREATE_BRIDGE2定义的函数createTextureLayer首先是通过参数context描述的一个CanvasContext对象的成员函数createTextureLayer创建一个Layer对象,接着再将该Layer对象封装成一个DeferredLayerUpdater对象返回给调用者。

CanvasContext类的成员函数createTextureLayer的实现如下所示:

Layer* CanvasContext::createTextureLayer() {
        ......
        return LayerRenderer::createTextureLayer(mRenderThread.renderState());
    }

这个函数定义在文件frameworks/base/libs/hwui/renderthread/CanvasContext.cpp中。

CanvasContext类的成员函数createTextureLayer调用LayerRenderer类的静态成员函数createTextureLayer创建一个Layer对象,如下所示:

Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
        ......

        Layer* layer = new Layer(Layer::kType_Texture, renderState, 0, 0);
        ......

        Caches::getInstance().activeTexture(0);
        layer->generateTexture();

        return layer;
    }

这个函数定义在文件frameworks/base/libs/hwui/LayerRenderer.cpp中。

LayerRenderer类的静态成员函数createTextureLayer首先是创建一个Layer对象,然后再调用该Layer对象的成员函数generateTexture生成一个Open GL纹理。从这里就可以看到,TextureView是通过Open GL纹理来实现的。

这一步执行完成之后,就可以得到一个Texture Layer了。回到TextureView类的成员函数getHardwareLayer中,接下来是判断成员变量mUpdateSurface的值是否等于false。如果等于false,就需要创建一个SurfaceTexture,并且这个SurfaceTexture会被设置到前面创建的Texture Layer中去。这样以后Texture Layer就可以通过该SurfaceTexture来获得Open GL纹理的内容。

当TextureView类的成员变量mUpdateSurface的值等于false时,实际上是表明由TextureView提供一个SurfaceTexture。这个SurfaceTexture可以通过TextureView类的成员函数getSurfaceTexture获得。TextureView的使用者获得了这个SurfaceTexture之后,就可以向TextureView提供Open GL纹理的内容了。这是一种Producer-Consumer工作模式,TextureView的使用者是Producer,而TextureView是Consumer。

当TextureView的使用者通过SurfaceTexture向TextureView提供了内容的时候,TextureView可以通过其成员变量mUpdateListener指向的一个OnFrameAvailableListener对象的成员函数onFrameAvailable获得通知,如下所示:

public class TextureView extends View {
        ......

        private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
                new SurfaceTexture.OnFrameAvailableListener() {
            @Override
            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                updateLayer();
                invalidate();
            }
        };

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/TextureView.java中。

当TextureView获得了新内容更新通知之后,一方面是调用TextureView类的成员函数updateLayer来标记Open GL纹理需要更新,另一方面是调用从父类继承下来的成员函数invalidate通知Main Thread需要重绘TextureView的UI。

TextureView类的成员函数updateLayer的实现如下所示:

public class TextureView extends View {
        ......

        private void updateLayer() {
            synchronized (mLock) {
                mUpdateLayer = true;
            }
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/TextureView.java中。

TextureView类的成员函数updateLayer只是简单地将成员变量mUpdateLayer的值设置为true,这将会导致每次TextureView类的成员函数getHardwareLayer被调用时,与当前正在处理的TextureView在Native层对应的DeferredLayerUpdater对象被标记为需要更新对应的Open GL纹理。

再回到TextureView类的成员函数getHardwareLayer中,当成员变量mUpdateSurface的值等于true时,这表明TextureView当前使用的SurfaceTexture是由TextureView的使用者提供的,并且该SurfaceTexture已经提供了新的内容,这时候就会直接调用前面分析的成员函数updateLayer设置更新标记,并且将该SurfaceTexture设置到成员变量mLayer描述的一个HardwareLayer中去。

TextureView类的成员函数getHardwareLayer最后调用了另外两个成员函数applyUpdate和applyTransformMatrix来更新TextureView的内容和属性,这里我们主要分析TextureView类的成员函数applyUpdate的实现,如下所示:

public class TextureView extends View {
        ......

        private void applyUpdate() {
            ......

            synchronized (mLock) {
                if (mUpdateLayer) {
                    mUpdateLayer = false;
                } else {
                    return;
                }
            }

            mLayer.prepare(getWidth(), getHeight(), mOpaque);
            mLayer.updateSurfaceTexture();

            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/TextureView.java中。

从这里就可以看到,只有当成员变量mUpdateLayer的值等于true的情况下,TextureView类的成员函数applyUpdate才会调用成员变量mLayer指向的一个HardwareLayer对象的成员函数prepare和updateSurfaceTexture来更新对应的Open GL纹理的大小、透明度和内容等分析。

这里我们主要分析TextureView的Open GL纹理内容的更新过程,即HardwareLayer类的成员函数updateSurfaceTexture的实现,如下所示:

final class HardwareLayer {
        ......

        public void updateSurfaceTexture() {
            nUpdateSurfaceTexture(mFinalizer.get());
            mRenderer.pushLayerUpdate(this);
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/HardwareLayer.java中。

HardwareLayer类的成员函数updateSurfaceTexture首先调用另外一个成员函数nUpdateSurfaceTexture通知Native层当前正在处理的HardwareLayer的内容有了更新,接着再调用成员变量mRenderer指向的一个ThreadedRenderer对象的成员函数pushLayerUpdate标记当前正在处理的HardwareLayer放在一个内部的更新列表中。

HardwareLayer类的成员函数nUpdateSurfaceTexture是一个JNI函数,由Native层的函数

static void android_view_HardwareLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz,
            jlong layerUpdaterPtr) {
        DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
        layer->updateTexImage();
    }

这个函数定义在文件frameworks/base/core/jni/android_view_HardwareLayer.cpp中。

参数layerUpdatePtr指向的是前面分析的由宏CREATE_BRIDGE2定义的函数createTextureLayer创建的一个DeferredLayerUpdater对象,这里调用它的成员函数updateTexImage更新当前正在处理的TextureView的Open GL纹理。

DeferredLayerUpdater类的成员函数updateTexImage的实现如下所示:

class DeferredLayerUpdater : public VirtualLightRefBase {
    public:
        ......

        ANDROID_API void updateTexImage() {
            mUpdateTexImage = true;
        }

        ......
    };

这个函数定义在文件frameworks/base/libs/hwui/DeferredLayerUpdater.h中。

从这里可以看到,DeferredLayerUpdater类的成员函数updateTexImage并没有真正去更新当前正在处理的TextureView的Open GL纹理,而只是将DeferredLayerUpdater类的成员变量mUpdateTexImage设置为true,用来表示当前正在处理的TextureView的Open GL纹理需要进行更新。之所以要这样做,是因为纹理的更新要在Render Thread进行,而现在是在Main Thread执行。等到后面应用程序窗口的Display List被渲染时,TextureView的Open GL纹理才会被真正的更新。

这一步执行完成之后,回到前面HardwareLayer类的成员函数updateSurfaceTexture中,接下来它调用成员变量mRenderer指向的一个ThreadedRenderer对象的成员函数pushLayerUpdate将当前正在处理的HardwareLayer保存在内部的一个待更新列表中,它的实现如下所示:

public class ThreadedRenderer extends HardwareRenderer {
        ......

        @Override
        void pushLayerUpdate(HardwareLayer layer) {
            nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。

ThreadedRenderer类的成员函数pushLayerUpdate调用另外一个成员函数nPushLayerUpdate将参数layer描述的一个HardwareLayer保存在Native层的一个待更新列表中。

ThreadedRenderer类的成员函数nPushLayerUpdate是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_pushLayerUpdate实现,如下所示:

static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz,
            jlong proxyPtr, jlong layerPtr) {
        RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
        DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
        proxy->pushLayerUpdate(layer);
    }

这个函数定义在文件frameworks/base/core/jni/android_view_ThreadedRenderer.cpp中。

参数proxyPtr和layerPtr指向的分别是Native层的一个RenderProxy对象和一个DeferredLayerUpdater对象,这里通过调用RenderProxy对象的成员函数pushLayerUpdate将DeferredLayerUpdater对象保存在一个待更新列表中。

RenderProxy类的成员函数pushLayerUpdate的实现如下所示:

void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
        mDrawFrameTask.pushLayerUpdate(layer);
    }

这个函数定义在文件frameworks/base/libs/hwui/renderthread/RenderProxy.cpp中。

从前面Android应用程序UI硬件加速渲染环境初始化过程分析一文可以知道,RenderProxy类的成员变量mDrawFrameTask描述的是一个DrawFrameTask对象,这个DrawFrameTask对象用来执行渲染应用程序窗口UI的任务,RenderProxy类的成员函数pushLayerUpdate就调用它的成员函数pushLayerUpdate将参数layer描述的DeferredLayerUpdater对象交给它处理。

DrawFrameTask类的成员函数pushLayerUpdate的实现如下所示:

void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
        ......

        for (size_t i = 0; i < mLayers.size(); i++) {
            if (mLayers[i].get() == layer) {
                return;
            }
        }
        mLayers.push_back(layer);
    }

这个函数定义在文件frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp中。

DrawFrameTask类的成员函数pushLayerUpdate的实现很简单,它只是将参数layer描述的DeferredLayerUpdater对象添加到成员变量mLayers描述的一个列表中。在添加之前,会检查列表中是否已经存在要添加的DeferredLayerUpdater对象。保存在这个列表中的DeferredLayerUpdater对象在渲染应用程序窗口的Display List的时候就会被处理,这个过程我们在接下来一篇文章分析应用程序窗口的Display List的渲染过程时再分析。

这一步执行完成之后,用来描述TextureView的UI的一个HardwareLayer对象就准备完毕,回到View类的成员函数updateDisplayListIfDirty中,接下来要做的事情就是将上述HardwareLayer对象绘制在当前正在处理的View对象关联的Render Node对象对应的Hardware Canvas中,这是通过调用该Hardware Canvas的成员函数drawHardwareLayer实现的。

从前面分析的过程可以知道,当前正在处理的View对象关联的Render Node对象对应的Hardware Canvas实际上是一个GLES20RecordingCanvas对象,而GLES20RecordingCanvas类的成员函数drawHardwareLayer是从其父类GLES20Canvas继承下来的,因此接下来我们就继续分析GLES20Canvas类的成员函数drawHardwareLayer的实现。

GLES20Canvas类的成员函数drawHardwareLayer的实现如下所示:

class GLES20Canvas extends HardwareCanvas {
        ......

        void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
            ......
            nDrawLayer(mRenderer, layer.getLayer(), x, y);
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/GLES20Canvas.java中。

GLES20Canvas类的成员函数drawHardwareLayer通过调用另外一个成员函数nDrawlayer将参数layer描述的一个Hardware Layer绘制当前正在处理的一个GLES20 Canvas中。

GLES20Canvas类的成员函数nDrawlayer是一个JNI函数,由Native层的函数android_view_GLES20Canvas_drawLayer实现,如下所示:

static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
            jlong rendererPtr, jlong layerPtr, jfloat x, jfloat y) {
        DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
        Layer* layer = reinterpret_cast<Layer*>(layerPtr);
        renderer->drawLayer(layer, x, y);
    }

这个函数定义在文件frameworks/base/core/jni/android_view_GLES20Canvas.cpp中。

参数rendererPtr指向的是一个DisplayListRenderer对象。这个DisplayListRenderer对象是和当前正在处理的View对象关联,它负责构建当前正处理的View的Display List。这里要做的事情就是将参数layerPtr描述的一个Layer对象记录在当前正处理的View的Display List中,这是通过调用DisplayListRenderer类的成员函数drawLayer实现的。

DisplayListRenderer类的成员函数drawLayer的实现如下所示:

status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) {
        layer = refLayer(layer);
        addDrawOp(new (alloc()) DrawLayerOp(layer, x, y));
        return DrawGlInfo::kStatusDone;
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

DisplayListRenderer类的成员函数drawLayer首先是调用另外一个成员函数refLayer将参数layer描述的一个Layer对象保存内部维护的一个Display List Data的一个Layer列表中,并且增加该Layer对象的引用计数,以便接下来将该Layer对象封装成一个Draw Layer Op,并且调用我们前面已经分析过的成员函数addDrawOp该Draw Layer Op记录在Display List Data中。

DisplayListRenderer类的成员函数refLayer的实现如下所示:

class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
        ......

        inline Layer* refLayer(Layer* layer) {
            mDisplayListData->layers.add(layer);
            mCaches.resourceCache.incrementRefcount(layer);
            return layer;
        }

        ......
    };

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.h中。

从这里就可以看到,DisplayListRenderer内部维护的一个Display List Data有一个成员变量layers,它指向的是一个列表,用来保存那些以Layer形式绘制的视图。同时从这里也可以看到,增加Layer对象的引用计数是通过调用成员变量mCaches指向的一个Caches对象的成员变量resourceCache描述的一个ResourceCache对象的成员函数incrementRefcount完成的。

这一步执行完成之后,TextureView的Display List就构建完毕,这个过程实际上就是将一个DrawLayerOp记录在TextureView的Display List中,而该DrawLayerOp封装了一个Layer对象,该Layer对象通过Open Gl纹理描述了TextureView的UI。

回到View类的成员函数updateDisplayListIfDirty中,接下来我们继续分析使用软件渲染的View的Display List的构建过程,即View类的成员函数buildDrawingCache的实现,如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
        ......

        public void buildDrawingCache(boolean autoScale) {
            if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                    mDrawingCache == null : mUnscaledDrawingCache == null)) {
                ......

                int width = mRight - mLeft;
                int height = mBottom - mTop;

                final AttachInfo attachInfo = mAttachInfo;
                ......

                Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
                ......

                if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
                    ......

                    try {
                        bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
                                width, height, quality);
                        ......
                        if (autoScale) {
                            mDrawingCache = bitmap;
                        } else {
                            mUnscaledDrawingCache = bitmap;
                        }
                        ......
                    } catch (OutOfMemoryError e) {
                        ......
                    }

                    ......
                }

                Canvas canvas;
                if (attachInfo != null) {
                    canvas = attachInfo.mCanvas;
                    if (canvas == null) {
                        canvas = new Canvas();
                    }
                    canvas.setBitmap(bitmap);
                    ......
                } else {
                    ......
                    canvas = new Canvas(bitmap);
                }

                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    ......
                    dispatchDraw(canvas);
                    ......
                } else {
                    draw(canvas);
                }

                ......

                if (attachInfo != null) {
                    // Restore the cached Canvas for our siblings
                    attachInfo.mCanvas = canvas;
                }
            }
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

前面在分析View类的成员函数updateDisplayListIfDirty的时候提到,当View类的成员mPrivateFlags的值的PFLAG_DRAWING_CACHE_VALID位等于0时,表明当前正处理的View的UI需要更新。

对于使用软件渲染的View来说,它的UI是渲染在一个Bitmap中的。View类有两个成员变量mDrawingCache和mUnscaledDrawingCache,它们指向的都是一个Bitmap,这些Bitmap保存的就是当前正在处理的View上次绘制的UI。当参数autoScale等于true和false时,当前正在处理的View的UI分别绘制在成员变量mDrawingCache和mUnscaledDrawingCache指向的Bitmap中。

使用软件渲染的View使用成员变量mDrawingCache和mUnscaledDrawingCache来缓存上次绘制的UI的目的,就类似于使用硬件渲染的View使用Display List来缓存上次绘制的UI是一样的,都是为了在一个View的UI不需要更新的时候能够快速地完成整个应用程序窗口UI的绘制。因此,当View使用成员变量mDrawingCache或者mUnscaledDrawingCache的值等于null时,就表明当前正在处理的View的UI还没有绘制过,因此现在就需要对它进行绘制。

在绘制一个View之前,需要有一个Canvas。对于使用软件渲染的View来说,它使用的Canvas是构建在成员变量mDrawingCache或者mUnscaledDrawingCache指向的一个Bitmap之上。因此,如果View类的成员变量mDrawingCache或者mUnscaledDrawingCache的值为null,就需要调用Bitmap类的静态成员函数createBitmap创建一块新的Bitmap中,并且相应地保存在成员变量mDrawingCache或者mUnscaledDrawingCache中。此外,当上次缓存的Bitmap的大小与当前正在处理的View的大小不一致时,也需要重新创建一块Bitmap。

有了Bitmap之后,就可以创建一个Canvas了。为了避免每次绘制软件渲染的View时,都为它创建一个Canvas,View类的成员函数buildDrawingCache会将第一次创建的Canvas保存在成员变量mAttachInfo描述的一个AttachInfo对象的成员变量mCanvas中。这样,以后只要将前面获得的一个Bitmap设置为缓存的Canvas的底层存储就可以了。这是通过调用Canvas类的成员函数setBitmap完成的。

有了Canvas之后,就可以调用View类的成员函数dispatchDraw或者draw绘制当前正在处理的View的UI了,这个逻辑与我们在前面分析View类的成员函数updateDisplayListIfDirty提到的使用硬件渲染的View的Display List的构建逻辑是一样的,区别只在于两者使用的Canvas不一样。

后面我们分析使用硬件渲染的View的Display List的构建过程时,再分析View类的成员函数draw的实现。至于View类的成员函数dispatchDraw的实现,它是成员函数draw的实现的一部分。因此,我们只分析后者的实现。

这一步执行完成之后,使用软件渲染的View的UI就绘制在View类的成员变量mDrawingCache或者mUnscaledDrawingCache指向的Bitmap上了。回到前面分析的View类的成员函数updateDisplayListIfDirty中,我们可以通过调用View类的成员函数getDrawingCache获得对应的Bitmap。获得了对应的Bitmap之后,就可以将它绘制当前正在处理的View关联的Render Node对应的Hardware Canvas之上。这是通过调用HardwareCanvas类的成员函数drawBitmap实现的。

前面提到,当前正在处理的View关联的Render Node对应的Hardware Canvas的实际类型为GLES20RecordingCanvas,它的成员函数drawBitmap是从父类GLES20Canvas继承下来的。

GLES20Canvas类的成员函数drawBitmap的实现如下所示:

class GLES20Canvas extends HardwareCanvas {
        ......

        @Override
        public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
            ......
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/GLES20Canvas.java中。

GLES20Canvas类的成员函数drawBitmap调用另外一个成员函数nDrawBitmap将参数bitmap描述的Bitmap绘制当前正在处理的GLES20Canvas上。

GLES20Canvas类的成员函数nDrawBitmap是一个JNI函数,由Native层的函数android_view_GLES20Canvas_drawBitmap实现,如下所示:

static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
            jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
            jfloat left, jfloat top, jlong paintPtr) {
        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
        ......

        DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
        Paint* paint = reinterpret_cast(paintPtr);

        // apply transform directly to canvas, so it affects shaders correctly
        renderer->save(SkCanvas::kMatrix_SaveFlag);
        renderer->translate(left, top);
        renderer->drawBitmap(bitmap, paint);
        renderer->restore();
    }

这个函数定义在文件frameworks/base/core/jni/android_view_GLES20Canvas.cpp中。

参数bitmapPtr描述的是Java层的Bitmap对象在Native层对应的一个SkBitmap对象,另外一个参数rendererPtr描述的是一个DisplayListRenderer对象,该DisplayListRenderer对象是用来构建一个View的Display List的,这里主要就是通过调用DisplayListRenderer类的成员函数drawBitmap来绘制参数bitmapPtr的Bitmap,从而获得一个包含了一个Bitmap绘制命令的Display List。

DisplayListRenderer类的成员函数drawBitmap的实现如下所示:

status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
        bitmap = refBitmap(bitmap);
        paint = refPaint(paint);

        addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
        return DrawGlInfo::kStatusDone;
    }

这个函数定义在文件frameworks/base/libs/hwui/DisplayListRenderer.cpp中。

DisplayListRenderer类的成员函数drawBitmap将参数bitmap和paint描述的一个SkBitmap和一个SkPaint封装成一个DrawBitmapOp,然后再调用我们前面分析过的DisplayListRenderer类的成员函数addDrawOp将该DrawBitmapOp添加在DisplayListRenderer类内部维护的一个Display List Data中。

这一步执行完成后,使用软件渲染的View的UI就通过Bitmap的形式记录在与其关联的Render Node的Display List中了。

回到View类的成员函数updateDisplayListIfDirty中,我们最后分析使用硬件渲染的View的Display List的构建过程,这是通过调用View类的成员函数draw完成的,它的实现如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
        ......

        public void draw(Canvas canvas) {
            ......

            // Step 1, draw the background, if needed
            int saveCount;

            if (!dirtyOpaque) {
                drawBackground(canvas);
            }

            ......

            // Step 2, save the canvas' layers
            ......

            // 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
            ......

            // Step 6, draw decorations (scrollbars)
            onDrawScrollBars(canvas);

            ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

View类的成员函数draw的详细实现分析可以参考前面Android应用程序窗口(Activity)的测量(Measure)、布局(Layout)和绘制(Draw)过程分析一文,这里我们只简单总结两点:

1. 一个View的主要UI是由子类实现的成员函数onDraw绘制的。这个成员函数通过参数canvas可以获得一个Canvas,然后就调用这个Canvas提供的API就可以绘制一个View的UI。这意味着对一个View来说,当它的成员函数onDraw被调用时,它是不需要区别它是通过硬件渲染还是软件渲染的。但是从结果来看,当使用硬件渲染时,调用Canvas API相当是将API调用记录在一个Display List中,而当使用软件渲染时,调用Canvas API相当是将UI绘制在一个Bitmap中。

2. 如果当前正在处理的View是一个View Group,那么它的子View是通过View类的成员函数dispatchDraw来递归绘制的。每一个子View被绘制时,都会先通过我们前面分析的成员函数getDisplayList获得一个Display List,然后再将这个Display List通过同样是我们在前面分析过的RenderNode类的成员函数drawRenderNode绘制在父View关联的一个Render Node对应的Hardware Canvas上,这相当于是将子View的Display List记录在父View的Display List上。

此外,对于使用硬件渲染的View来说,它的Background也是抽象为一个Render Node绘制在宿主View关联的一个Render Node对应的Hardware Canvas上的,相当于是将Background看作是一个View的子View。为了更好地理解这一点,接下来我们继续分析一个View的Background的绘制过程,即分析View类的成员函数drawBackground的实现,如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
        ......

        private void drawBackground(Canvas canvas) {
            final Drawable background = mBackground;
            ......

            if (canvas.isHardwareAccelerated() && mAttachInfo != null
                    && mAttachInfo.mHardwareRenderer != null) {
                mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);

                final RenderNode displayList = mBackgroundRenderNode;
                if (displayList != null && displayList.isValid()) {
                    ......
                    ((HardwareCanvas) canvas).drawRenderNode(displayList);
                    return;
                }
            }

            ......
        }

        ......
    }     

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

如果当前正在处理的View是使用硬件加速的,那么参数canvas指向的就是一个与当前正在处理的View关联的Render Node对应的Hardware Canvas。在这种情况下,View类的成员函数drawBackground首先是调用另外一个成员函数getDrawableRenderNode获得当前正在处理的View的Background对应的一个Render Node,然后再将该Render Node绘制在参数canvas描述的一个Hardware Canvas上,这是通过调用我们前面分析过的HardwareCanvas类的成员函数drawRenderNode实现的。

接下来我们主要分析View类的成员函数getDrawableRenderNode的实现,以便可以了解如果获得与一个View的Background关联的Render Node,如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
        ......

        private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
            if (renderNode == null) {
                renderNode = RenderNode.create(drawable.getClass().getName(), this);
            }

            final Rect bounds = drawable.getBounds();
            final int width = bounds.width();
            final int height = bounds.height();
            final HardwareCanvas canvas = renderNode.start(width, height);
            try {
                drawable.draw(canvas);
            } finally {
                renderNode.end(canvas);
            }

            // Set up drawable properties that are view-independent.
            renderNode.setLeftTopRightBottom(bounds.left, bounds.top, bounds.right, bounds.bottom);
            renderNode.setProjectBackwards(drawable.isProjected());
            renderNode.setProjectionReceiver(true);
            renderNode.setClipToBounds(false);
            return renderNode;
        }

        ......
    } 

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

参数drawable描述的就是View的Background使用的一个Drawable对象,另外一个参数renderNode指向的是保存在View类的成员变量mBackgroundRenderNode中的一个RenderNode对象。

如果View类的成员变量mBackgroundRenderNode的值为null,那么就需要调用RenderNode类的静态成员函数create创建一个新的Render Node。这个Render Node就是与View的Background关联的Render Node。

有了关联的Render Node之后,一个Background的Display List的构建过程就与一个View的Display List的构建过程是一样的,分三步进行:

1. 调用关联的Render Node的成员函数start获得一个Hardware Canvas。

2. 调用前面获得的Hardware Canvas提供的API绘制Background的UI。Background的UI是通过一个Drawable对象描述的,因此这里需要做的就是将Drawable绘制在前面获得的Hardware Canvas上,这是通过调用Drawable类的成员函数draw实现的。

  1. 调用关联的Render Node的成员函数end将前面已经绘制好的Hardware Canvas的Display List提取出来,并且作为关联的Render Node的Display List。

此外,与Background的关联的Render Node有两个特别的属性,就是我们在图3中提到的Projection Backwards和Projection Receiver。从这里就可以看到:

1. 所有与Background的关联的Render Node的Projection Receiver属性值均为true,这意味着它们均可以接收其它Render Node的投影。

  1. 与Background的关联的Render Node的Projection Receiver属性值与用来描述Background的Drawable对象的成员函数isProjected是一样的。当一个Drawable是一个Ripple Drawable,并且这个Ripple Drawable不包含有任何的Layer时,它的成员函数isProjected就为true。这意味着该Ripple Drawable需要投影在最近的一个设置了Background的父View的Background上进行渲染。

这样,一个应用程序窗口的Display List就构建完成了。这个构建完成的Display List对应的就是应用程序窗口的Root Render Node的Display List,并且这个Display List通过递归的方式包含了所有子View的Display List。这样,最后我们通过渲染应用程序窗口的Root Render Node的Display List,就可以获得整个应用程序窗口的UI。

前面我们提到了Display List具有的一个很好的特性,即如果一个View只是一些简单的属性发生变化,例如透明度,那么不必重新构建它的Display List,而只需要修改上次构建的Display List的相应属性值即可,这样就避免了执行View的绘制相关成员函数,例如成员函数draw和onDraw。

接下来我们就通过设置一个View的透明度值来说明这一过程,如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
        ......

        public void setAlpha(float alpha) {
            ......
            if (mTransformationInfo.mAlpha != alpha) {
                mTransformationInfo.mAlpha = alpha;
                if (onSetAlpha((int) (alpha * 255))) {
                    ......
                    invalidate(true);
                } else {
                    ......
                    mRenderNode.setAlpha(getFinalAlpha());
                    ......
                }
            }
        }

        ......
    }     

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

参数alpha即为要设置的透明度值,View的当前透明度值记录在成员变量mTransformationInfo描述的一个TransformationInfo对象的成员函数mAlpha中。当这两个值不相等时,就需要修改当前正在处理的View的透明度值。

修改了当前正在处理的View的透明度值,View类的成员函数setAlpha调用另外一个成员函数onSetAlpha询问子类是否要自己处理这个透明度值的变化。如果子类需要处理的话,那么调用它的成员函数onSetAlpha的返回值就为true。在这种情况下,由于不知道子类会怎么响应这个透明度值的变化事件,例如子类可能会修改其它的UI元素,因此View类的成员函数setAlpha就保守地调用成员函数invalidate来通知应用程序窗口,在下一个Vsync信号到来时,重新构建当前处理的View的Display List。

但是,如果子类不需要响应透明度值的变化事件,那么就不需要调用成员函数invalidate来通知应用程序窗口重新构建当前正在处理的View的Display List了,而是相应地修改与当前正在处理的View关联的Render Node的Alpha属性即可,这是通过调用RenderNode类的成员函数setAlpha完成的。

RenderNode类的成员函数setAlpha的实现如下所示:

public class RenderNode {
        ......

        public boolean setAlpha(float alpha) {
            return nSetAlpha(mNativeRenderNode, alpha);
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/android/view/RenderNode.java中。

RenderNode类的成员函数setAlpha调用另外一个成员函数nSetAlpha来设置一个Render Node的Alpha属性。

RenderNode类的成员函数nSetAlpha是一个JNI函数,由Native层的函数android_view_RenderNode_setAlpha实现,如下所示:

#define SET_AND_DIRTY(prop, val, dirtyFlag) \
        (reinterpret_cast<RenderNode*>(renderNodePtr)->mutateStagingProperties().prop(val) \
            ? (reinterpret_cast<RenderNode*>(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \
            : false)

    ......

    static jboolean android_view_RenderNode_setAlpha(JNIEnv* env,
            jobject clazz, jlong renderNodePtr, float alpha) {
        return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
    }

这个函数定义在文件frameworks/base/core/jni/android_view_RenderNode.cpp中。

函数android_view_RenderNode_setAlpha通过宏SET_AND_DIRTY来设置一个Render Node的Alpha属性。宏SET_AND_DIRTY首先是调用Render Node的成员函数mutateStagingProperties获得一个RenderProperties对象,如下所示:

class RenderNode : public VirtualLightRefBase {
    public:
        ......

        RenderProperties& mutateStagingProperties() {
            return mStagingProperties;
        }

        ......

    privagte:
        ......

        RenderProperties mStagingProperties;
        ......
    };

这个函数定义在文件frameworks/base/libs/hwui/RenderNode.h中。

宏SET_AND_DIRTY接着再调用获得的RenderProperties对象的成员函数setAlpha设置一个Render Node的Alpha属性,如下所示:

class ANDROID_API RenderProperties {
    public:
        ......

        bool setAlpha(float alpha) {
            alpha = MathUtils::clampAlpha(alpha);
            return RP_SET(mPrimitiveFields.mAlpha, alpha);
        }

        ......
    private:
        ......

        struct PrimitiveFields {
            ......
            float mAlpha;
            ......
        } mPrimitiveFields;

        ......
    };

这个函数定义在文件frameworks/base/libs/hwui/RenderProperties.h。

RenderProperties对象的成员函数setAlpha通过宏RP_SET来设置一个Render Node的Alpha属性,这个Alpha属性保存在Render Node内部的一个RenderProperties对象的成员变量mPrimitiveFields描述的一个PrimitiveFields对象的成员变量mAlpha中。

如果一个Render Node的Alpha属性发生了变化,也就是它之前的Alpha值与新设置的Alpha值不一样,那么宏RP_SET的返回值就为true。在这种情况下,宏SET_AND_DIRTY就会调用Render Node的成员函数setPropertyFieldsDirty标记它的属性发生了变化,以便后面在渲染该Render Node的Display List时,可以进行相应的处理。

从前面分析的这个Alpha属性设置过程就可以知道,每一个View关联的Render Node在内部通过一个RenderProperties对象保存了它的一些属性。当这些属性发生变化时,不必重新构建View的Display List,而只需要修改上述的RenderProperties对象相应成员变量值即可。通过这种方式,就可以提到应用程序窗口的渲染效率。

至此,应用程序窗口的Display List的构建过程我们就分析完成了。Display List的构建只是应用程序窗口的整个绘制过程中的第一步,第二步是对它的Display List进行渲染,这样才可以将应用程序窗口的UI显示在屏幕上。在接下来的一篇文章中,我们就继续分析应用程序窗口的Display List的渲染过程,敬请关注!更多的内容也可以关注老罗的新浪微博:http://weibo.com/shengyangluo

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录