本文基于Android6.0橄唬。WindowManagerService只負(fù)責(zé)窗口管理,并不負(fù)責(zé)View的繪制跟圖層混合,本文就來(lái)分析WMS到底是怎么管理窗口的幢炸。初接觸Android時(shí)感覺(jué):Activity似乎就是Google封裝好的窗口充岛,APP只要合理的啟動(dòng)新的Activity就打開(kāi)了新窗口保檐,這樣理解沒(méi)什么不對(duì),Activity確實(shí)可以看做一種窗口及View的封裝崔梗,不過(guò)從源碼來(lái)看夜只,Activity跟Window還是存在不同。本文主要從窗口的添加流程來(lái)將APP端蒜魄、WMS端扔亥、SurfaceFlinger端三塊串聯(lián)起來(lái),主要說(shuō)一下幾個(gè)方面
- 窗口的分類(lèi):Activity谈为、Dialog旅挤、PopupWindow、Toast等對(duì)應(yīng)窗口的區(qū)別
- Window伞鲫、IWindow 粘茄、WindowState、WindowToken榔昔、AppToken等之間的關(guān)系
- 窗口的添加及Surface申請(qǐng)與Binder傳遞
窗口的分類(lèi)簡(jiǎn)述
在Android系統(tǒng)中驹闰,PopupWindow、Dialog撒会、Activity嘹朗、Toast等都有窗口的概念,但又各有不同诵肛,Android將窗口大致分為三類(lèi):應(yīng)用窗口屹培、子窗口、系統(tǒng)窗口怔檩。其中褪秀,Activity與Dialog屬于應(yīng)用窗口、PopupWindow屬于子窗口薛训,必須依附到其他非子窗口才能存在媒吗,而Toast屬于系統(tǒng)窗口,Dialog可能比較特殊乙埃,從表現(xiàn)上來(lái)說(shuō)偏向于子窗口闸英,必須依附Activity才能存在锯岖,但是從性質(zhì)上來(lái)說(shuō),仍然是應(yīng)用窗口甫何,有自己的WindowToken出吹,不同窗口之間的關(guān)系后面會(huì)更加詳細(xì)的分析,這里有一個(gè)概念即可辙喂。
窗口的添加
Activity并不是View展示的唯一方式捶牢,分析窗口添加流程的話,Activity也并不是最好的例子巍耗,因?yàn)锳ctivity還會(huì)牽扯到AMS的知識(shí)秋麸,這里我們不用Activity,而是用一個(gè)懸浮View的展示來(lái)分析窗口的添加芍锦,代碼入下:
private void addTextViewWindow(Context context){
TextView mview=new TextView(context);
...<!--設(shè)置顏色 樣式-->
<!--關(guān)鍵點(diǎn)1-->
WindowManager mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
<!--關(guān)鍵點(diǎn)2-->
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
wmParams.format = PixelFormat.RGBA_8888;
wmParams.width = 800;
wmParams.height = 800;
<!--關(guān)鍵點(diǎn)3-->
mWindowManager.addView(mview, wmParams);
}
這有三點(diǎn)比較關(guān)鍵竹勉,關(guān)鍵點(diǎn)1:獲取WindowManagerService服務(wù)的代理對(duì)象,不過(guò)對(duì)于Application而言娄琉,獲取到的其實(shí)是一個(gè)封裝過(guò)的代理對(duì)象次乓,一個(gè)WindowManagerImpl實(shí)例,Application 的getSystemService()源碼其實(shí)是在ContextImpl中:有興趣的可以看看APP啟動(dòng)時(shí)Context的創(chuàng)建:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
SystemServiceRegistry類(lèi)用靜態(tài)字段及方法中封裝了一些服務(wù)的代理孽水,其中就包括WindowManagerService
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
static {
...
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx.getDisplay());
}});
...
}
因此context.getApplicationContext().getSystemService()最終可以簡(jiǎn)化為new WindowManagerImpl(ctx.getDisplay())票腰,下面看下WindowManagerImpl的構(gòu)造方法,它有兩個(gè)實(shí)現(xiàn)方法女气,對(duì)于Activity跟Application其實(shí)是有區(qū)別的杏慰,這點(diǎn)后面分析:
public WindowManagerImpl(Display display) {
this(display, null);
}
private WindowManagerImpl(Display display, Window parentWindow) {
mDisplay = display;
mParentWindow = parentWindow;
}
對(duì)于Application采用的是一參構(gòu)造方法,所以其mParentWindow=null炼鞠,這點(diǎn)后面會(huì)有用缘滥,到這里,通過(guò)getService獲取WMS代理的封裝類(lèi)谒主,接著看第二點(diǎn)朝扼,WindowManager.LayoutParams,主要看一個(gè)type參數(shù)霎肯,這個(gè)參數(shù)決定了窗口的類(lèi)型擎颖,這里我們定義成一個(gè)Toast窗口,屬于系統(tǒng)窗口观游,不需要處理父窗口搂捧、子窗口之類(lèi)的事,更容易分析懂缕,最后看關(guān)鍵點(diǎn)3允跑,利用WindowManagerImpl的addView方法添加View到WMS,
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
不過(guò)很明顯WindowManagerImpl最后是委托mGlobal來(lái)進(jìn)行這項(xiàng)操作,WindowManagerGlobal是一個(gè)單利聋丝,一個(gè)進(jìn)程只有一個(gè):
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
接著看WindowManagerGlobal的addView荤崇,對(duì)于添加系統(tǒng)窗口,這里將將代碼精簡(jiǎn)一下潮针,不關(guān)系子窗口等之類(lèi)的邏輯
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
<!--關(guān)鍵點(diǎn)1-->
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
<!--關(guān)鍵點(diǎn)2-->
try {
root.setView(view, wparams, panelParentView);
}
... }
先看關(guān)鍵點(diǎn)1,在向WMS添加View的時(shí)候倚喂,WindowManagerGlobal首先為View新建了一個(gè)ViewRootImpl每篷,ViewRootImpl可以看做也是Window和View之間的通信的紐帶,比如將View添加到WMS端圈、處理WMS傳入的觸摸事件焦读、通知WMS更新窗口大小等、同時(shí)ViewRootImpl也封裝了View的繪制與更新方法等舱权〈;危看一下ViewRootImpl如何通過(guò)setView將視圖添加到WMS的:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
<!--關(guān)鍵點(diǎn)1 -->
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
<!--關(guān)鍵點(diǎn)2 -->
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
...
先看關(guān)鍵點(diǎn)1,這里是先為relayout占一個(gè)位置宴倍,其實(shí)是依靠Handler先發(fā)送一個(gè)Message张症,排在所有WMS發(fā)送過(guò)來(lái)的消息之前,先布局繪制一次鸵贬,之后才會(huì)處理WMS傳來(lái)的各種事件俗他,比如觸摸事件等,畢竟要首先將各個(gè)View的布局阔逼、位置處理好兆衅,才能準(zhǔn)確的處理WMS傳來(lái)的事件。接著看做關(guān)鍵點(diǎn)2嗜浮,這里才是真正添加窗口的地方羡亩,雖然關(guān)鍵點(diǎn)1執(zhí)行在前,但是用的是Handler發(fā)消息的方式來(lái)處理危融,其Runable一定是在關(guān)鍵點(diǎn)2之后執(zhí)行畏铆,接著看關(guān)鍵點(diǎn)2,這里有個(gè)比較重要的對(duì)象mWindowSession與mWindow专挪,兩者都是在ViewRootImpl在實(shí)例化的時(shí)候創(chuàng)建的:
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mWindow = new W(this);
mWindowSession它是通過(guò)WindowManagerGlobal.getWindowSession獲得的一個(gè)Binder服務(wù)代理及志,是App端向WMS發(fā)送消息的通道。相對(duì)的寨腔,mWindow是一個(gè)W extends IWindow.Stub Binder服務(wù)對(duì)象速侈,其實(shí)可以看做是App端的窗口對(duì)象,主要作用是傳遞給WMS迫卢,并作為WMS向APP端發(fā)送消息的通道倚搬,在Android系統(tǒng)中存在大量的這種互為C\S的場(chǎng)景。接著看mWindowSession獲取的具體操作是:首先通過(guò)getWindowManagerService 獲取WMS的代理乾蛤,之后通過(guò)WMS的代理在服務(wù)端open一個(gè)Session每界,并在APP端獲取該Session的代理:
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
<!--關(guān)鍵點(diǎn)1-->
IWindowManager windowManager = getWindowManagerService();
<!--關(guān)鍵點(diǎn)2-->
sWindowSession = windowManager.openSession(***)
...
return sWindowSession;
}
}
看關(guān)鍵點(diǎn)1 :首先要記住sWindowSession是一個(gè)單例的對(duì)象捅僵,之后就可以將getWindowManagerService函數(shù)其實(shí)可以簡(jiǎn)化成下面一句代碼,其實(shí)就是獲得WindowManagerService的代理眨层,之前的WindowManagerImpl都是一個(gè)殼子庙楚,或者說(shuō)接口封裝,并未真正的獲得WMS的代理:
IWindowManager.Stub.asInterface(ServiceManager.getService("window"))
再看關(guān)鍵點(diǎn)2:sWindowSession = windowManager.openSession趴樱,它通過(guò)binder驅(qū)動(dòng)后馒闷,會(huì)通知WMS回調(diào)openSession,打開(kāi)一個(gè)Session返回給APP端叁征,而Session extends IWindowSession.Stub 纳账,很明顯也是一個(gè)Binder通信的Stub端,封裝了每一個(gè)Session會(huì)話的操作捺疼。
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
到這里看到如何獲取Session疏虫,下面就是利用Session來(lái)add一個(gè)窗口:其實(shí)是調(diào)用Session.java的addToDisplayWithoutInputChannel函數(shù)
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
不過(guò)它又反過(guò)來(lái)去調(diào)用WMS的addWindow,繞這么大一圈啤呼,并且APP端IWindowSession還是單例的卧秘,為什么不直接用WMS來(lái)處理呢?疑惑官扣,在WMS中addWindow又做了什么呢斯议,就像名字寫(xiě)的,負(fù)責(zé)添加一個(gè)窗口醇锚,代碼精簡(jiǎn)后如下:
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
...
synchronized(mWindowMap) {
...
<!--關(guān)鍵點(diǎn)1 不能重復(fù)添加-->
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
<!--關(guān)鍵點(diǎn)2 對(duì)于子窗口類(lèi)型的處理 1哼御、必須有父窗口 2,父窗口不能是子窗口類(lèi)型-->
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}}
...
boolean addToken = false;
<!--關(guān)鍵點(diǎn)3 根據(jù)IWindow 獲取WindowToken WindowToken是窗口分組的基礎(chǔ)焊唬,每個(gè)窗口必定有一個(gè)分組-->
WindowToken token = mTokenMap.get(attrs.token);
<!--關(guān)鍵點(diǎn)4對(duì)于Toast類(lèi)系統(tǒng)窗口恋昼,其attrs.token可以看做是null, 如果目前沒(méi)有其他的類(lèi)似系統(tǒng)窗口展示赶促,token仍然獲取不到液肌,仍然要走新建流程-->
if (token == null) {
...
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
}
...
<!--關(guān)鍵點(diǎn)5 新建WindowState,WindowState與窗口是一對(duì)一的關(guān)系鸥滨,可以看做是WMS中與窗口的抽象實(shí)體-->
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
...
if (addToken) {
mTokenMap.put(attrs.token, token);
}
win.attach();
mWindowMap.put(client.asBinder(), win);
...
<!--關(guān)鍵點(diǎn)6-->
addWindowToListInOrderLocked(win, true);
return res;
}
這里有幾個(gè)概念需要先了解下:
- IWindow:APP端窗口暴露給WMS的抽象實(shí)例嗦哆,在ViewRootImpl中實(shí)例化,與ViewRootImpl一一對(duì)應(yīng)婿滓,同時(shí)也是WMS向APP端發(fā)送消息的Binder通道老速。
- WindowState:WMS端窗口的令牌,與IWindow凸主,或者說(shuō)與窗口一一對(duì)應(yīng)橘券,是WMS管理窗口的重要依據(jù)。
- WindowToken:窗口的令牌,其實(shí)也可以看做窗口分組的依據(jù)旁舰,在WMS端锋华,與分組對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)是WindowToken(窗口令牌),而與組內(nèi)每個(gè)窗口對(duì)應(yīng)的是WindowState對(duì)象箭窜,每塊令牌(AppWindowToken毯焕、WindowToken)都對(duì)應(yīng)一組窗口(WindowState),Activity與Dialog對(duì)應(yīng)的是AppWindowToken磺樱,PopupWindow對(duì)應(yīng)的是普通的WindowToken芥丧。
- AppToken:其實(shí)是ActivityRecord里面的IApplicationToken.Stub appToken 代理,也是ActivityClientRecord里面的token坊罢,可以看做Activity在其他服務(wù)(非AMS)的抽象
那么接著關(guān)鍵點(diǎn)1:一個(gè)窗口不能被添加兩次,IWindow是一個(gè)Binder代理擅耽,在WMS端活孩,一個(gè)窗口只會(huì)有一個(gè)IWindow代理,這是由Binder通信機(jī)制保證的乖仇,這個(gè)對(duì)象不能被添加兩次憾儒,否則會(huì)報(bào)錯(cuò)。關(guān)鍵點(diǎn)2乃沙,如果是子窗口的話起趾,父窗口必須已被添加,由于我們分析的是系統(tǒng)Toast窗口警儒,可以先不用關(guān)心训裆;關(guān)鍵點(diǎn)3,WindowManager.LayoutParams中有一個(gè)token字段蜀铲,該字段標(biāo)志著窗口的分組屬性边琉,比如Activity及其中的Dialog是復(fù)用用一個(gè)AppToken,Activity里的PopupWindow復(fù)用一個(gè)IWindow類(lèi)型Token记劝,其實(shí)就是Activity的ViewRootImpl里面創(chuàng)建的IWindow变姨,而對(duì)于我們現(xiàn)在添加的Toast類(lèi)系統(tǒng)窗口,并未設(shè)置其attrs.token厌丑,那即是null定欧,其實(shí)所有的Toast類(lèi)系統(tǒng)窗口的attrs.token都可以看做null,就算不是null怒竿,也會(huì)在WMS被強(qiáng)制設(shè)置為null砍鸠。所以Toast類(lèi)系統(tǒng)窗口必定復(fù)用一個(gè)WindowToken,也可以說(shuō)所有的Toast類(lèi)系統(tǒng)窗口都是位于同一分組耕驰,這也是因?yàn)樵擃?lèi)型系統(tǒng)窗口太常用睦番,而且為所有進(jìn)程服務(wù),直接用一個(gè)WindowToken管理更加快捷,畢竟快速新建與釋放WindowToken也算是一種開(kāi)銷(xiāo)托嚣。假設(shè)到我們添加系統(tǒng)窗口的時(shí)候巩检,沒(méi)有任何系統(tǒng)窗口展示,是獲取不到key=null的WindowToken的示启,要新建WindowToken兢哭,并且添加到全局的TokenMap中,而關(guān)鍵點(diǎn)5夫嗓,其實(shí)就是新建窗口在WMS端的抽象實(shí)例:WindowState迟螺,它同窗口一一對(duì)應(yīng),詳細(xì)記錄了窗口的參數(shù)舍咖、Z順序矩父、狀態(tài)等各種信息,新建只有會(huì)被放入全局的Map中排霉,同時(shí)也會(huì)被附加到相應(yīng)的WindowToken分組中去窍株,到這里APP端向WMS注冊(cè)窗口的流程就算走完了,不過(guò)只算完成了前半部分攻柠,WMS還需要向SurfaceFlinger申請(qǐng)Surface球订,才算完成真正的分配了窗口。在向SurfaceFlinger申請(qǐng)Surface之前瑰钮,WMS端需要獲得SF的代理冒滩,在WindowState對(duì)象創(chuàng)建后會(huì)利用 win.attach()函數(shù)為當(dāng)前APP申請(qǐng)建立SurfaceFlinger的鏈接:
void attach() {
if (WindowManagerService.localLOGV) Slog.v(
mSession.windowAddedLocked();
}
void windowAddedLocked() {
if (mSurfaceSession == null) {
// SurfaceSession新建
mSurfaceSession = new SurfaceSession();
mService.mSessions.add(this);
...
}
mNumWindow++;
}
可以看到SurfaceSession對(duì)于Session來(lái)說(shuō)是單利的,也就是與APP的Seesion一一對(duì)應(yīng)浪谴,SurfaceSession所握著的SurfaceFlinger的代理其實(shí)就是SurfaceComposerClient开睡,其實(shí)現(xiàn)如下:
public SurfaceSession() {
mNativeClient = nativeCreate();
}
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}
Session與APP進(jìn)程是一一對(duì)應(yīng)的,它會(huì)進(jìn)一步為當(dāng)前進(jìn)程建立SurfaceSession會(huì)話苟耻,可以這么理解:Session是APP同WMS通信的通道士八,SurfaceSession是WMS為APP向SurfaceFlinger申請(qǐng)的通信通道,同樣 SurfaceSession與APP也是一一對(duì)應(yīng)的梁呈,既然是同SurfaceFlinger通信的信使婚度,那么SurfaceSession就應(yīng)該握著SurfaceFlinger的代理,其實(shí)就是SurfaceComposerClient里的ISurfaceComposerClient mClient對(duì)象官卡,它是SurfaceFlinger為每個(gè)APP封裝一個(gè)代理蝗茁,也就是 **進(jìn)程 <-> Session <-> SurfaceSession <-> SurfaceComposerClient <-> ISurfaceComposerClient(BpSurfaceComposerClient) **五者是一條線, 為什么不直接與SurfaceFlinger通信呢?大概為了SurfaceFlinger管理每個(gè)APP的Surface比較方便吧寻咒,這四個(gè)類(lèi)的模型如下圖:
至于ISurfaceComposerClient(BpSurfaceComposerClient) 究竟是怎么樣一步步創(chuàng)建的哮翘,其實(shí)它是利用ComposerService這樣一個(gè)單利對(duì)象為為每個(gè)APP在WMS端申請(qǐng)一個(gè)ISurfaceComposerClient對(duì)象,在WMS端表現(xiàn)為BpSurfaceComposerClient毛秘,在SurfaceFlinger端表現(xiàn)為BnSurfaceComposerClient饭寺,具體代碼如下:
SurfaceComposerClient::SurfaceComposerClient()
: mStatus(NO_INIT), mComposer(Composer::getInstance())
{
}
// 單利的阻课,所以只有第一次的時(shí)候采用
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
if (sm != 0) {
sp<ISurfaceComposerClient> conn = sm->createConnection();
if (conn != 0) {
mClient = conn;
mStatus = NO_ERROR;
}
}
}
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
sp<ISurfaceComposerClient> bclient;
sp<Client> client(new Client(this));
status_t err = client->initCheck();
if (err == NO_ERROR) {
bclient = client;
}
return bclient;
}
剛才說(shuō)完成了前半部分,主要針對(duì)WMS的窗口管理艰匙,后半部分則是圍繞Surface的分配來(lái)進(jìn)行的限煞,還記得之前ViewRootImpl在setView時(shí)候分了兩步嗎?雖然先調(diào)用requestLayout先執(zhí)行员凝,但是由于其內(nèi)部利用Handler發(fā)送消息延遲執(zhí)行的署驻,所以可以看做requestLayout是在addWindow之后執(zhí)行的,那么這里就看添加窗口之后健霹,如何分配Surface的旺上,requestLayout函數(shù)調(diào)用里面使用了Hanlder的一個(gè)小手段,那就是利用postSyncBarrier添加了一個(gè)Barrier(擋板)糖埋,這個(gè)擋板的作用是阻塞普通的同步消息的執(zhí)行宣吱,在擋板被撤銷(xiāo)之前,只會(huì)執(zhí)行異步消息瞳别,而requestLayout先添加了一個(gè)擋板Barrier征候,之后自己插入了一個(gè)異步任務(wù)mTraversalRunnable,其主要作用就是保證mTraversalRunnable在所有同步Message之前被執(zhí)行洒试,保證View繪制的最高優(yōu)先級(jí)。具體實(shí)現(xiàn)如下:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
<!--關(guān)鍵點(diǎn)1 添加塞子-->
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
<!--關(guān)鍵點(diǎn)2 添加異步消息任務(wù)-->
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
mTraversalRunnable任務(wù)的主要作用是:如果Surface未分配朴上,則請(qǐng)求分配Surface垒棋,并測(cè)量、布局痪宰、繪圖叼架,其執(zhí)行主體其實(shí)是performTraversals()函數(shù),該函數(shù)包含了APP端View繪制大部分的邏輯, performTraversals函數(shù)很長(zhǎng)衣撬,這里只簡(jiǎn)要看幾個(gè)點(diǎn),其實(shí)主要是關(guān)鍵點(diǎn)1:relayoutWindow:
private void performTraversals() {
final View host = mView;
...
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null) {
<!--關(guān)鍵點(diǎn)1 申請(qǐng)Surface或者重新設(shè)置參數(shù)-->
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
<!--關(guān)鍵點(diǎn)2 測(cè)量-->
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
<!--關(guān)鍵點(diǎn)3 布局-->
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
<!--關(guān)鍵點(diǎn)4 更新window-->
try {
mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
...
<!--關(guān)鍵點(diǎn)5 繪制-->
performDraw();
...
}
relayoutWindow主要是通過(guò)mWindowSession.relayout向WMS申請(qǐng)或者更新Surface如下乖订,這里只關(guān)心一個(gè)重要的參數(shù)mSurface,在Binder通信中mSurface是一個(gè)out類(lèi)型的參數(shù)具练,也就是Surface內(nèi)部的內(nèi)容需要WMS端負(fù)責(zé)填充乍构,并回傳給APP端:
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params, ... mSurface);
...
return relayoutResult;
}
看下到底relayout是如何想SurfaceFlinger申請(qǐng)Surface的。我們知道每個(gè)窗口都有一個(gè)WindowState與其對(duì)應(yīng)扛点,另外每個(gè)窗口也有自己的動(dòng)畫(huà)哥遮,比如入場(chǎng)/出廠動(dòng)畫(huà),而WindowStateAnimator就是與WindowState的動(dòng)畫(huà)陵究,為什么要提WindowStateAnimator眠饮,因?yàn)閃indowStateAnimator是
public int relayoutWindow(Session session, IWindow client, int seq,... Surface outSurface) {
WindowState win = windowForClientLocked(session, client, false);
WindowStateAnimator winAnimator = win.mWinAnimator;
<!--關(guān)鍵點(diǎn)1 -->
SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
if (surfaceControl != null) {
<!--關(guān)鍵點(diǎn)2 -->
outSurface.copyFrom(surfaceControl);
} else {
outSurface.release();
}
這里只看Surface創(chuàng)建代碼,首先通過(guò)windowForClientLocked找到WindowState铜邮,利用WindowState的WindowStateAnimator成員創(chuàng)建一個(gè)SurfaceControl仪召,SurfaceControl會(huì)調(diào)用native函數(shù)nativeCreate(session, name, w, h, format, flags)創(chuàng)建Surface寨蹋,
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
jstring nameStr, jint w, jint h, jint format, jint flags) {
ScopedUtfChars name(env, nameStr);
<!--關(guān)鍵點(diǎn)1-->
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
<!--關(guān)鍵點(diǎn)2-->
sp<SurfaceControl> surface = client->createSurface(
String8(name.c_str()), w, h, format, flags);
surface->incStrong((void *)nativeCreate);
return reinterpret_cast<jlong>(surface.get());
}
關(guān)鍵點(diǎn)1是取到SurfaceSession對(duì)象中SurfaceComposerClient對(duì)象,之后調(diào)用SurfaceComposerClient的createSurface方法進(jìn)一步創(chuàng)建SurfaceControl扔茅,
sp<SurfaceControl> SurfaceComposerClient::createSurface(
const String8& name,
uint32_t w,
uint32_t h,
PixelFormat format,
uint32_t flags)
{
sp<SurfaceControl> sur;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
sp<IGraphicBufferProducer> gbp;
<!--關(guān)鍵點(diǎn)1 獲取圖層的關(guān)鍵信息handle, gbp-->
status_t err = mClient->createSurface(name, w, h, format, flags,
&handle, &gbp);
<!--關(guān)鍵點(diǎn)2 根據(jù)返回的圖層關(guān)鍵信息 創(chuàng)建SurfaceControl對(duì)象-->
if (err == NO_ERROR) {
sur = new SurfaceControl(this, handle, gbp);
}
}
return sur;
}
這里先看mClient->createSurface已旧,SurfaceComposerClient的mClient其實(shí)是一個(gè)BpSurfaceComposerClient對(duì)象,它SurfaceFlinger端Client在WMS端的代理咖摹,因此創(chuàng)建Surface的代碼還是在SurfaceFlinger服務(wù)端的Client對(duì)象中评姨,這里有兩個(gè)關(guān)鍵的變量sp<IBinder> handle與 sp<IGraphicBufferProducer> gbp,前者標(biāo)志在SurfaceFlinger端的圖層萤晴,后者用來(lái)創(chuàng)建GraphicBuffer吐句,兩者類(lèi)型都是IBinder類(lèi)型,同時(shí)也是需要SurfaceFlinger填充的對(duì)象店读,這兩者是一個(gè)圖層對(duì)應(yīng)的最關(guān)鍵的信息:
status_t Client::createSurface(
const String8& name,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp){
...
<!--關(guān)鍵點(diǎn)2 這里并未直接創(chuàng)建 嗦枢,而是通過(guò)發(fā)送了一個(gè)MessageCreateLayer消息-->
sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
name, this, w, h, format, flags, handle, gbp);
mFlinger->postMessageSync(msg);
return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}
Client 并不會(huì)直接新建圖層,而是向SurfaceFlinger發(fā)送一個(gè)MessageCreateLayer消息屯断,通知SurfaceFlinger服務(wù)去執(zhí)行文虏,其handler代碼如下:
class MessageCreateLayer : public MessageBase {
SurfaceFlinger* flinger;
Client* client;
virtual bool handler() {
result = flinger->createLayer(name, client, w, h, format, flags,
handle, gbp);
return true;
}
};
其實(shí)就是調(diào)用SurfaceFlinger的createLayer,創(chuàng)建一個(gè)圖層殖演,到這里才是真正的創(chuàng)建圖層:
status_t SurfaceFlinger::createLayer(
const String8& name,
const sp<Client>& client,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
{
if (int32_t(w|h) < 0) {
return BAD_VALUE;
}
status_t result = NO_ERROR;
sp<Layer> layer;
<!--關(guān)鍵點(diǎn)1 新建不同圖層-->
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceNormal:
result = createNormalLayer(client,
name, w, h, flags, format,
handle, gbp, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceDim:
result = createDimLayer(client,
name, w, h, flags,
handle, gbp, &layer);
break;
default:
result = BAD_VALUE;
break;
}
if (result != NO_ERROR) {
return result;
}
...
}
SurfaceFlinger會(huì)根據(jù)不同的窗口參數(shù)氧秘,創(chuàng)建不同類(lèi)型的圖層,這里只看一下createNormalLayer普通樣式的圖層趴久,
status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
// initialize the surfaces
switch (format) {
case PIXEL_FORMAT_TRANSPARENT:
case PIXEL_FORMAT_TRANSLUCENT:
format = PIXEL_FORMAT_RGBA_8888;
break;
case PIXEL_FORMAT_OPAQUE:
format = PIXEL_FORMAT_RGBX_8888;
break;
}
<!--關(guān)鍵點(diǎn) 1 -->
*outLayer = new Layer(this, client, name, w, h, flags);
status_t err = (*outLayer)->setBuffers(w, h, format, flags);
<!--關(guān)鍵點(diǎn) 2-->
if (err == NO_ERROR) {
*handle = (*outLayer)->getHandle();
*gbp = (*outLayer)->getProducer();
}
return err;
}
可以看到 圖層最終對(duì)應(yīng)的是Layer丸相,這里會(huì)新建一個(gè)Layer對(duì)象,Layer中包含著與這個(gè)圖層對(duì)應(yīng)的Handle及Producer對(duì)象彼棍,Handle可以看做是Surface的唯一性標(biāo)識(shí)灭忠,不過(guò)好像沒(méi)太大的作用,最多是一個(gè)標(biāo)識(shí)座硕,將來(lái)清理的時(shí)候有用弛作。相比之下gbp = (*outLayer)->getProducer()比較重要,它實(shí)際是一個(gè)BufferQueueProducer對(duì)象华匾,關(guān)系到共享內(nèi)存的分配問(wèn)題映琳,后面會(huì)專(zhuān)門(mén)分析,這里到此打住蜘拉,我們終于得到了一個(gè)圖層對(duì)象刊头,到這里之后,我們梳理一下诸尽,圖層如何建立的:
- 首先APP端新建一個(gè)Surface圖層的容器殼子原杂,
- APP通過(guò)Binder通信將這個(gè)Surface的殼子傳遞給WMS,
- WMS為了填充Surface去向SurfaceFlinger申請(qǐng)真正的圖層您机,
- SurfaceFlinger收到WMS請(qǐng)求為APP端的Surface分配真正圖層
- 將圖層相關(guān)的關(guān)鍵信息Handle及Producer傳遞給WMS
Layer建立之后穿肄,SurfaceFlinger會(huì)將圖層標(biāo)識(shí)信息Handle及Producer傳遞給WMS年局,WMS利用這兩者創(chuàng)建一個(gè)SurfaceControl對(duì)象,之后再利用該對(duì)象創(chuàng)建Surface咸产,具體代碼如下:
void getSurface(Surface outSurface) {
outSurface.copyFrom(mSurfaceControl);
}
public void copyFrom(SurfaceControl other) {
long surfaceControlPtr = other.mNativeObject;
long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
synchronized (mLock) {
setNativeObjectLocked(newNativeObject);
}
}
可以看到Surface的拷貝函數(shù)其實(shí)就是直接修改Surface native對(duì)象指針值矢否,native的Surface對(duì)象中包含mGraphicBufferProducer對(duì)象,很重要脑溢,會(huì)被傳遞給APP端僵朗。
static jlong nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz,
jlong surfaceControlNativeObj) {
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
sp<Surface> surface(ctrl->getSurface());
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner);
}
return reinterpret_cast<jlong>(surface.get());
}
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
mSurfaceData = new Surface(mGraphicBufferProducer, false);
}
return mSurfaceData;
}
到這里WMS端Surface創(chuàng)建及填充完畢,并且Surface其實(shí)與WMS的SurfaceControl一一對(duì)應(yīng)屑彻,當(dāng)APP端需要在圖層級(jí)別進(jìn)行操控的時(shí)候验庙,其實(shí)還是要依靠SurfaceControl的,WMS的Surface創(chuàng)建完畢后社牲,需要傳遞給APP端粪薛,之后APP端就獲得直接同SurfaceFlinger通信的能力,比如繪圖與UI更新搏恤,怎傳遞的呢违寿?我們知道Surface實(shí)現(xiàn)了Parcel接口,因此可以傳遞序列化的數(shù)據(jù)熟空,其實(shí)看一下Surface nativeReadFromParcel就知道到底是怎么傳遞的了藤巢,利用readStrongBinder獲取IGraphicBufferProducer對(duì)象的句柄,之后轉(zhuǎn)化為IGraphicBufferProducer代理其實(shí)就是BpGraphicBufferProducer息罗,之后利用BpGraphicBufferProducer構(gòu)建Surface掂咒,這樣APP端Surface就被填充完畢,可以同SurfaceFlinger通信了:
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == NULL) {
doThrowNPE(env);
return 0;
}
sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
sp<IBinder> binder(parcel->readStrongBinder());
if (self != NULL
&& (IInterface::asBinder(self->getIGraphicBufferProducer()) == binder)) {
return jlong(self.get());
}
sp<Surface> sur;
sp<IGraphicBufferProducer> gbp(interface_cast<IGraphicBufferProducer>(binder));
if (gbp != NULL) {
sur = new Surface(gbp, true);
sur->incStrong(&sRefBaseOwner);
}
if (self != NULL) {
self->decStrong(&sRefBaseOwner);
}
return jlong(sur.get());
}
到這里為止阱当,APP<->WMS <->WMS 通信申請(qǐng)Surface的流程算走完了
總結(jié)
窗口的添加流程簡(jiǎn)化如下俏扩,這里暫且忽略窗口的分組管理糜工。
- APP首先去WMS登記窗口
- WMS端登記窗口
- APP新建Surface殼子弊添,請(qǐng)求WMS填充Surface
- WMS請(qǐng)求SurfaceFlinger分配窗口圖層
- SurfaceFlinger分配Layer,將結(jié)果回傳給WMS
- WMS將窗口信息填充到Surface傳輸?shù)紸PP
- APP端獲得填充信息捌木,獲取與SurfaceFlinger通信的能力
作者:看書(shū)的小蝸牛
原文鏈接: WindowManagerService窗口管理之Window添加流程
僅供參考油坝,歡迎指正