四、視圖處理流程
這個部分牽涉到的內容比較多,主要來說分三塊:視圖繪制、視圖渲染、視圖合成,之前的文章也是花了好幾個系列來寫過。那么再好好完整地過一遍吧。
4.1 視圖繪制渲染流程
1)布局加載
梳理布局加載首先需要了解視圖層級關系,如下圖所示:
Activity是系統可視化交互組件,四大組件都由AMS統一管理生命周期,事實上它的職責只是生命周期的管理,由設計模式的單一職責的原則,那勢必需要將Activity和其上的視圖View進行解耦,那么就引入Window的概念,它是應用端的抽象類,對于Activity來說,它的具體實現類是PhoneWindow,在Activity執行attach的時候,會創建了一個PhoneWindow對象。PhoneWindow作為裝載根視圖DecorView的頂級容器,Activity通過setContentView主要干兩件事情:
- 調用PhoneWindow來創建DecorView,DecorView是整個ViewTree的rootView;
- 通過LayoutInflater.inflater,對xml布局做pull解析,創建對應的View對象,形成ViewTree,加載到DecorView的contentView部分。
2)應用程序窗口管理
在應用端,通過WindowManager對Window進行統一管理,WindowManager是一個接口類,繼承自接口ViewManager,負責窗口的管理,例如:增、刪、改。它的實現類是WindowManagerImpl,而具體操作實際上又會交給WindowManagerGlobal來處理,它是個單例,進程唯一。WindowManagerGlobal對每個窗口的DecorView和ViewRootImpl進行統一管理,同時與WMS進行binder call通信。而ViewRootImpl又是WindowManagerGlobal具體操作的執行者。
以addView為例,具體window是由WMS統一管理的,所以這里會進行binder IPC。
binder IPC:
IWindowSession: 應用程序通過Session與WMS通信,并且每個應用程序進程都會對應一個Session。
IWindow: 作為WMS主動與應用程序通信的client端,因為不同的Window是不同的client,因此它也被作為識別window的key。
3)WindowState創建
ViewRootImpl.setView()方法會向WMS請求添加一個Window,mWindowSession.addToDisplay()跨進程最終調用到了WMS.addWindow(): 這里會創建WindowState,它是WMS中描述應用程序窗口的對象。
4)與SurfaceFlinger建立連接(WMS與SurfaceFlinger通信)
WMS.addWindow()中WindowState通過attach方法建立建立一個SurfaceSession走windowAddedLocked()流程與SurfaceFlinger進行連接:
ComPoserService作為client 與 SurfaceFlinger server進行binder IPC , 獲取到SurfaceFlinger創建的Client對象,它相當于是SurfaceFlinger內部對應用程序客戶端的封裝對象,而Client與SurfaceComposerClient又互為binder ipc的兩端,SurfaceComposerClient為client端,Client為server端。
5)創建Surface(WMS分別與應用程序和SurfaceFlinger通信)
Surface是真正UI視圖的載體和處理者。
ViewRootImpl內會new一個Surface,ViewRootImpl 、WindowState與Surface是一一對應關系。
創建Surface主要走的是ViewRootImpl setView操作的requestLayout流程中的relayoutWindow部分:這個過程實際上就是在SurfaceFlinger創建Layer,對應在客戶端通過SurfaceControl在native層創建Surface并copyForm返回給ViewRootImpl內java層的Surface,為什么這么麻煩?因為Surface主要負責調用繪制引擎執行渲染視圖的操作,這部分工作在native效果更高。并且Android4.0之后的硬件加速繪制,渲染過程是又native進程RenderThread來負責的。
三者關系是:
- View是視圖的素材;
- WindowState是窗口實體對象,WMS會通過z-order計算來決定窗口的層級關系,為SurfaceFlinger合成Layer提供層級依據。
- Surface是View的載體和處理者。它會調用繪制引擎處理View,然后申請一塊buffer,存入繪制素材,傳遞給SurfaceFlinger。
6)scheduleTraversals
添加視圖會走ViewRootImpl的setView方法,這個方法有兩個核心,一個與WMS通信創建WindowState,一個是執行scheduleTraversals開始進入繪制流程,這個流程東西比較多,主要分為以下幾個部分:
- 消息屏障
- Choreographer請求vsync信號
- measure
- layout
- draw
下面一一來梳理。
7)消息屏障
Handler中的Message可以分為兩類:同步消息、異步消息。通過MessageQueue.postSyncBarrier(是@hide方法)函數來設置同步屏障,當設置了同步屏障之后,Looper循環中將會忽略所有的同步消息,返回異步消息。換句話說,同步屏障為Handler消息機制增加了一種簡單的優先級機制,異步消息的優先級要高于同步消息。
在scheduleTraversals中postCallback最終發送的消息通過msg.setAsynchronous(true);設置為了異步消息,它的目的其實就是提高UI任務的優先級,讓perfromTraversal提前于所有同步消息執行。
8)Choreographer請求vsync信號
這里有兩點需要先說明:
- 流暢度概念 人肉眼對畫面流暢感的最低要求是:顯卡合成幀速率為60fps,即16.6ms繪1幀的要求。
- vsync Android16.6ms發出一次vsync信號,類似定時中斷,同步整個繪制、渲染、合成流程。但是原則是按需請求,主要請求與接收vsync信號的類是:Choreographer(視圖繪制)、SurfaceFlinger(視圖合成)。
在Android4.1之后增加了Choreographer機制,用于同Vsync機制配合,統一動畫、輸入和繪制時機。在scheduleTraversals會post一個CALLBACK_TRAVERSAL類型的callback,請求vysnc信號,最終執行doFrame回調,來執行performTraversals。performTraversals流程最主要的三個工作就是測量、布局與繪制。
9)performMeasure
先來看測量。在measure中,模式和尺寸通過MeasureSpec來封裝。MeasureSpec代表一個32位int值,高2位代表SpecMode,低30位代表SepcSize. 這樣的打包方式好處是避免過多的對象內存分配。
SpecMode:測量模式
模式 | 描述 |
---|---|
UNSPECIFIED | 父容器不作限制,一般用于系統內部 |
EXACTLY | 精確模式,大小為SpecSize,對應LayoutParams中的match_parent或者具體數值 |
AT_MOST | 最大模式,大小不能大于SpecSize,對應于LayoutParams中的warp_content |
SpecSize:對應某種測量模式下的尺寸大小
這里DecorView的MeasureSpec由窗口尺寸和其自身LayoutParams共同決定,普通View其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同決定。其中,LayoutParams類是用于子視圖向父視圖傳達自己尺寸意愿的一個參數包,包含了Layout的高、寬信息。LayoutParams在LayoutInflater.inflater過程中與View一起被解析成對象,保存在WindowManagerGlobal集合中。
measure過程:
如果是強制測繪或者沒有緩存,執行onMeasure(),否者從緩存中取出數據。View的onMeasure()就是解析寬高的MeasureSpec分別與SuggestedMinimumWidth共同決定其:mMeasuredWidth、mMeasuredHeight。
SuggestedMinimumWidth邏輯:如果View沒有設置背景,那么返回mMinWidth的值,否則返回mMinWidth和背景最小寬度的最大值。而ViewGroup還會執行measureChild,將measure任務傳遞到每個子View處理。
10)performLayout
布局流程相對比較簡單,layout的作用是ViewGroup用來確定子view的位置,當ViewGroup的位置被確定之后,它在onLayout中會遍歷所有子view, 最終通過setFrame來設置每個子View的左上右下。
11)performDraw
視圖繪制過程,這里分為軟件繪制和硬件加速:
軟件繪制由CPU繪制,CPU的特點是擅長串行的復雜邏輯運算,而硬件加速由GPU繪制,GPU擅長大量并行的相同簡單邏輯工作。而視圖的繪制工作明顯屬于后者,因此GPU效率更高,4.0之后,Android默認啟用硬件加速。
軟件繪制走的drawSoftware方法:
- 調用lockCanvas, 實際執行邏輯在native層對應的nativeLockCanvas中,實現:構造SkiaCanvas;向SurfaceFlinger dequeue一塊GraphicBuffer. 前者用于繪制,后者用于存放并傳輸UI視圖元素。
- View.draw(canvas),通過構造的Canvas繪制視圖,最終UI元素保存到申請到的GraphicBuffer,繪制API對應是底層Skia庫。
- 調用unlockCanvasAndPost 實現:queueBuffer給SurfaceFlinger 并且通知其消費。
整個繪制與渲染過程都是在UI Thread完成的,因為現在Android基本不走軟件繪制了,所以具體細節不詳細分析。
而硬件加速繪制走ThreadedRenderer的draw方法
整個硬件加速的繪制與渲染過程簡述如下:
UIThread:
ThreadedRenderer的draw方法實際上就是構建DrawOp樹(里面封裝OpenGL渲染命令)。在Android硬件加速框架中,View視圖被抽象成RenderNode節點,View中的繪制都會被抽象成一個個DrawOp(DisplayListOp),整個繪制過程由CPU完成,遞歸繪制完成后,構建出DisplayList,數據結構如下:
RenderThread:
UIThread將DisplayList同步到RenderThread,RenderThread有一個大loop,繪制操作都以RenderTask的形式post到RenderThread中處理,RenderThread負責渲染,由GPU來處理。渲染核心邏輯:
- OpenGLPipeline::getFrame(): 先通過dequeueBuffer申請一塊GraphicBuffer,
- OpenGLPipeline::draw: 將DisplayList遞歸優化并轉為標準openGL命令,提交給GPU渲染,得到的數據放入申請的GraphicBuffer中。
- OpenGLPipeline::swapBuffers 通過queueBuffer將GraphicBuffer投入BufferQueue ,同時觸發invalidate-sf-vsync請求,通知SurfaceFlinger處理。
4.2 視圖合成與送顯流程
- INVALIDATE:接收invalidate-sf-vsync請求,acquireBuffer更新臟區域、發送refresh-sf-vsync請求。
- REFRESH:接收refresh-sf-vsync請求,按z-order計算可見區域,合成視圖并做柵格化處理,通過FrameBuffer送顯。
視圖處理整體流程:
到這里,從應用安裝,到點擊Laucher應用圖標,最后顯示主頁視圖的整個流程就簡單梳理完了。
可以參看之前的相關文章:
Android圖形系統(一)-Window加載視圖過程
Android圖形系統(二)-DecorView布局加載流程
Android圖形系統(三)-View繪制流程
Android圖形系統(四)-Activity、Window、View關系總結
Android圖形系統(五)-Surface圖形系統概覽
Android圖形系統(六)-app與SurfaceFlinger服務連接過程
Android圖形系統(七)-app請求SurfaceFlinger創建Surface過程
Android圖形系統(八)-app與SurfaceFlinger共享UI元數據過程
Android圖形系統(九)-View、Canvas與Surface的關系
Android圖形系統(十)-SurfaceFlinger啟動及圖層合成送顯過程
Android圖形系統(十一)-Choreographer
Android圖形系統(十二)-流暢度概念
Android圖形系統(十三)-Vsync信號處理
Android9.0 硬件加速(一)-開篇
Android9.0 硬件加速(二)-RenderThread線程的啟動
Android9.0 硬件加速(三)-綁定Surface到RenderThread
Android9.0 硬件加速(四)-UI Thread繪制過程
Android9.0 硬件加速(五) -RenderThread渲染過程
Android WMS(一)-窗口管理
Android WMS(二)-Surface管理
從systrace看app冷啟動過程(一)-應用程序啟動
從systrace看app冷啟動過程(二)-首幀的繪制與渲染
從systrace看app冷啟動過程(三)-首幀的合成與送顯