Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android系統教程 >> 安卓省電與加速 >> Android應用程序UI硬件加速渲染的Display List構建過程分析

Android應用程序UI硬件加速渲染的Display List構建過程分析

編輯:安卓省電與加速

在硬件加速渲染環境中,Android應用程序窗口的UI渲染是分兩步進行的。第一步是構建Display List,發生在應用程序進程的Main Thread中;第二步是渲染Display List,發生在應用程序進程的Render Thread中。Display List是以視圖為單位進行構建的,因此每一個視圖都對應有一個Display List。本文詳細分析這些Display List的構建過程。

 

這裡說的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);
        ......
    }

    ......
}

這個函數定義在文件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繪制下一幀。

這裡我們只關注第一件事情,其余兩件事情在接下來的兩篇文章中再詳細分析。

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 sPool =
            new SynchronizedPool(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(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(rendererPtr);
    RenderNode* renderNode = reinterpret_cast(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。

3. 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對象的成員變量projectionReceivpeIndex中。後面在渲染應用程序窗口的Display List的時候,就會用到這個Projection Receivpe 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(rendererPtr);
    return reinterpret_cast(renderer->finishRecording());
}
這個函數定義在文件frameworks/base/core/jni/android_view_GLES20Canvas.cpp中。

 

參數renderPtr描述的是一個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(renderNodePtr);
    DisplayListData* newData = reinterpret_cast(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。

3. 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可以通過Textrue類的成員函數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(proxyPtr);
    DeferredLayerUpdater* layer = proxy->createTextureLayer();
    return reinterpret_cast(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(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(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(proxyPtr);
    DeferredLayerUpdater* layer = reinterpret_cast(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(rendererPtr);
    Layer* layer = reinterpret_cast(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(bitmapPtr);
    ......

    DisplayListRenderer* renderer = reinterpret_cast(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創建一個新的Rendr 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實現的。

3. 調用關聯的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的投影。

2. 與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(renderNodePtr)->mutateStagingProperties().prop(val) 
        ? (reinterpret_cast(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 LayerProperties {
public:
    ......

    bool setAlpha(uint8_t alpha) {
        return RP_SET(mAlpha, alpha);
    }

    ......

private:
    ......

    uint8_t mAlpha;
  
    ......
};
這個函數定義在文件frameworks/base/libs/hwui/RenderProperties.h。

 

RenderProperties對象的成員函數setAlpha通過宏RP_SET來設置一個Render Node的Alpha屬性,這個Alpha屬性保存在Render Node內部的一個RenderProperties對象的成員變量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顯示在屏幕上。 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved