概述
我們知道,activity顯示出頁面是在onresum之后,那么他具體到底是怎么添加和繪制的呢
繪制的入口
從前面講的APP啟動流程分析中我們知道,在創(chuàng)建Activiy的流程這一步中,其步驟
1、創(chuàng)建Activity的Context;
2、通過反射創(chuàng)建Activity對象;
3、執(zhí)行Activity的attach動作,其中會創(chuàng)建應用窗口的PhoneWindow對象并設置WindowManage;
4、執(zhí)行應用Activity的onCreate生命周期函數(shù),并在setContentView中創(chuàng)建窗口的DecorView對象;
在Activiy Resume的流程這一步中,其步驟:
1、執(zhí)行應用Activity的onResume生命周期函數(shù);
2、執(zhí)行WindowManager的addView動作開啟視圖繪制邏輯;
3、創(chuàng)建Activity的ViewRootImpl對象;
4、執(zhí)行ViewRootImpl的setView函數(shù)開啟UI界面繪制動作;
所以其入口跟隨Activity的啟動而調用
應用UI布局與繪制
應用在執(zhí)行Activity的Resume流程的最后,會創(chuàng)建ViewRootImpl對象并調用其setView函數(shù),從此并開啟了應用界面UI布局與繪制的流程。在開始講解這個過程之前,我們先來整理一下前面代碼中講到的這些概念,如Activity、PhoneWindow、DecorView、ViewRootImpl、WindowManager它們之間的關系與職責,因為這些核心類基本構成了Android系統(tǒng)的GUI顯示系統(tǒng)在應用進程側的核心架構,其整體架構如下圖所示:

Window是一個抽象類,通過控制DecorView提供了一些標準的UI方案,比如背景、標題、虛擬按鍵等,而PhoneWindow是Window的唯一實現(xiàn)類,在Activity創(chuàng)建后的attach流程中創(chuàng)建,應用啟動顯示的內容裝載到其內部的mDecor(DecorView);
DecorView是整個界面布局View控件樹的根節(jié)點,通過它可以遍歷訪問到整個View控件樹上的任意節(jié)點;
WindowManager是一個接口,繼承自ViewManager接口,提供了View的基本操作方法;
WindowManagerImp實現(xiàn)了WindowManager接口,內部通過組合方式持有WindowManagerGlobal,用來操作View;
WindowManagerGlobal是一個全局單例,內部可以通過ViewRootImpl將View添加至窗口中;
ViewRootImpl是所有View的Parent,用來總體管理View的繪制以及與系統(tǒng)WMS窗口管理服務的IPC交互從而實現(xiàn)窗口的開辟;ViewRootImpl是應用進程運轉的發(fā)動機,可以看到ViewRootImpl內部包含mView(就是DecorView)、mSurface、Choregrapher,mView代表整個控件樹,mSurfacce代表畫布,應用的UI渲染會直接放到mSurface中,Choregorapher使得應用請求vsync信號,接收信號后開始渲染流程;
我們從ViewRootImpl的setView流程繼續(xù)結合代碼往下看:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
}
...
// 開啟繪制硬件加速,初始化RenderThread渲染線程運行環(huán)境
enableHardwareAcceleration(attrs);
...
// 1.觸發(fā)繪制動作
requestLayout();
...
inputChannel = new InputChannel();
...
// 2.Binder調用訪問系統(tǒng)窗口管理服務WMS接口,實現(xiàn)addWindow添加注冊應用窗口的操作,并傳入inputChannel用于接收觸控事件
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
...
// 3.創(chuàng)建WindowInputEventReceiver對象,實現(xiàn)應用窗口接收觸控事件
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
...
// 4.設置DecorView的mParent為ViewRootImpl
view.assignParent(this);
...
}
}
從以上代碼可以看出ViewRootImpl的setView內部關鍵流程如下:
1、requestLayout()通過一系列調用觸發(fā)界面繪制(measure、layout、draw)動作,下文會詳細展開分析;
2、通過Binder調用訪問系統(tǒng)窗口管理服務WMS的addWindow接口,實現(xiàn)添加、注冊應用窗口的操作,并傳入本地創(chuàng)建inputChannel對象用于后續(xù)接收系統(tǒng)的觸控事件,這一步執(zhí)行完我們的View就可以顯示到屏幕上了。關于WMS的內部實現(xiàn)流程也非常復雜,由于篇幅有限本文就不詳細展開分析了。
3、創(chuàng)建WindowInputEventReceiver對象,封裝實現(xiàn)應用窗口接收系統(tǒng)觸控事件的邏輯;
4、執(zhí)行view.assignParent(this),設置DecorView的mParent為ViewRootImpl。所以,雖然ViewRootImpl不是一個View,但它是所有View的頂層Parent。
接下來ViewRootImpl的requestLayout動作繼續(xù)往下看界面繪制的流程代碼:
/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 檢查當前UI繪制操作是否發(fā)生在主線程,如果發(fā)生在子線程則會拋出異常
checkThread();
mLayoutRequested = true;
// 觸發(fā)繪制操作
scheduleTraversals();
}
}
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
//...
// 注意此處會往主線程的MessageQueue消息隊列中添加同步欄刪,因為系統(tǒng)繪制消息屬于異步消息,需要更高優(yōu)先級的處理
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 通過Choreographer往主線程消息隊列添加CALLBACK_TRAVERSAL繪制類型的待執(zhí)行消息,用于觸發(fā)后續(xù)UI線程真正實現(xiàn)繪制動作
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
//...
}
}
Choreographer 的引入,主要是配合系統(tǒng)Vsync垂直同步機制(Android“黃油計劃”中引入的機制之一,協(xié)調APP生成UI數(shù)據(jù)和SurfaceFlinger合成圖像,避免Tearing畫面撕裂的現(xiàn)象),給上層 App 的渲染提供一個穩(wěn)定的 Message 處理的時機,也就是 Vsync 到來的時候 ,系統(tǒng)通過對 Vsync 信號周期的調整,來控制每一幀繪制操作的時機。Choreographer 扮演 Android 渲染鏈路中承上啟下的角色:
1、負責接收和處理 App 的各種更新消息和回調,等到 Vsync 到來的時候統(tǒng)一處理。比如集中處理 Input(主要是 Input 事件的處理) 、Animation(動畫相關)、Traversal(包括 measure、layout、draw 等操作) ,判斷卡頓掉幀情況,記錄 CallBack 耗時等;
2、負責請求和接收 Vsync 信號。接收 Vsync 事件回調(通過 FrameDisplayEventReceiver.onVsync ),請求 Vsync(FrameDisplayEventReceiver.scheduleVsync) 。
Choreographer在收到CALLBACK_TRAVERSAL類型的繪制任務后,其內部的工作流程如下圖所示

ViewRootImpl調用Choreographer的postCallback接口放入待執(zhí)行的繪制消息后,Choreographer會先向系統(tǒng)申請APP 類型的vsync信號,然后等待系統(tǒng)vsync信號到來后,去回調到ViewRootImpl的doTraversal函數(shù)中執(zhí)行真正的繪制動作(measure、layout、draw)。
接著ViewRootImpl的doTraversal函數(shù)的簡化代碼流程往下看:
/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 調用removeSyncBarrier及時移除主線程MessageQueue中的Barrier同步欄刪,以避免主線程發(fā)生“假死”
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
// 執(zhí)行具體的繪制任務
performTraversals();
...
}
}
private void performTraversals() {
...
// 1.從DecorView根節(jié)點出發(fā),遍歷整個View控件樹,完成整個View控件樹的measure測量操作
windowSizeMayChange |= measureHierarchy(...);
...
if (mFirst...) {
// 2.第一次執(zhí)行traversals繪制任務時,Binder調用訪問系統(tǒng)窗口管理服務WMS的relayoutWindow接口,實現(xiàn)WMS計算應用窗口尺寸并向系統(tǒng)surfaceflinger正式申請Surface“畫布”操作
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
}
...
// 3.從DecorView根節(jié)點出發(fā),遍歷整個View控件樹,完成整個View控件樹的layout測量操作
performLayout(lp, mWidth, mHeight);
...
// 4.從DecorView根節(jié)點出發(fā),遍歷整個View控件樹,完成整個View控件樹的draw測量操作
performDraw();
...
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
// 通過Binder IPC訪問系統(tǒng)WMS服務的relayout接口,申請Surface“畫布”操作
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize, mBlastSurfaceControl);
if (mSurfaceControl.isValid()) {
if (!useBLAST()) {
// 本地Surface對象獲取指向遠端分配的Surface的引用
mSurface.copyFrom(mSurfaceControl);
} else {
...
}
}
...
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
// 原生標識View樹的measure測量過程的trace tag
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
// 從mView指向的View控件樹的根節(jié)點DecorView出發(fā),遍歷訪問整個View樹,并完成整個布局View樹的測量工作
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private void performDraw() {
...
boolean canUseAsync = draw(fullRedrawNeeded);
...
}
private boolean draw(boolean fullRedrawNeeded) {
...
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...
// 如果開啟并支持硬件繪制加速,則走硬件繪制的流程(從Android 4.+開始,默認情況下都是支持跟開啟了硬件加速的)
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// 否則走drawSoftware軟件繪制的流程
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
ViewRootImpl中負責的整個應用界面繪制的主要流程
1、從界面View控件樹的根節(jié)點DecorView出發(fā),遞歸遍歷整個View控件樹,完成對整個View控件樹的measure測量操作,由于篇幅所限,本文就不展開分析這塊的詳細流程;
2、界面第一次執(zhí)行繪制任務時,會通過BinderIPC訪問系統(tǒng)窗口管理服務WMS的relayout接口,實現(xiàn)窗口尺寸的計算并向系統(tǒng)申請用于本地繪制渲染的Surface“畫布”的操作(具體由SurfaceFlinger負責創(chuàng)建應用界面對應的BufferQueueLayer對象,并通過內存共享的方式通過Binder將地址引用透過WMS回傳給應用進程這邊),由于篇幅所限,本文就不展開分析這塊的詳細流程;
3、從界面View控件樹的根節(jié)點DecorView出發(fā),遞歸遍歷整個View控件樹,完成對整個View控件樹的layout測量操作;
4、從界面View控件樹的根節(jié)點DecorView出發(fā),遞歸遍歷整個View控件樹,完成對整個View控件樹的draw測量操作,如果開啟并支持硬件繪制加速(從Android 4.X開始谷歌已經(jīng)默認開啟硬件加速),則走GPU硬件繪制的流程,否則走CPU軟件繪制的流程;
繪制流程如下:

DecorView是所有視圖的根視圖,也就是最頂層布局,它是一個ViewGroup。每個Activity的View繪制流程都是先從DecorView的繪制開始,然后依次遞歸繪制它的子View和子ViewGroup,子ViewGroup再遞歸繪制它包含的子View,直到所有的View都繪制完成。
這里說說View繪制涉及到的類和方法
從上面源碼分析中可以看出,View繪制的入口是ViewRootImpl類的performTraversals()方法。在performTraversals()方法中有3個重要的步驟方法:
(1)performMeasure():該方法主要用于測量所有View和ViewGroup的大小(寬和高)
(2)performLayout():該方法用于確定所有View和ViewGroup在父布局的位置
(3)performDraw():該方法進行具體的繪制操作
performMeasure測量
1.測量涉及的方法
performMeasure()主要就是用來測量View和ViewGroup的寬和高,涉及到的方法有:
(1)View:Measure() —>onMeasure() —>setMeasureDimension() —>getDefaultSize()
View的測量流程:View的測量是從Measure()方法開始的,在Measure()里面沒有具體的操作,而是直接調用onMeasure()方法。在onMeasure()方法里調用了兩個getDefaultSize()方法來分別測量View的寬和高的值。最后調用setMeasureDimension()方法將View的寬和高測量值保存下來。
(2)ViewGroup:Measure() —>onMeasure() —>measureChildren() —>measureChild()/measureChildWithMargins() —>Measure() —>onMeasure()—>setMeasureDimension() —>getDefaultSize()。
ViewGroup除了需要測量自己的寬高大小,還需要測量它包含的子View或子ViewGroup的大小,所以它涉及到的方法除了測量View需要的方法之外,還有measureChildren()方法,在measureChildren()方法內通過循環(huán)調用measureChild()方法,在measureChild方法中又調用子View或子ViewGroup的Measure()方法。
總結:在performMeasure()方法中首先調用的是頂級視圖DecorView的Measure()方法,在Measure()方法中調用onMeasure()方法進行DecorView的寬高大小測量。由于DecorView是一個ViewGroup,所以在onMeasure()方法內又調用了measureChildren()方法來測量它的子View和子ViewGroup寬高大小,這樣一步步往下遞歸遍歷,最后測量出所有的View和ViewGroup的大小。
2.MeasureSpec
上面描述了測量View和ViewGroup大小的大致流程,但是在具體測量的時候還涉及到了一個MeasureSpec類,這個類是View類的內部類,主要內容是一個int型變量,int是32位的,其中高2位表示模式(Mode),低30位表示大?。⊿ize)。每一個View和ViewGroup都有自己的MeasureSpec,具體來說是有2個MeasureSpec:widthMeasureSpec和heightMeasureSpec。這兩個MeasureSpec從measure()方法開始作為參數(shù),一直傳遞到getDefaultSize()方法,最后在getDefaultSize()方法里面參考這2個MeasureSpec來確定具體大小。
MeasureSpec高2位表示的模式有3種:UNSPECIFIED(不指定的)、EXACTLY(確定的)、AT_MOST(至多的)。
子View的大小通常是受限于父布局的,舉個例子,假如某個子View大小設為match_parent,那么該子View的大小就依賴于父布局的大小,父布局如果大小為200200dp,子View就為200200dp。
子View的MeasureSpec是由自身的LayoutParams和父布局的MeasureSpec共同確定的。在上面例子中父布局大小為200*200dp,于是父布局的widthMeasureSpec和widthMeasureSpec模式Mode為EXACTLY(確定的),大小Size為200。又由于子View的LayoutParams指定為match_parent,于是子View的widthMeasureSpec和widthMeasureSpec模式Mode也為EXACTLY(確定的),大小Size也為200。
上面子View的MeasureSpec確定過程發(fā)生在measureChild()方法中,確定好子View的MeasureSpec后就將其作為參數(shù)傳入到measure()方法,最后在getDefaultSize()方法里根據(jù)子View的兩個MeasureSpec來確定子View的寬高。
父MeasureSpec和子MeasureSpec的關系圖如下,上面例子對應下圖的第二行第一列。
| 父/子 | EXACTLY | AT_MOST | UNSPECIFIED |
|---|---|---|---|
| 具體尺寸 | EXACTLY 、childSize | EXACTLY 、childSize | EXACTLY 、childSize |
| match_parent | EXACTLY 、parentSize | AT_MOST、parentSize | UNSPECIFIED、SDK<23?0:parentSize |
| wrap_content | AT_MOST、parentSize | AT_MOST、parentSize | UNSPECIFIED、SDK<23?0:parentSize |
上圖總結:
1.當子View的LayoutParams指定為具體的尺寸如200*200dp,那么無論父布局的MeasureSpec模式和大小是什么,子View的模式都為EXACTLY(確定的),大小就是LayoutParams指定的具體值200,而不依賴于父布局MeasureSpec指定的大小。
2.當子View的LayoutParams指定為match_parent時,子View的MeasureSpec就和父布局的MeasureSpec相同。
2.當子View的LayoutParams指定為wrap_content時,分兩種情況:當父布局MeasureSpec模式為UNSPECIFIED(不指定的),子View的MeasureSpec模式也是UNSPECIFIED(不指定的);當父布局MeasureSpec模式為EXACTLY(確定的)和AT_MOST(至多的),子View的MeasureSpec模式都是AT_MOST(至多的)。無論Mode是哪一種,子View的MeasureSpec的大?。⊿ize)都是父布局的MeasureSpec中指定的大小,表示子View大小不應該超過父布局大小。
只要知道了父布局的MeasureSpec和子View的LayoutParams,那么子View的MeasureSpec也就確定了,從而能夠進一步確定子View的大小。
ps:由于DecorView是頂級布局,所以它的MeasureSpec是通過窗口大小和自身的LayoutParams確定的。
performLayout布局
在performTraversals()方法中,首先調用performMeasure()方法來測量所有View和ViewGroup的寬高大小。之后就是調用performLayout()方法確定每個View和ViewGroup在其父布局中的位置。
布局涉及的方法
(1)View:layout()—>setFrame()
View布局流程:View通過調用layout()方法來確定它在父布局的位置,具體是在layout()方法內調用setFrame()方法,將該View的上下左右四個點位置作為參數(shù)傳入,從而確定了View在父布局的位置。
ViewGroup:layout()—>setFrame()—>onLayout()
ViewGroup布局流程:ViewGroup同樣先調用layout()方法,通過layout()方法內的setFrame()方法設置自己相對于父布局的位置。但是由于ViewGroup包含子View或子ViewGroup,所以需要重寫onLayout()方法,在onLayout()方法中遍歷自己的子View或子ViewGroup,分別調用它們的layout()方法來完成子View或子ViewGroup的布局。
總結:在performLayout()方法中首先調用的是頂級布局DecorView的Layout()方法,由于DecorView是一個ViewGroup,所以它調用的是父類View的layout()方法(原因在下面PS中介紹),并且在layout()方法中確定了自己的位置之后再重寫onLayout()方法來遍歷子View和子ViewGroup,通過調用View和ViewGroup的layout()方法來確定它們在父布局DecorView中的位置,這樣一步步往下遞歸遍歷,直到所有的View都確定了在父布局的位置為止。
PS:
1.ViewGroup的layout()方法是final修飾的,不能被重寫;View的layout()方法是可以被重寫的。
2.在ViewGroup中l(wèi)ayout()方法中最終調用的是View的layout()方法。
2.View的onLayout()方法是一個空方法,這是因為View沒有子View或子ViewGroup,不需要onLayout()方法。而ViewGroup的onLayout()方法是一個抽象方法,即ViewGroup的子類必須重寫這個方法,因為ViewGroup包含子View或子ViewGroup,需要在onLayout()方法中遍歷View或子ViewGroup并調用它們的layout()方法,從而確定它們在父布局中的位置。
performDraw繪制
在performTraversals()方法中,首先調用performMeasure()方法來測量所有View和ViewGroup的寬高大小。之后就是調用performLayout()方法確定每個View和ViewGroup在其父布局中的位置。最后就是調用performDraw進行具體的繪制。
繪制涉及的方法:
View:draw()—>drawBackground()—>onDraw()—>onDrawScrollBars()。
View繪制流程:首先調用的是draw()方法,在該方法內依次調用drawBackground()方法來繪制View背景,調用onDraw()方法繪制View內容,最后調用onDrawScrollBars()方法繪制裝飾。
ViewGroup:draw()—>drawBackground()—>onDraw()—>dispatchDraw()—>onDrawScrollBars()。
ViewGroup繪制流程:在ViewGroup的draw()方法中,在調用drawBackground()方法來繪制View背景、調用onDraw()方法繪制View內容之后還需要調用dispatchDraw()方法來繪制子View和子ViewGroup,具體是在dispatchDraw()方法內循環(huán)遍歷子View和子ViewGroup,并且調用它們的draw()方法進行繪制。
總結:在performDraw()方法中首先調用頂級布局DecorView的draw()方法繪制自身,由于DecorView是一個ViewGroup,所以在繪制完自己之后還要調用dispatchDraw()方法來遍歷繪制它的子View和子ViewGroup。這樣一步步往下遞歸遍歷繪制,直到所有View和ViewGroup都繪制完成為止。
PS:在View類中的onDraw()方法為空方法,具體的繪制邏輯需要在子類中的onDraw()方法中實現(xiàn)。
渲染
RenderThread渲染
在ViewRootImpl中完成了對界面的measure、layout和draw等繪制流程后,用戶依然還是看不到屏幕上顯示的應用界面內容,因為整個Android系統(tǒng)的顯示流程除了前面講到的UI線程的繪制外,界面還需要經(jīng)過RenderThread線程的渲染處理,渲染完成后,還需要通過Binder調用“上幀”交給surfaceflinger進程中進行合成后送顯才能最終顯示到屏幕上。本小節(jié)中,我們將接上一節(jié)中ViewRootImpl中最后draw的流程繼續(xù)往下分析開啟硬件加速情況下,RenderThread渲染線程的工作流程。由于目前Android 4.X之后系統(tǒng)默認界面是開啟硬件加速的,所以本文我們重點分析硬件加速條件下的界面渲染流程,我們先分析一下簡化的代碼流程:
/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
private boolean draw(boolean fullRedrawNeeded) {
...
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...
// 硬件加速條件下的界面渲染流程
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
...
}
}
/*frameworks/base/core/java/android/view/ThreadedRenderer.java*/
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
...
// 1.從DecorView根節(jié)點出發(fā),遞歸遍歷View控件樹,記錄每個View節(jié)點的繪制操作命令,完成繪制操作命令樹的構建
updateRootDisplayList(view, callbacks);
...
// 2.JNI調用同步Java層構建的繪制命令樹到Native層的RenderThread渲染線程,并喚醒渲染線程利用OpenGL執(zhí)行渲染任務;
int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
...
}
從上面的代碼可以看出,硬件加速繪制主要包括兩個階段:
1、從DecorView根節(jié)點出發(fā),遞歸遍歷View控件樹,記錄每個View節(jié)點的drawOp繪制操作命令,完成繪制操作命令樹的構建;
2、JNI調用同步Java層構建的繪制命令樹到Native層的RenderThread渲染線程,并喚醒渲染線程利用OpenGL執(zhí)行渲染任務;
構建繪制命令樹
我們先來看看第一階段構建繪制命令樹的代碼簡化流程:
/*frameworks/base/core/java/android/view/ThreadedRenderer.java*/
private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
// 原生標記構建View繪制操作命令樹過程的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
// 遞歸子View的updateDisplayListIfDirty實現(xiàn)構建DisplayListOp
updateViewTreeDisplayList(view);
...
if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
// 獲取根View的SkiaRecordingCanvas
RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
try {
...
// 利用canvas緩存DisplayListOp繪制命令
canvas.drawRenderNode(view.updateDisplayListIfDirty());
...
} finally {
// 將所有DisplayListOp繪制命令填充到RootRenderNode中
mRootNode.endRecording();
}
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
private void updateViewTreeDisplayList(View view) {
...
// 從DecorView根節(jié)點出發(fā),開始遞歸調用每個View樹節(jié)點的updateDisplayListIfDirty函數(shù)
view.updateDisplayListIfDirty();
...
}
/*frameworks/base/core/java/android/view/View.java*/
public RenderNode updateDisplayListIfDirty() {
...
// 1.利用`View`對象構造時創(chuàng)建的`RenderNode`獲取一個`SkiaRecordingCanvas`“畫布”;
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
...
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// 如果僅僅是ViewGroup,并且自身不用繪制,直接遞歸子View
dispatchDraw(canvas);
...
} else {
// 2.利用SkiaRecordingCanvas,在每個子View控件的onDraw繪制函數(shù)中調用drawLine、drawRect等繪制操作時,創(chuàng)建對應的DisplayListOp繪制命令,并緩存記錄到其內部的SkiaDisplayList持有的DisplayListData中;
draw(canvas);
}
} finally {
// 3.將包含有`DisplayListOp`繪制命令緩存的`SkiaDisplayList`對象設置填充到`RenderNode`中;
renderNode.endRecording();
...
}
...
}
public void draw(Canvas canvas) {
...
// draw the content(View自己實現(xiàn)的onDraw繪制,由應用開發(fā)者自己實現(xiàn))
onDraw(canvas);
...
// draw the children
dispatchDraw(canvas);
...
}
/*frameworks/base/graphics/java/android/graphics/RenderNode.java*/
public void endRecording() {
...
// 從SkiaRecordingCanvas中獲取SkiaDisplayList對象
long displayList = canvas.finishRecording();
// 將SkiaDisplayList對象填充到RenderNode中
nSetDisplayList(mNativeRenderNode, displayList);
canvas.recycle();
}
從以上代碼可以看出,構建繪制命令樹的過程是從View控件樹的根節(jié)點DecorView觸發(fā),遞歸調用每個子View節(jié)點的updateDisplayListIfDirty函數(shù),最終完成繪制樹的創(chuàng)建,簡述流程如下:
利用View對象構造時創(chuàng)建的RenderNode獲取一個SkiaRecordingCanvas“畫布”;
利用SkiaRecordingCanvas,在每個子View控件的onDraw繪制函數(shù)中調用drawLine、drawRect等繪制操作時,創(chuàng)建對應的DisplayListOp繪制命令,并緩存記錄到其內部的SkiaDisplayList持有的DisplayListData中;
將包含有DisplayListOp繪制命令緩存的SkiaDisplayList對象設置填充到RenderNode中;
最后將根View的緩存DisplayListOp設置到RootRenderNode中,完成構建。
以上整個構建繪制命令樹的過程可以用如下流程圖表示:

執(zhí)行渲染繪制任務
經(jīng)過上一小節(jié)中的分析,應用在UI線程中從根節(jié)點DecorView出發(fā),遞歸遍歷每個子View節(jié)點,搜集其drawXXX繪制動作并轉換成DisplayListOp命令,將其記錄到DisplayListData并填充到RenderNode中,最終完成整個View繪制命令樹的構建。從此UI線程的繪制任務就完成了。下一步UI線程將喚醒RenderThread渲染線程,觸發(fā)其利用OpenGL執(zhí)行界面的渲染任務,本小節(jié)中我們將重點分析這個流程。我們還是先看看這塊代碼的簡化流程:
/*frameworks/base/graphics/java/android/graphics/HardwareRenderer.java*/
public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) {
// JNI調用native層的相關函數(shù)
return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length);
}
/*frameworks/base/libs/hwui/jni/android_graphics_HardwareRenderer.cpp*/
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
...
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
return proxy->syncAndDrawFrame();
}
/*frameworks/base/libs/hwui/renderthread/RenderProxy.cpp*/
int RenderProxy::syncAndDrawFrame() {
// 喚醒RenderThread渲染線程,執(zhí)行DrawFrame繪制任務
return mDrawFrameTask.drawFrame();
}
/*frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp*/
int DrawFrameTask::drawFrame() {
...
postAndWait();
...
}
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
// 向RenderThread渲染線程的MessageQueue消息隊列放入一個待執(zhí)行任務,以將其喚醒執(zhí)行run函數(shù)
mRenderThread->queue().post([this]() { run(); });
// UI線程暫時進入wait等待狀態(tài)
mSignal.wait(mLock);
}
void DrawFrameTask::run() {
// 原生標識一幀渲染繪制任務的systrace tag
ATRACE_NAME("DrawFrame");
...
{
TreeInfo info(TreeInfo::MODE_FULL, *mContext);
//1.將UI線程構建的DisplayListOp繪制命令樹同步到RenderThread渲染線程
canUnblockUiThread = syncFrameState(info);
...
}
...
// 同步完成后則可以喚醒UI線程
if (canUnblockUiThread) {
unblockUiThread();
}
...
if (CC_LIKELY(canDrawThisFrame)) {
// 2.執(zhí)行draw渲染繪制動作
context->draw();
} else {
...
}
...
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
...
// 調用CanvasContext的prepareTree函數(shù)實現(xiàn)繪制命令樹同步的流程
mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
...
}
/*frameworks/base/libs/hwui/renderthread/CanvasContext.cpp*/
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued,
RenderNode* target) {
...
for (const sp<RenderNode>& node : mRenderNodes) {
...
// 遞歸調用各個子View對應的RenderNode執(zhí)行prepareTree動作
node->prepareTree(info);
...
}
...
}
/*frameworks/base/libs/hwui/RenderNode.cpp*/
void RenderNode::prepareTree(TreeInfo& info) {
ATRACE_CALL();
...
prepareTreeImpl(observer, info, false);
...
}
void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
...
if (info.mode == TreeInfo::MODE_FULL) {
// 同步繪制命令樹
pushStagingDisplayListChanges(observer, info);
}
if (mDisplayList) {
// 遍歷調用各個子View對應的RenderNode的prepareTreeImpl
bool isDirty = mDisplayList->prepareListAndChildren(
observer, info, childFunctorsNeedLayer,
[](RenderNode* child, TreeObserver& observer, TreeInfo& info,
bool functorsNeedLayer) {
child->prepareTreeImpl(observer, info, functorsNeedLayer);
});
...
}
...
}
void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
...
syncDisplayList(observer, &info);
...
}
void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
...
// 完成賦值同步DisplayList對象
mDisplayList = mStagingDisplayList;
mStagingDisplayList = nullptr;
...
}
void CanvasContext::draw() {
...
// 1.調用OpenGL庫使用GPU,按照構建好的繪制命令完成界面的渲染
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
&(profiler()));
...
// 2.將前面已經(jīng)繪制渲染好的圖形緩沖區(qū)Binder上幀給SurfaceFlinger合成和顯示
bool didSwap =
mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
...
}
從以上代碼可以看出:UI線程利用RenderProxy向RenderThread線程發(fā)送一個DrawFrameTask任務請求,RenderThread被喚醒,開始渲染,大致流程如下:
syncFrameState中遍歷View樹上每一個RenderNode,執(zhí)行prepareTreeImpl函數(shù),實現(xiàn)同步繪制命令樹的操作;
調用OpenGL庫API使用GPU,按照構建好的繪制命令完成界面的渲染(具體過程,由于本文篇幅所限,暫不展開分析);
將前面已經(jīng)繪制渲染好的圖形緩沖區(qū)Binder上幀給SurfaceFlinger合成和顯示;
整個過程可以用如下流程圖表示:

SurfaceFlinger合成顯示
SurfaceFlinger合成顯示部分完全屬于Android系統(tǒng)GUI中圖形顯示的內容,邏輯結構也比較復雜,但不屬于本文介紹內容的重點。所以本小節(jié)中只是總體上介紹一下其工作原理與思想,不再詳細分析源碼,感興趣的讀者可以關注筆者后續(xù)的文章再來詳細分析講解。簡單的說SurfaceFlinger作為系統(tǒng)中獨立運行的一個Native進程,借用Android官網(wǎng)的描述,其職責就是負責接受來自多個來源的數(shù)據(jù)緩沖區(qū),對它們進行合成,然后發(fā)送到顯示設備。如下圖所示:

從上圖可以看出,其實SurfaceFlinger在Android系統(tǒng)的整個圖形顯示系統(tǒng)中是起到一個承上啟下的作用:
對上:通過Surface與不同的應用進程建立聯(lián)系,接收它們寫入Surface中的繪制緩沖數(shù)據(jù),對它們進行統(tǒng)一合成。
對下:通過屏幕的后緩存區(qū)與屏幕建立聯(lián)系,發(fā)送合成好的數(shù)據(jù)到屏幕顯示設備。
圖形的傳遞是通過Buffer作為載體,Surface是對Buffer的進一步封裝,也就是說Surface內部具有多個Buffer供上層使用,如何管理這些Buffer呢?答案就是BufferQueue ,下面我們來看看BufferQueue的工作原理:
BufferQueue機制
借用一張經(jīng)典的圖來描述BufferQueue的工作原理:

BufferQueue是一個典型的生產(chǎn)者-消費者模型中的數(shù)據(jù)結構。在Android應用的渲染流程中,應用扮演的就是“生產(chǎn)者”的角色,而SurfaceFlinger扮演的則是“消費者”的角色,其配合工作的流程如下:
應用進程中在開始界面的繪制渲染之前,需要通過Binder調用dequeueBuffer接口從SurfaceFlinger進程中管理的BufferQueue 中申請一張?zhí)幱趂ree狀態(tài)的可用Buffer,如果此時沒有可用Buffer則阻塞等待;
應用進程中拿到這張可用的Buffer之后,選擇使用CPU軟件繪制渲染或GPU硬件加速繪制渲染,渲染完成后再通過Binder調用queueBuffer接口將緩存數(shù)據(jù)返回給應用進程對應的BufferQueue(如果是 GPU 渲染的話,這里還有個 GPU處理的過程,所以這個 Buffer 不會馬上可用,需要等 GPU 渲染完成的Fence信號),并申請sf類型的Vsync以便喚醒“消費者”SurfaceFlinger進行消費;
SurfaceFlinger 在收到 Vsync 信號之后,開始準備合成,使用 acquireBuffer獲取應用對應的 BufferQueue 中的 Buffer 并進行合成操作;
合成結束后,SurfaceFlinger 將通過調用 releaseBuffer將 Buffer 置為可用的free狀態(tài),返回到應用對應的 BufferQueue中。
Vsync同步機制
Vysnc垂直同步是Android在“黃油計劃”中引入的一個重要機制,本質上是為了協(xié)調BufferQueue的應用生產(chǎn)者生成UI數(shù)據(jù)動作和SurfaceFlinger消費者的合成消費動作,避免出現(xiàn)畫面撕裂的Tearing現(xiàn)象。Vysnc信號分為兩種類型:
app類型的Vsync:app類型的Vysnc信號由上層應用中的Choreographer根據(jù)繪制需求進行注冊和接收,用于控制應用UI繪制上幀的生產(chǎn)節(jié)奏。根據(jù)第7小結中的分析:應用在UI線程中調用invalidate刷新界面繪制時,需要先透過Choreographer向系統(tǒng)申請注冊app類型的Vsync信號,待Vsync信號到來后,才能往主線程的消息隊列放入待繪制任務進行真正UI的繪制動作;
sf類型的Vsync:sf類型的Vsync是用于控制SurfaceFlinger的合成消費節(jié)奏。應用完成界面的繪制渲染后,通過Binder調用queueBuffer接口將緩存數(shù)據(jù)返還給應用對應的BufferQueue時,會申請sf類型的Vsync,待SurfaceFlinger 在其UI線程中收到 Vsync 信號之后,便開始進行界面的合成操作。
Vsync信號的生成是參考屏幕硬件的刷新周期的,其架構如下圖所示:

總結
本文結合源碼完整的分析了應用Activity界面第一幀畫面顯示的完整流程,這其中涉及了App應用、system_server框架、Art虛擬機、surfaceflinger等一系列Android系統(tǒng)核心模塊的相互配合,有很多的細節(jié)也由于篇幅所限無法完全展開分析,感興趣的讀者可以結合AOSP源碼繼續(xù)深入分析。