概述
我們知道油宜,activity顯示出頁(yè)面是在onresum之后希痴,那么他具體到底是怎么添加和繪制的呢
繪制的入口
從前面講的APP啟動(dòng)流程分析中我們知道者甲,在創(chuàng)建Activiy的流程這一步中,其步驟
1砌创、創(chuàng)建Activity的Context虏缸;
2、通過反射創(chuàng)建Activity對(duì)象纺铭;
3寇钉、執(zhí)行Activity的attach動(dòng)作,其中會(huì)創(chuàng)建應(yīng)用窗口的PhoneWindow對(duì)象并設(shè)置WindowManage舶赔;
4扫倡、執(zhí)行應(yīng)用Activity的onCreate生命周期函數(shù),并在setContentView中創(chuàng)建窗口的DecorView對(duì)象竟纳;
在Activiy Resume的流程這一步中撵溃,其步驟:
1、執(zhí)行應(yīng)用Activity的onResume生命周期函數(shù);
2锥累、執(zhí)行WindowManager的addView動(dòng)作開啟視圖繪制邏輯;
3缘挑、創(chuàng)建Activity的ViewRootImpl對(duì)象;
4、執(zhí)行ViewRootImpl的setView函數(shù)開啟UI界面繪制動(dòng)作桶略;
所以其入口跟隨Activity的啟動(dòng)而調(diào)用
應(yīng)用UI布局與繪制
應(yīng)用在執(zhí)行Activity的Resume流程的最后语淘,會(huì)創(chuàng)建ViewRootImpl對(duì)象并調(diào)用其setView函數(shù),從此并開啟了應(yīng)用界面UI布局與繪制的流程际歼。在開始講解這個(gè)過程之前惶翻,我們先來整理一下前面代碼中講到的這些概念,如Activity鹅心、PhoneWindow吕粗、DecorView、ViewRootImpl旭愧、WindowManager它們之間的關(guān)系與職責(zé)颅筋,因?yàn)檫@些核心類基本構(gòu)成了Android系統(tǒng)的GUI顯示系統(tǒng)在應(yīng)用進(jìn)程側(cè)的核心架構(gòu)宙暇,其整體架構(gòu)如下圖所示:
Window是一個(gè)抽象類,通過控制DecorView提供了一些標(biāo)準(zhǔn)的UI方案议泵,比如背景占贫、標(biāo)題、虛擬按鍵等肢簿,而PhoneWindow是Window的唯一實(shí)現(xiàn)類靶剑,在Activity創(chuàng)建后的attach流程中創(chuàng)建,應(yīng)用啟動(dòng)顯示的內(nèi)容裝載到其內(nèi)部的mDecor(DecorView)池充;
DecorView是整個(gè)界面布局View控件樹的根節(jié)點(diǎn)桩引,通過它可以遍歷訪問到整個(gè)View控件樹上的任意節(jié)點(diǎn);
WindowManager是一個(gè)接口收夸,繼承自ViewManager接口坑匠,提供了View的基本操作方法;
WindowManagerImp實(shí)現(xiàn)了WindowManager接口卧惜,內(nèi)部通過組合方式持有WindowManagerGlobal厘灼,用來操作View;
WindowManagerGlobal是一個(gè)全局單例咽瓷,內(nèi)部可以通過ViewRootImpl將View添加至窗口中设凹;
ViewRootImpl是所有View的Parent,用來總體管理View的繪制以及與系統(tǒng)WMS窗口管理服務(wù)的IPC交互從而實(shí)現(xiàn)窗口的開辟茅姜;ViewRootImpl是應(yīng)用進(jìn)程運(yùn)轉(zhuǎn)的發(fā)動(dòng)機(jī)闪朱,可以看到ViewRootImpl內(nèi)部包含mView(就是DecorView)、mSurface钻洒、Choregrapher奋姿,mView代表整個(gè)控件樹,mSurfacce代表畫布素标,應(yīng)用的UI渲染會(huì)直接放到mSurface中称诗,Choregorapher使得應(yīng)用請(qǐng)求vsync信號(hào),接收信號(hào)后開始渲染流程头遭;
我們從ViewRootImpl的setView流程繼續(xù)結(jié)合代碼往下看:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
}
...
// 開啟繪制硬件加速寓免,初始化RenderThread渲染線程運(yùn)行環(huán)境
enableHardwareAcceleration(attrs);
...
// 1.觸發(fā)繪制動(dòng)作
requestLayout();
...
inputChannel = new InputChannel();
...
// 2.Binder調(diào)用訪問系統(tǒng)窗口管理服務(wù)WMS接口,實(shí)現(xiàn)addWindow添加注冊(cè)應(yīng)用窗口的操作,并傳入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對(duì)象计维,實(shí)現(xiàn)應(yīng)用窗口接收觸控事件
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
...
// 4.設(shè)置DecorView的mParent為ViewRootImpl
view.assignParent(this);
...
}
}
從以上代碼可以看出ViewRootImpl的setView內(nèi)部關(guān)鍵流程如下:
1袜香、requestLayout()通過一系列調(diào)用觸發(fā)界面繪制(measure、layout享潜、draw)動(dòng)作,下文會(huì)詳細(xì)展開分析嗅蔬;
2剑按、通過Binder調(diào)用訪問系統(tǒng)窗口管理服務(wù)WMS的addWindow接口疾就,實(shí)現(xiàn)添加、注冊(cè)應(yīng)用窗口的操作艺蝴,并傳入本地創(chuàng)建inputChannel對(duì)象用于后續(xù)接收系統(tǒng)的觸控事件猬腰,這一步執(zhí)行完我們的View就可以顯示到屏幕上了。關(guān)于WMS的內(nèi)部實(shí)現(xiàn)流程也非常復(fù)雜猜敢,由于篇幅有限本文就不詳細(xì)展開分析了姑荷。
3、創(chuàng)建WindowInputEventReceiver對(duì)象缩擂,封裝實(shí)現(xiàn)應(yīng)用窗口接收系統(tǒng)觸控事件的邏輯鼠冕;
4、執(zhí)行view.assignParent(this)胯盯,設(shè)置DecorView的mParent為ViewRootImpl懈费。所以,雖然ViewRootImpl不是一個(gè)View,但它是所有View的頂層Parent博脑。
接下來ViewRootImpl的requestLayout動(dòng)作繼續(xù)往下看界面繪制的流程代碼:
/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 檢查當(dāng)前UI繪制操作是否發(fā)生在主線程憎乙,如果發(fā)生在子線程則會(huì)拋出異常
checkThread();
mLayoutRequested = true;
// 觸發(fā)繪制操作
scheduleTraversals();
}
}
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
//...
// 注意此處會(huì)往主線程的MessageQueue消息隊(duì)列中添加同步欄刪,因?yàn)橄到y(tǒng)繪制消息屬于異步消息叉趣,需要更高優(yōu)先級(jí)的處理
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 通過Choreographer往主線程消息隊(duì)列添加CALLBACK_TRAVERSAL繪制類型的待執(zhí)行消息泞边,用于觸發(fā)后續(xù)UI線程真正實(shí)現(xiàn)繪制動(dòng)作
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
//...
}
}
Choreographer 的引入,主要是配合系統(tǒng)Vsync垂直同步機(jī)制(Android“黃油計(jì)劃”中引入的機(jī)制之一疗杉,協(xié)調(diào)APP生成UI數(shù)據(jù)和SurfaceFlinger合成圖像阵谚,避免Tearing畫面撕裂的現(xiàn)象),給上層 App 的渲染提供一個(gè)穩(wěn)定的 Message 處理的時(shí)機(jī)乡数,也就是 Vsync 到來的時(shí)候 椭蹄,系統(tǒng)通過對(duì) Vsync 信號(hào)周期的調(diào)整,來控制每一幀繪制操作的時(shí)機(jī)净赴。Choreographer 扮演 Android 渲染鏈路中承上啟下的角色:
1绳矩、負(fù)責(zé)接收和處理 App 的各種更新消息和回調(diào),等到 Vsync 到來的時(shí)候統(tǒng)一處理玖翅。比如集中處理 Input(主要是 Input 事件的處理) 翼馆、Animation(動(dòng)畫相關(guān))、Traversal(包括 measure金度、layout应媚、draw 等操作) ,判斷卡頓掉幀情況猜极,記錄 CallBack 耗時(shí)等中姜;
2、負(fù)責(zé)請(qǐng)求和接收 Vsync 信號(hào)。接收 Vsync 事件回調(diào)(通過 FrameDisplayEventReceiver.onVsync )丢胚,請(qǐng)求 Vsync(FrameDisplayEventReceiver.scheduleVsync) 翩瓜。
Choreographer在收到CALLBACK_TRAVERSAL類型的繪制任務(wù)后,其內(nèi)部的工作流程如下圖所示
ViewRootImpl調(diào)用Choreographer的postCallback接口放入待執(zhí)行的繪制消息后携龟,Choreographer會(huì)先向系統(tǒng)申請(qǐng)APP 類型的vsync信號(hào)兔跌,然后等待系統(tǒng)vsync信號(hào)到來后,去回調(diào)到ViewRootImpl的doTraversal函數(shù)中執(zhí)行真正的繪制動(dòng)作(measure峡蟋、layout坟桅、draw)。
接著ViewRootImpl的doTraversal函數(shù)的簡(jiǎn)化代碼流程往下看:
/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 調(diào)用removeSyncBarrier及時(shí)移除主線程MessageQueue中的Barrier同步欄刪蕊蝗,以避免主線程發(fā)生“假死”
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
// 執(zhí)行具體的繪制任務(wù)
performTraversals();
...
}
}
private void performTraversals() {
...
// 1.從DecorView根節(jié)點(diǎn)出發(fā)仅乓,遍歷整個(gè)View控件樹,完成整個(gè)View控件樹的measure測(cè)量操作
windowSizeMayChange |= measureHierarchy(...);
...
if (mFirst...) {
// 2.第一次執(zhí)行traversals繪制任務(wù)時(shí)匿又,Binder調(diào)用訪問系統(tǒng)窗口管理服務(wù)WMS的relayoutWindow接口方灾,實(shí)現(xiàn)WMS計(jì)算應(yīng)用窗口尺寸并向系統(tǒng)surfaceflinger正式申請(qǐng)Surface“畫布”操作
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
}
...
// 3.從DecorView根節(jié)點(diǎn)出發(fā),遍歷整個(gè)View控件樹碌更,完成整個(gè)View控件樹的layout測(cè)量操作
performLayout(lp, mWidth, mHeight);
...
// 4.從DecorView根節(jié)點(diǎn)出發(fā)裕偿,遍歷整個(gè)View控件樹,完成整個(gè)View控件樹的draw測(cè)量操作
performDraw();
...
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
// 通過Binder IPC訪問系統(tǒng)WMS服務(wù)的relayout接口痛单,申請(qǐng)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對(duì)象獲取指向遠(yuǎn)端分配的Surface的引用
mSurface.copyFrom(mSurfaceControl);
} else {
...
}
}
...
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
// 原生標(biāo)識(shí)View樹的measure測(cè)量過程的trace tag
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
// 從mView指向的View控件樹的根節(jié)點(diǎn)DecorView出發(fā)嘿棘,遍歷訪問整個(gè)View樹,并完成整個(gè)布局View樹的測(cè)量工作
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.+開始鸟妙,默認(rèn)情況下都是支持跟開啟了硬件加速的)
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// 否則走drawSoftware軟件繪制的流程
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
ViewRootImpl中負(fù)責(zé)的整個(gè)應(yīng)用界面繪制的主要流程
1、從界面View控件樹的根節(jié)點(diǎn)DecorView出發(fā)挥吵,遞歸遍歷整個(gè)View控件樹重父,完成對(duì)整個(gè)View控件樹的measure測(cè)量操作,由于篇幅所限忽匈,本文就不展開分析這塊的詳細(xì)流程房午;
2、界面第一次執(zhí)行繪制任務(wù)時(shí)丹允,會(huì)通過BinderIPC訪問系統(tǒng)窗口管理服務(wù)WMS的relayout接口雌桑,實(shí)現(xiàn)窗口尺寸的計(jì)算并向系統(tǒng)申請(qǐng)用于本地繪制渲染的Surface“畫布”的操作(具體由SurfaceFlinger負(fù)責(zé)創(chuàng)建應(yīng)用界面對(duì)應(yīng)的BufferQueueLayer對(duì)象伟桅,并通過內(nèi)存共享的方式通過Binder將地址引用透過WMS回傳給應(yīng)用進(jìn)程這邊)鳄橘,由于篇幅所限讳苦,本文就不展開分析這塊的詳細(xì)流程;
3批狐、從界面View控件樹的根節(jié)點(diǎn)DecorView出發(fā)扇售,遞歸遍歷整個(gè)View控件樹,完成對(duì)整個(gè)View控件樹的layout測(cè)量操作;
4承冰、從界面View控件樹的根節(jié)點(diǎn)DecorView出發(fā)嘱根,遞歸遍歷整個(gè)View控件樹,完成對(duì)整個(gè)View控件樹的draw測(cè)量操作巷懈,如果開啟并支持硬件繪制加速(從Android 4.X開始谷歌已經(jīng)默認(rèn)開啟硬件加速),則走GPU硬件繪制的流程慌洪,否則走CPU軟件繪制的流程顶燕;
繪制流程如下:
DecorView是所有視圖的根視圖,也就是最頂層布局冈爹,它是一個(gè)ViewGroup涌攻。每個(gè)Activity的View繪制流程都是先從DecorView的繪制開始,然后依次遞歸繪制它的子View和子ViewGroup频伤,子ViewGroup再遞歸繪制它包含的子View恳谎,直到所有的View都繪制完成。
這里說說View繪制涉及到的類和方法
從上面源碼分析中可以看出憋肖,View繪制的入口是ViewRootImpl類的performTraversals()方法因痛。在performTraversals()方法中有3個(gè)重要的步驟方法:
(1)performMeasure():該方法主要用于測(cè)量所有View和ViewGroup的大小(寬和高)
(2)performLayout():該方法用于確定所有View和ViewGroup在父布局的位置
(3)performDraw():該方法進(jìn)行具體的繪制操作
performMeasure測(cè)量
1.測(cè)量涉及的方法
performMeasure()主要就是用來測(cè)量View和ViewGroup的寬和高岸更,涉及到的方法有:
(1)View:Measure() —>onMeasure() —>setMeasureDimension() —>getDefaultSize()
View的測(cè)量流程:View的測(cè)量是從Measure()方法開始的鸵膏,在Measure()里面沒有具體的操作,而是直接調(diào)用onMeasure()方法怎炊。在onMeasure()方法里調(diào)用了兩個(gè)getDefaultSize()方法來分別測(cè)量View的寬和高的值谭企。最后調(diào)用setMeasureDimension()方法將View的寬和高測(cè)量值保存下來。
(2)ViewGroup:Measure() —>onMeasure() —>measureChildren() —>measureChild()/measureChildWithMargins() —>Measure() —>onMeasure()—>setMeasureDimension() —>getDefaultSize()评肆。
ViewGroup除了需要測(cè)量自己的寬高大小债查,還需要測(cè)量它包含的子View或子ViewGroup的大小,所以它涉及到的方法除了測(cè)量View需要的方法之外瓜挽,還有measureChildren()方法盹廷,在measureChildren()方法內(nèi)通過循環(huán)調(diào)用measureChild()方法,在measureChild方法中又調(diào)用子View或子ViewGroup的Measure()方法秸抚。
總結(jié):在performMeasure()方法中首先調(diào)用的是頂級(jí)視圖DecorView的Measure()方法速和,在Measure()方法中調(diào)用onMeasure()方法進(jìn)行DecorView的寬高大小測(cè)量。由于DecorView是一個(gè)ViewGroup剥汤,所以在onMeasure()方法內(nèi)又調(diào)用了measureChildren()方法來測(cè)量它的子View和子ViewGroup寬高大小颠放,這樣一步步往下遞歸遍歷,最后測(cè)量出所有的View和ViewGroup的大小吭敢。
2.MeasureSpec
上面描述了測(cè)量View和ViewGroup大小的大致流程碰凶,但是在具體測(cè)量的時(shí)候還涉及到了一個(gè)MeasureSpec類,這個(gè)類是View類的內(nèi)部類,主要內(nèi)容是一個(gè)int型變量欲低,int是32位的辕宏,其中高2位表示模式(Mode),低30位表示大欣场(Size)瑞筐。每一個(gè)View和ViewGroup都有自己的MeasureSpec,具體來說是有2個(gè)MeasureSpec:widthMeasureSpec和heightMeasureSpec腊瑟。這兩個(gè)MeasureSpec從measure()方法開始作為參數(shù)聚假,一直傳遞到getDefaultSize()方法,最后在getDefaultSize()方法里面參考這2個(gè)MeasureSpec來確定具體大小闰非。
MeasureSpec高2位表示的模式有3種:UNSPECIFIED(不指定的)膘格、EXACTLY(確定的)、AT_MOST(至多的)财松。
子View的大小通常是受限于父布局的瘪贱,舉個(gè)例子,假如某個(gè)子View大小設(shè)為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的兩個(gè)MeasureSpec來確定子View的寬高璃谨。
父MeasureSpec和子MeasureSpec的關(guān)系圖如下,上面例子對(duì)應(yīng)下圖的第二行第一列鲤妥。
父/子 | 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 |
上圖總結(jié):
1.當(dāng)子View的LayoutParams指定為具體的尺寸如200*200dp阱冶,那么無論父布局的MeasureSpec模式和大小是什么刁憋,子View的模式都為EXACTLY(確定的),大小就是LayoutParams指定的具體值200木蹬,而不依賴于父布局MeasureSpec指定的大小至耻。
2.當(dāng)子View的LayoutParams指定為match_parent時(shí),子View的MeasureSpec就和父布局的MeasureSpec相同镊叁。
2.當(dāng)子View的LayoutParams指定為wrap_content時(shí)有梆,分兩種情況:當(dāng)父布局MeasureSpec模式為UNSPECIFIED(不指定的),子View的MeasureSpec模式也是UNSPECIFIED(不指定的)意系;當(dāng)父布局MeasureSpec模式為EXACTLY(確定的)和AT_MOST(至多的),子View的MeasureSpec模式都是AT_MOST(至多的)饺汹。無論Mode是哪一種蛔添,子View的MeasureSpec的大小(Size)都是父布局的MeasureSpec中指定的大小兜辞,表示子View大小不應(yīng)該超過父布局大小迎瞧。
只要知道了父布局的MeasureSpec和子View的LayoutParams,那么子View的MeasureSpec也就確定了逸吵,從而能夠進(jìn)一步確定子View的大小凶硅。
ps:由于DecorView是頂級(jí)布局,所以它的MeasureSpec是通過窗口大小和自身的LayoutParams確定的扫皱。
performLayout布局
在performTraversals()方法中足绅,首先調(diào)用performMeasure()方法來測(cè)量所有View和ViewGroup的寬高大小。之后就是調(diào)用performLayout()方法確定每個(gè)View和ViewGroup在其父布局中的位置韩脑。
布局涉及的方法
(1)View:layout()—>setFrame()
View布局流程:View通過調(diào)用layout()方法來確定它在父布局的位置氢妈,具體是在layout()方法內(nèi)調(diào)用setFrame()方法,將該View的上下左右四個(gè)點(diǎn)位置作為參數(shù)傳入段多,從而確定了View在父布局的位置首量。
ViewGroup:layout()—>setFrame()—>onLayout()
ViewGroup布局流程:ViewGroup同樣先調(diào)用layout()方法,通過layout()方法內(nèi)的setFrame()方法設(shè)置自己相對(duì)于父布局的位置进苍。但是由于ViewGroup包含子View或子ViewGroup加缘,所以需要重寫onLayout()方法,在onLayout()方法中遍歷自己的子View或子ViewGroup觉啊,分別調(diào)用它們的layout()方法來完成子View或子ViewGroup的布局拣宏。
總結(jié):在performLayout()方法中首先調(diào)用的是頂級(jí)布局DecorView的Layout()方法,由于DecorView是一個(gè)ViewGroup杠人,所以它調(diào)用的是父類View的layout()方法(原因在下面PS中介紹)蚀浆,并且在layout()方法中確定了自己的位置之后再重寫onLayout()方法來遍歷子View和子ViewGroup缀程,通過調(diào)用View和ViewGroup的layout()方法來確定它們?cè)诟覆季諨ecorView中的位置,這樣一步步往下遞歸遍歷市俊,直到所有的View都確定了在父布局的位置為止杨凑。
PS:
1.ViewGroup的layout()方法是final修飾的,不能被重寫;View的layout()方法是可以被重寫的摆昧。
2.在ViewGroup中l(wèi)ayout()方法中最終調(diào)用的是View的layout()方法撩满。
2.View的onLayout()方法是一個(gè)空方法,這是因?yàn)閂iew沒有子View或子ViewGroup绅你,不需要onLayout()方法伺帘。而ViewGroup的onLayout()方法是一個(gè)抽象方法,即ViewGroup的子類必須重寫這個(gè)方法忌锯,因?yàn)閂iewGroup包含子View或子ViewGroup伪嫁,需要在onLayout()方法中遍歷View或子ViewGroup并調(diào)用它們的layout()方法,從而確定它們?cè)诟覆季种械奈恢谩?/p>
performDraw繪制
在performTraversals()方法中偶垮,首先調(diào)用performMeasure()方法來測(cè)量所有View和ViewGroup的寬高大小张咳。之后就是調(diào)用performLayout()方法確定每個(gè)View和ViewGroup在其父布局中的位置。最后就是調(diào)用performDraw進(jìn)行具體的繪制似舵。
繪制涉及的方法:
View:draw()—>drawBackground()—>onDraw()—>onDrawScrollBars()脚猾。
View繪制流程:首先調(diào)用的是draw()方法,在該方法內(nèi)依次調(diào)用drawBackground()方法來繪制View背景砚哗,調(diào)用onDraw()方法繪制View內(nèi)容龙助,最后調(diào)用onDrawScrollBars()方法繪制裝飾。
ViewGroup:draw()—>drawBackground()—>onDraw()—>dispatchDraw()—>onDrawScrollBars()蛛芥。
ViewGroup繪制流程:在ViewGroup的draw()方法中提鸟,在調(diào)用drawBackground()方法來繪制View背景、調(diào)用onDraw()方法繪制View內(nèi)容之后還需要調(diào)用dispatchDraw()方法來繪制子View和子ViewGroup仅淑,具體是在dispatchDraw()方法內(nèi)循環(huán)遍歷子View和子ViewGroup沽一,并且調(diào)用它們的draw()方法進(jìn)行繪制。
總結(jié):在performDraw()方法中首先調(diào)用頂級(jí)布局DecorView的draw()方法繪制自身漓糙,由于DecorView是一個(gè)ViewGroup铣缠,所以在繪制完自己之后還要調(diào)用dispatchDraw()方法來遍歷繪制它的子View和子ViewGroup。這樣一步步往下遞歸遍歷繪制昆禽,直到所有View和ViewGroup都繪制完成為止蝗蛙。
PS:在View類中的onDraw()方法為空方法,具體的繪制邏輯需要在子類中的onDraw()方法中實(shí)現(xiàn)醉鳖。
渲染
RenderThread渲染
在ViewRootImpl中完成了對(duì)界面的measure捡硅、layout和draw等繪制流程后,用戶依然還是看不到屏幕上顯示的應(yīng)用界面內(nèi)容盗棵,因?yàn)檎麄€(gè)Android系統(tǒng)的顯示流程除了前面講到的UI線程的繪制外壮韭,界面還需要經(jīng)過RenderThread線程的渲染處理北发,渲染完成后,還需要通過Binder調(diào)用“上幀”交給surfaceflinger進(jìn)程中進(jìn)行合成后送顯才能最終顯示到屏幕上喷屋。本小節(jié)中琳拨,我們將接上一節(jié)中ViewRootImpl中最后draw的流程繼續(xù)往下分析開啟硬件加速情況下,RenderThread渲染線程的工作流程屯曹。由于目前Android 4.X之后系統(tǒng)默認(rèn)界面是開啟硬件加速的狱庇,所以本文我們重點(diǎn)分析硬件加速條件下的界面渲染流程,我們先分析一下簡(jiǎn)化的代碼流程:
/*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é)點(diǎn)出發(fā)恶耽,遞歸遍歷View控件樹密任,記錄每個(gè)View節(jié)點(diǎn)的繪制操作命令,完成繪制操作命令樹的構(gòu)建
updateRootDisplayList(view, callbacks);
...
// 2.JNI調(diào)用同步Java層構(gòu)建的繪制命令樹到Native層的RenderThread渲染線程偷俭,并喚醒渲染線程利用OpenGL執(zhí)行渲染任務(wù)浪讳;
int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
...
}
從上面的代碼可以看出,硬件加速繪制主要包括兩個(gè)階段:
1涌萤、從DecorView根節(jié)點(diǎn)出發(fā)淹遵,遞歸遍歷View控件樹,記錄每個(gè)View節(jié)點(diǎn)的drawOp繪制操作命令形葬,完成繪制操作命令樹的構(gòu)建;
2暮的、JNI調(diào)用同步Java層構(gòu)建的繪制命令樹到Native層的RenderThread渲染線程笙以,并喚醒渲染線程利用OpenGL執(zhí)行渲染任務(wù);
構(gòu)建繪制命令樹
我們先來看看第一階段構(gòu)建繪制命令樹的代碼簡(jiǎn)化流程:
/*frameworks/base/core/java/android/view/ThreadedRenderer.java*/
private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
// 原生標(biāo)記構(gòu)建View繪制操作命令樹過程的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
// 遞歸子View的updateDisplayListIfDirty實(shí)現(xiàn)構(gòu)建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é)點(diǎn)出發(fā)冻辩,開始遞歸調(diào)用每個(gè)View樹節(jié)點(diǎn)的updateDisplayListIfDirty函數(shù)
view.updateDisplayListIfDirty();
...
}
/*frameworks/base/core/java/android/view/View.java*/
public RenderNode updateDisplayListIfDirty() {
...
// 1.利用`View`對(duì)象構(gòu)造時(shí)創(chuàng)建的`RenderNode`獲取一個(gè)`SkiaRecordingCanvas`“畫布”猖腕;
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
...
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// 如果僅僅是ViewGroup,并且自身不用繪制恨闪,直接遞歸子View
dispatchDraw(canvas);
...
} else {
// 2.利用SkiaRecordingCanvas倘感,在每個(gè)子View控件的onDraw繪制函數(shù)中調(diào)用drawLine、drawRect等繪制操作時(shí)咙咽,創(chuàng)建對(duì)應(yīng)的DisplayListOp繪制命令老玛,并緩存記錄到其內(nèi)部的SkiaDisplayList持有的DisplayListData中;
draw(canvas);
}
} finally {
// 3.將包含有`DisplayListOp`繪制命令緩存的`SkiaDisplayList`對(duì)象設(shè)置填充到`RenderNode`中钧敞;
renderNode.endRecording();
...
}
...
}
public void draw(Canvas canvas) {
...
// draw the content(View自己實(shí)現(xiàn)的onDraw繪制蜡豹,由應(yīng)用開發(fā)者自己實(shí)現(xiàn))
onDraw(canvas);
...
// draw the children
dispatchDraw(canvas);
...
}
/*frameworks/base/graphics/java/android/graphics/RenderNode.java*/
public void endRecording() {
...
// 從SkiaRecordingCanvas中獲取SkiaDisplayList對(duì)象
long displayList = canvas.finishRecording();
// 將SkiaDisplayList對(duì)象填充到RenderNode中
nSetDisplayList(mNativeRenderNode, displayList);
canvas.recycle();
}
從以上代碼可以看出,構(gòu)建繪制命令樹的過程是從View控件樹的根節(jié)點(diǎn)DecorView觸發(fā)溉苛,遞歸調(diào)用每個(gè)子View節(jié)點(diǎn)的updateDisplayListIfDirty函數(shù)镜廉,最終完成繪制樹的創(chuàng)建,簡(jiǎn)述流程如下:
利用View對(duì)象構(gòu)造時(shí)創(chuàng)建的RenderNode獲取一個(gè)SkiaRecordingCanvas“畫布”愚战;
利用SkiaRecordingCanvas娇唯,在每個(gè)子View控件的onDraw繪制函數(shù)中調(diào)用drawLine齐遵、drawRect等繪制操作時(shí),創(chuàng)建對(duì)應(yīng)的DisplayListOp繪制命令塔插,并緩存記錄到其內(nèi)部的SkiaDisplayList持有的DisplayListData中梗摇;
將包含有DisplayListOp繪制命令緩存的SkiaDisplayList對(duì)象設(shè)置填充到RenderNode中;
最后將根View的緩存DisplayListOp設(shè)置到RootRenderNode中佑淀,完成構(gòu)建留美。
以上整個(gè)構(gòu)建繪制命令樹的過程可以用如下流程圖表示:
執(zhí)行渲染繪制任務(wù)
經(jīng)過上一小節(jié)中的分析,應(yīng)用在UI線程中從根節(jié)點(diǎn)DecorView出發(fā)伸刃,遞歸遍歷每個(gè)子View節(jié)點(diǎn)谎砾,搜集其drawXXX繪制動(dòng)作并轉(zhuǎn)換成DisplayListOp命令,將其記錄到DisplayListData并填充到RenderNode中捧颅,最終完成整個(gè)View繪制命令樹的構(gòu)建景图。從此UI線程的繪制任務(wù)就完成了。下一步UI線程將喚醒RenderThread渲染線程碉哑,觸發(fā)其利用OpenGL執(zhí)行界面的渲染任務(wù)挚币,本小節(jié)中我們將重點(diǎn)分析這個(gè)流程。我們還是先看看這塊代碼的簡(jiǎn)化流程:
/*frameworks/base/graphics/java/android/graphics/HardwareRenderer.java*/
public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) {
// JNI調(diào)用native層的相關(guān)函數(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繪制任務(wù)
return mDrawFrameTask.drawFrame();
}
/*frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp*/
int DrawFrameTask::drawFrame() {
...
postAndWait();
...
}
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
// 向RenderThread渲染線程的MessageQueue消息隊(duì)列放入一個(gè)待執(zhí)行任務(wù)妆毕,以將其喚醒執(zhí)行run函數(shù)
mRenderThread->queue().post([this]() { run(); });
// UI線程暫時(shí)進(jìn)入wait等待狀態(tài)
mSignal.wait(mLock);
}
void DrawFrameTask::run() {
// 原生標(biāo)識(shí)一幀渲染繪制任務(wù)的systrace tag
ATRACE_NAME("DrawFrame");
...
{
TreeInfo info(TreeInfo::MODE_FULL, *mContext);
//1.將UI線程構(gòu)建的DisplayListOp繪制命令樹同步到RenderThread渲染線程
canUnblockUiThread = syncFrameState(info);
...
}
...
// 同步完成后則可以喚醒UI線程
if (canUnblockUiThread) {
unblockUiThread();
}
...
if (CC_LIKELY(canDrawThisFrame)) {
// 2.執(zhí)行draw渲染繪制動(dòng)作
context->draw();
} else {
...
}
...
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
...
// 調(diào)用CanvasContext的prepareTree函數(shù)實(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) {
...
// 遞歸調(diào)用各個(gè)子View對(duì)應(yīng)的RenderNode執(zhí)行prepareTree動(dòng)作
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) {
// 遍歷調(diào)用各個(gè)子View對(duì)應(yīng)的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對(duì)象
mDisplayList = mStagingDisplayList;
mStagingDisplayList = nullptr;
...
}
void CanvasContext::draw() {
...
// 1.調(diào)用OpenGL庫(kù)使用GPU,按照構(gòu)建好的繪制命令完成界面的渲染
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ā)送一個(gè)DrawFrameTask任務(wù)請(qǐng)求贮尖,RenderThread被喚醒笛粘,開始渲染,大致流程如下:
syncFrameState中遍歷View樹上每一個(gè)RenderNode湿硝,執(zhí)行prepareTreeImpl函數(shù)薪前,實(shí)現(xiàn)同步繪制命令樹的操作;
調(diào)用OpenGL庫(kù)API使用GPU关斜,按照構(gòu)建好的繪制命令完成界面的渲染(具體過程示括,由于本文篇幅所限,暫不展開分析)痢畜;
將前面已經(jīng)繪制渲染好的圖形緩沖區(qū)Binder上幀給SurfaceFlinger合成和顯示垛膝;
整個(gè)過程可以用如下流程圖表示:
SurfaceFlinger合成顯示
SurfaceFlinger合成顯示部分完全屬于Android系統(tǒng)GUI中圖形顯示的內(nèi)容,邏輯結(jié)構(gòu)也比較復(fù)雜丁稀,但不屬于本文介紹內(nèi)容的重點(diǎn)繁涂。所以本小節(jié)中只是總體上介紹一下其工作原理與思想,不再詳細(xì)分析源碼二驰,感興趣的讀者可以關(guān)注筆者后續(xù)的文章再來詳細(xì)分析講解扔罪。簡(jiǎn)單的說SurfaceFlinger作為系統(tǒng)中獨(dú)立運(yùn)行的一個(gè)Native進(jìn)程,借用Android官網(wǎng)的描述桶雀,其職責(zé)就是負(fù)責(zé)接受來自多個(gè)來源的數(shù)據(jù)緩沖區(qū)矿酵,對(duì)它們進(jìn)行合成唬复,然后發(fā)送到顯示設(shè)備。如下圖所示:
從上圖可以看出全肮,其實(shí)SurfaceFlinger在Android系統(tǒng)的整個(gè)圖形顯示系統(tǒng)中是起到一個(gè)承上啟下的作用:
對(duì)上:通過Surface與不同的應(yīng)用進(jìn)程建立聯(lián)系敞咧,接收它們寫入Surface中的繪制緩沖數(shù)據(jù),對(duì)它們進(jìn)行統(tǒng)一合成辜腺。
對(duì)下:通過屏幕的后緩存區(qū)與屏幕建立聯(lián)系休建,發(fā)送合成好的數(shù)據(jù)到屏幕顯示設(shè)備。
圖形的傳遞是通過Buffer作為載體评疗,Surface是對(duì)Buffer的進(jìn)一步封裝测砂,也就是說Surface內(nèi)部具有多個(gè)Buffer供上層使用,如何管理這些Buffer呢百匆?答案就是BufferQueue 砌些,下面我們來看看BufferQueue的工作原理:
BufferQueue機(jī)制
借用一張經(jīng)典的圖來描述BufferQueue的工作原理:
BufferQueue是一個(gè)典型的生產(chǎn)者-消費(fèi)者模型中的數(shù)據(jù)結(jié)構(gòu)。在Android應(yīng)用的渲染流程中加匈,應(yīng)用扮演的就是“生產(chǎn)者”的角色存璃,而SurfaceFlinger扮演的則是“消費(fèi)者”的角色,其配合工作的流程如下:
應(yīng)用進(jìn)程中在開始界面的繪制渲染之前雕拼,需要通過Binder調(diào)用dequeueBuffer接口從SurfaceFlinger進(jìn)程中管理的BufferQueue 中申請(qǐng)一張?zhí)幱趂ree狀態(tài)的可用Buffer纵东,如果此時(shí)沒有可用Buffer則阻塞等待;
應(yīng)用進(jìn)程中拿到這張可用的Buffer之后啥寇,選擇使用CPU軟件繪制渲染或GPU硬件加速繪制渲染偎球,渲染完成后再通過Binder調(diào)用queueBuffer接口將緩存數(shù)據(jù)返回給應(yīng)用進(jìn)程對(duì)應(yīng)的BufferQueue(如果是 GPU 渲染的話,這里還有個(gè) GPU處理的過程示姿,所以這個(gè) Buffer 不會(huì)馬上可用甜橱,需要等 GPU 渲染完成的Fence信號(hào))逊笆,并申請(qǐng)sf類型的Vsync以便喚醒“消費(fèi)者”SurfaceFlinger進(jìn)行消費(fèi)栈戳;
SurfaceFlinger 在收到 Vsync 信號(hào)之后,開始準(zhǔn)備合成难裆,使用 acquireBuffer獲取應(yīng)用對(duì)應(yīng)的 BufferQueue 中的 Buffer 并進(jìn)行合成操作子檀;
合成結(jié)束后,SurfaceFlinger 將通過調(diào)用 releaseBuffer將 Buffer 置為可用的free狀態(tài)乃戈,返回到應(yīng)用對(duì)應(yīng)的 BufferQueue中褂痰。
Vsync同步機(jī)制
Vysnc垂直同步是Android在“黃油計(jì)劃”中引入的一個(gè)重要機(jī)制,本質(zhì)上是為了協(xié)調(diào)BufferQueue的應(yīng)用生產(chǎn)者生成UI數(shù)據(jù)動(dòng)作和SurfaceFlinger消費(fèi)者的合成消費(fèi)動(dòng)作症虑,避免出現(xiàn)畫面撕裂的Tearing現(xiàn)象缩歪。Vysnc信號(hào)分為兩種類型:
app類型的Vsync:app類型的Vysnc信號(hào)由上層應(yīng)用中的Choreographer根據(jù)繪制需求進(jìn)行注冊(cè)和接收,用于控制應(yīng)用UI繪制上幀的生產(chǎn)節(jié)奏谍憔。根據(jù)第7小結(jié)中的分析:應(yīng)用在UI線程中調(diào)用invalidate刷新界面繪制時(shí)匪蝙,需要先透過Choreographer向系統(tǒng)申請(qǐng)注冊(cè)app類型的Vsync信號(hào)主籍,待Vsync信號(hào)到來后,才能往主線程的消息隊(duì)列放入待繪制任務(wù)進(jìn)行真正UI的繪制動(dòng)作逛球;
sf類型的Vsync:sf類型的Vsync是用于控制SurfaceFlinger的合成消費(fèi)節(jié)奏千元。應(yīng)用完成界面的繪制渲染后,通過Binder調(diào)用queueBuffer接口將緩存數(shù)據(jù)返還給應(yīng)用對(duì)應(yīng)的BufferQueue時(shí)颤绕,會(huì)申請(qǐng)sf類型的Vsync幸海,待SurfaceFlinger 在其UI線程中收到 Vsync 信號(hào)之后,便開始進(jìn)行界面的合成操作奥务。
Vsync信號(hào)的生成是參考屏幕硬件的刷新周期的物独,其架構(gòu)如下圖所示:
總結(jié)
本文結(jié)合源碼完整的分析了應(yīng)用Activity界面第一幀畫面顯示的完整流程,這其中涉及了App應(yīng)用汗洒、system_server框架议纯、Art虛擬機(jī)、surfaceflinger等一系列Android系統(tǒng)核心模塊的相互配合溢谤,有很多的細(xì)節(jié)也由于篇幅所限無法完全展開分析瞻凤,感興趣的讀者可以結(jié)合AOSP源碼繼續(xù)深入分析。