本文是Android檢視層原始碼分析系列第二篇文章,通過上一篇文章深入剖析Window組成已經瞭解到WindowManager.addView(contentView)
最終會交給WindowManagerService
處理,WindowManagerService
會建立這個contentView
相關的UI載體Surface
,本文的目的就是理清Surface
的建立過程,以及它與SurfaceFlinger
的關係。
文章比較長,但希望你可以堅持看完,相信會有一定收穫的
本文是基於Google Android Repo中的較新的原始碼分析的。Android UI
渲染這一塊變化還是比較大的,邏輯可能和網上大部分文章有一些出入。
在本文正式開始之前,我們先來了解一下與SurfaceFlinger相關的基礎知識:
- 與SurfaceFlinger相關的基礎知識
SurfaceFlinger
可以說是Android UI
渲染體系的核心,在Android系統啟動時會啟動SurfaceFlinger
服務,它的主要作用就是被Android應用程式呼叫,把繪製(測量,佈局,繪製)後的視窗(Surface)渲染到手機螢幕上。所以整個
UI重新整理過程可能像下面這張圖:
當然實際上的UI渲染原理可沒有像上圖那樣這麼簡單。為了接下來在原始碼追蹤的過程中不迷路,我們帶著下面幾個問題來開始分析:
- WindowManagerService是如何管理Window的?
- Surface是如何建立的?
- SurfaceFlinger是如何管理多個應用的UI渲染的?
- SurfaceFlinger中UI渲染的基本單元是什麼?
ok,接下來就開始Android UI顯示原理之Surface的建立
的主要流程分析。由於整個體系的原始碼流程很複雜,因此在追蹤原始碼時我只貼了一些整個流程分析中的主要節點,並且加了一些註釋。
在上一篇文章中我們知道,ViewRootImpl
管理著整個view tree。
對於ViewRootImpl.setView()
,我們可以簡單的把它當做一個UI渲染操作
的入口,因此我們就從這個方法開始看:
WindowManagerService對於Window的管理
ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
//mWindowSession是一個aidl,ViewRootImpl利用它來和WindowManagerService互動
//mWindow是一個aidl,WindowManagerService可以利用這個物件與服務端互動
//mAttachInfo可以理解為是一個data bean,可以跨程式傳遞
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
...
}
複製程式碼
即ViewRootImpl.setView()
方法會向WindowManagerService
請求新增一個Window
,mWindowSession.addToDisplay()
跨程式最終呼叫到了WindowManagerService.addWindow()
:
WindowManagerService.java
public int addWindow(Session session, IWindow client...) {
...
//WindowState用來描述一個Window
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
...
win.attach(); //會建立一個SurfaceSession
mWindowMap.put(client.asBinder(), win); //mWindowMap是WindowManagerService用來儲存當前所有Window新的的集合
...
win.mToken.addWindow(win); //一個token下會有多個win state。 其實token與PhoneWindow是一一對應的。
...
}
複製程式碼
WindowState
是WindowManagerService
用來描述應用程式的一個Window
的物件。上面註釋我標註了win.attach()
,這個方法可以說是Window
與SurfaceFlinger
連結的起點,它最終會呼叫到Session.windowAddedLocked()
:
Session.java
void windowAddedLocked(String packageName) {
...
if (mSurfaceSession == null) {
...
mSurfaceSession = new SurfaceSession();
...
}
}
//SurfaceSession類的構造方法
public final class SurfaceSession {
private long mNativeClient; // SurfaceComposerClient*
public SurfaceSession() {
mNativeClient = nativeCreate();
}
複製程式碼
這裡呼叫了native方法nativeCreate()
,這個方法其實是返回了一個SurfaceComposerClient指標
。那這個物件是怎麼建立的呢?
SurfaceComposerClient的建立
android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient(); //建構函式其實並沒有做什麼
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}
複製程式碼
即構造了一個SurfaceComposerClient
物件。並返回它的指標。這個物件一個應用程式就有一個,它是應用程式與SurfaceFlinger
溝通的橋樑,為什麼這麼說呢?在SurfaceComposerClient指標
第一次使用時會呼叫下面這個方法:
//這個方法在第一次使用SurfaceComposerClient的指標的時候會呼叫
void SurfaceComposerClient::onFirstRef() {
....
sp<ISurfaceComposerClient> conn;
//sf 就是SurfaceFlinger
conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) :
sf->createConnection();
...
}
複製程式碼
即通過SurfaceFlinger(它本身具有跨程式通訊的能力)
建立了一個ISurfaceComposerClient
物件:
SurfaceFlinger.cpp
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
return initClient(new Client(this)); //initClient這個方法其實並沒有做什麼,
}
複製程式碼
即構造了一個Client
物件,Client
實現了ISurfaceComposerClient
介面。是一個可以跨程式通訊的aidl物件。即SurfaceComposerClient
可以通過它來和SurfaceFlinger
通訊。除此之外它還可以建立Surface
,並且維護一個應用程式的所有Layer(下文會分析到它是什麼)
。它是一個十分重要的物件,我們先來看一下它的組成,它所涉及的其他東西在下文分析中都會講到:
Client.h
class Client : public BnSurfaceComposerClient
{
public:
...
void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
void detachLayer(const Layer* layer);
...
private:
// ISurfaceComposerClient interface。 gbp很重要,它維護這一個應用程式的渲染 Buffer佇列
virtual status_t createSurface(...sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp);
virtual status_t destroySurface(const sp<IBinder>& handle);
//跨程式通訊方法
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
...
// constant
sp<SurfaceFlinger> mFlinger;
// protected by mLock
DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers; // 一個應用程式的所有Layer
...
};
複製程式碼
經過上面這一頓原始碼分析,我們大概知道了ViewRootImpl.setView()
所引發的主要操作:
WindowManagerService
建立了一個WindowState
。用來表示客戶端的一個Window
WindowManagerService
建立了一個SurfaceSession
,SurfaceSession
會與SurfaceFlinger
構建連結,建立了一個SurfaceComposerClient
物件,一個應用程式只具有一個這個物件。SurfaceComposerClient
建立了一個Client
, 這個物件十分重要,它維護這應用程式的渲染核心資料,並負責與SurfaceFlinger
通訊。
如下圖:
經過上面的步驟,應用程式的ViewRootImpl
已經被WindowManagerService
識別,並且應用程式已經與SurfaceFlinger
建立連線。即建立了SurfaceComposerClient
和Client
物件
文章開始就已經說了Surface
是Window(ViewRootImpl)
的UI載體,那Surface
是在哪裡建立的呢?
Surface的建立
其實一個ViewRootImpl
就對應一個Surface
。這點可以通過ViewRootImpl
的原始碼看出:
ViewRootImpl.java
public final Surface mSurface = new Surface();
複製程式碼
即ViewRootImpl
在構造的時候就new 了一個 Surface
。但其實這個新new的Surface
並沒有什麼邏輯,它的建構函式是空的。那麼Surface
到底是在哪裡建立的呢?
觸發Surface的建立操作
我們還是回到ViewRootImpl.setView()
來看一下:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout(); //susion 請求layout。先新增到待渲染佇列中
...
res = mWindowSession.addToDisplay(mWindow, ...); //WindowManagerService會建立mWindow對應的WindowState
...
}
複製程式碼
即在向WindowManagerService
請求建立WindowState
之前,呼叫了requestLayout()
,這個方法會引起ViewRootImpl
所管理的整個view tree的重新渲染。它最終會呼叫到scheduleTraversals()
:
void scheduleTraversals() {
...
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
複製程式碼
對於Choreographer
本文不做詳細的分析,可以去參考 Android Choreographer 原始碼分析的分析來了解這個類。
scheduleTraversals()
會通過Choreographer
來post一個mTraversalRunnable
,Choreographer
接收顯示系統的時間脈衝(垂直同步訊號-VSync訊號,16ms發出一次),在下一個frame渲染時控制執行這個mTraversalRunnable
。
但是mTraversalRunnable
的執行至少要在應用程式與SurfaceFlinger
建立連線之後。這是因為渲染操作是由SurfaceFlinger
負責排程了,如果應用程式還沒有與SurfaceFlinger
建立連線,那SurfaceFlinger
當然不會渲染這個應用程式。所以在執行完mWindowSession.addToDisplay(mWindow, ...)
之後,才會執行mTraversalRunnable
:
ViewRootImpl.java
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
複製程式碼
doTraversal()
會呼叫到ViewRootImpl.performTraversals()
,大部分同學可能知道這個方法是一個view tree
的measure/layout/draw
的控制方法:
private void performTraversals() {
finalView host = mView; //mView是一個Window的根View,對於Activity來說就是DecorView
...
relayoutWindow(params, viewVisibility, insetsPending);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
...
}
複製程式碼
Surface
的具體建立就由relayoutWindow(params, viewVisibility, insetsPending)
這個方法來完成的。這個方法會通過IPC
呼叫到WindowManagerService.relayoutWindow()
:
ViewRootImpl.java
private int relayoutWindow(WindowManager.LayoutParams params, ...) throws RemoteException {
...
int relayoutResult = mWindowSession.relayout(mWindow,..., mSurface);
...
}
複製程式碼
上面我省略了mWindowSession.relayout()
方法的很多引數,不過有一個十分重要的引數我沒有省略,就是mSurface
。前面已經分析了它就是一個空的Surface
物件。其實:
真正的Surface建立是由SurfaceControl
完成的,應用程式ViewRootImpl
的Surface
只是一個指標,指向這個Surface
下面就來看一下SurfaceControl
是如何建立Surface
的:
mWindowSession.relayout()
會呼叫到WindowManagerService.relayoutWindow()
:
WindowManagerService.java
//這裡的outSurface其實就是ViewRootImpl中的那個Surface
public int relayoutWindow(Session session, IWindow client....Surface outSurface){
...
result = createSurfaceControl(outSurface, result, win, winAnimator);
...
}
private int createSurfaceControl(Surface outSurface, int result, WindowState win,WindowStateAnimator winAnimator) {
...
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
...
surfaceController.getSurface(outSurface);
}
複製程式碼
winAnimator.createSurfaceLocked
實際上是建立了一個SurfaceControl
。即上面是先構造SurfaceControl
,然後在構造Surface
。
SurfaceControl的建立
winAnimator.createSurfaceLocked
其實是通過SurfaceControl
的建構函式建立了一個SurfaceControl
物件,這個物件的作用其實就是負責維護Surface
,Surface
其實也是由這個物件負責建立的,我們看一下這個物件的構造方法:
SurfaceControl.java
long mNativeObject; //成員指標變數,指向native建立的SurfaceControl
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
SurfaceControl parent, int windowType, int ownerUid){
...
mNativeObject = nativeCreate(session, name, w, h, format, flags,
parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
...
}
複製程式碼
即呼叫了nativeCreate()
並返回一個SurfaceControl
指標:
android_view_SurfaceControl.cpp
static jlong nativeCreate(JNIEnv* env, ...) {
//這個client其實就是前面建立的SurfaceComposerClinent
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
sp<SurfaceControl> surface; //建立成功之後,這個指標會指向新建立的SurfaceControl
status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
...
return reinterpret_cast<jlong>(surface.get()); //返回這個SurfaceControl的地址
}
複製程式碼
即呼叫到SurfaceComposerClient.createSurfaceChecked()
:
SurfaceComposerClient.cpp
//outSurface會指向新建立的SurfaceControl
status_t SurfaceComposerClient::createSurfaceChecked(...sp<SurfaceControl>* outSurface..)
{
sp<IGraphicBufferProducer> gbp; //這個物件很重要
...
err = mClient->createSurface(name, w, h, format, flags, parentHandle, windowType, ownerUid, &handle, &gbp);
if (err == NO_ERROR) {
//SurfaceControl建立成功, 指標賦值
*outSurface = new SurfaceControl(this, handle, gbp, true);
}
return err;
}
複製程式碼
上面這個方法實際上是呼叫Client.createSurface()
來建立一個Surface
。在建立時有一個很重要的引數sp<IGraphicBufferProducer> gbp
,在下面原始碼分析中我們也要重點注意它。這是因為應用所渲染的每一幀,實際上都會新增到IGraphicBufferProducer
中,來等待SurfaceFlinger
的渲染。這個過程我們在下一篇文章中會分析,我們先繼續來看一下Surface
是如何被Client
建立的:
Client.cpp
status_t Client::createSurface(...)
{
...
//gbp 直接透傳到了SurfaceFlinger
return mFlinger->createLayer(name, this, w, h, format, flags, windowType, ownerUid, handle, gbp, &parent);
}
複製程式碼
??? 不是說好的要建立Surface
呢?怎麼變成mFlinger->createLayer()
? 額,我也不明白這個突然轉變,不過我們只要知道:
Surface
在SurfaceFlinger
中對應的實體其實是Layer
我們繼續看一下mFlinger->createLayer()
SurfaceFlinger.cpp
status_t SurfaceFlinger::createLayer(const String8& name,const sp<Client>& client...)
{
status_t result = NO_ERROR;
sp<Layer> layer; //將要建立的layer
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceNormal:
result = createBufferLayer(client,
uniqueName, w, h, flags, format,
handle, gbp, &layer); // 注意gbp,這時候還沒有構造呢!
break;
... //Layer 分為好幾種,這裡不全部列出
}
...
result = addClientLayer(client, *handle, *gbp, layer, *parent); //這個layer和client相關聯, 新增到Client的mLayers集合中。
...
return result;
}
複製程式碼
從SurfaceFlinger.createLayer()
方法可以看出Layer
分為好幾種。我們這裡只對普通的BufferLayer
的建立做一下分析,看createBufferLayer()
:
status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client... sp<Layer>* outLayer)
{
...
sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags);
status_t err = layer->setBuffers(w, h, format, flags); //設定layer的寬高
if (err == NO_ERROR) {
*handle = layer->getHandle(); //建立handle
*gbp = layer->getProducer(); //建立 gbp IGraphicBufferProducer
*outLayer = layer; //把新建的layer的指標拷貝給outLayer,這樣outLayer就指向了新建的BufferLayer
}
return err;
}
複製程式碼
前面我說過IGraphicBufferProducer(gbp)
是一個很重要的物件,它涉及到SurfaceFlinger
的渲染邏輯,下面我們就看一下這個物件的建立邏輯:
IGraphicBufferProducer(gbp)的建立
BufferLayer.cpp
sp<IGraphicBufferProducer> BufferLayer::getProducer() const {
return mProducer;
}
複製程式碼
即mProducer
其實是Layer
的成員變數,它的建立時機是Layer
第一次被使用時:
void BufferLayer::onFirstRef() {
...
BufferQueue::createBufferQueue(&producer, &consumer, true);
mProducer = new MonitoredProducer(producer, mFlinger, this);
...
}
複製程式碼
所以mProducer
的例項是MonitoredProducer
,但其實它只是一個裝飾類,它實際功能都委託給構造它的引數producer
:
BufferQueue.cpp
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
...
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); //注意這個consumer
...
*outProducer = producer;
*outConsumer = consumer;
}
複製程式碼
所以實際實現mProducer
的工作的queueProducer
是BufferQueueProducer
。
所以構造一個SurfaceControl
所做的工作就是建立了一個SurfaceControl
,並讓SurfaceFlinger
建立了一個對應的Layer
,Layer
中有一個IGraphicBufferProducer
,它的例項是BufferQueueProducer
。
可以用下面這個圖來描述SurfaceControl
的建立過程:
從SurfaceControl中獲取Surface
我們回看WindowManagerService.createSurfaceControl()
, 來看一下java層的Surface
物件到底是個什麼:
WindowManagerService.java
private int createSurfaceControl(Surface outSurface, int result, WindowState win,WindowStateAnimator winAnimator) {
...
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
...
surfaceController.getSurface(outSurface);
}
複製程式碼
上面我們已經瞭解了winAnimator.createSurfaceLocked
的整個過程,我們看一下surfaceController.getSurface(outSurface)
, surfaceController
是WindowSurfaceController
的例項:
//WindowSurfaceController.java
void getSurface(Surface outSurface) {
outSurface.copyFrom(mSurfaceControl);
}
//Surface.java
public void copyFrom(SurfaceControl other) {
...
long surfaceControlPtr = other.mNativeObject;
...
long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
...
mNativeObject = ptr; // mNativeObject指向native建立的Surface
}
複製程式碼
即Surface.copyFrom()
方法呼叫nativeGetFromSurfaceControl()
來獲取一個指標,這個指標是根據前面建立的SurfaceControl
的指標來尋找的,即傳入的引數surfaceControlPtr
:
android_view_Surface.cpp
static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj)); //把java指標轉化內native指標
sp<Surface> surface(ctrl->getSurface()); //直接構造一個Surface,指向 ctrl->getSurface()
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner); //強引用
}
return reinterpret_cast<jlong>(surface.get());
}
複製程式碼
這裡的ctrl
指向前面建立的SurfaceControl
,繼續追溯ctrl->getSurface()
:
SurfaceControl.cpp
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
return generateSurfaceLocked();
}
return mSurfaceData;
}
sp<Surface> SurfaceControl::generateSurfaceLocked() const
{
//這個mGraphicBufferProducer其實就是上面分析的BufferQueueProducer
mSurfaceData = new Surface(mGraphicBufferProducer, false);
return mSurfaceData;
}
複製程式碼
即直接new了一個nativie的Surface
返回給java層,java層的Surface
指向的就是native層的Surface
。
所以Surface
的實際建立可以用下圖表示:
經過上面這個圖,也可以理解SurfaceControl
為什麼叫SurfaceControl
了。
本文到這裡已經很長了,對於UI的渲染過程:即ViewRootImpl是如何渲染到Surface到下一篇文章再分析。
歡迎關注我的Android進階計劃看更多幹貨
歡迎關注我的微信公眾號:susion隨心
參考文章: