前言
在此前的系列文章中我們學(xué)習(xí)了WindowManager體系和Window的屬性,這一篇我們接著來講Window的添加過程。建議閱讀此篇文章前先閱讀本系列的前兩篇文章。
1.概述
WindowManager對(duì)Window進(jìn)行管理蓬抄,說到管理那就離不開對(duì)Window的添加、更新和刪除的操作恩掷,在這里我們把它們統(tǒng)稱為Window的操作倡鲸。對(duì)于Window的操作,最終都是交由WMS來進(jìn)行處理黄娘。窗口的操作分為兩大部分峭状,一部分是WindowManager處理部分,另一部分是WMS處理部分逼争。我們知道Window分為三大類优床,分別是:Application Window(應(yīng)用程序窗口)、Sub Windwow(子窗口)和System Window(系統(tǒng)窗口)誓焦,對(duì)于不同類型的窗口添加過程會(huì)有所不同胆敞,但是對(duì)于WMS處理部分,添加的過程基本上是一樣的杂伟, WMS對(duì)于這三大類的窗口基本是“一視同仁”的移层。
本篇主要會(huì)講解Window的操作的WindowManager處理部分,至于WMS處理部分會(huì)在后續(xù)的解析WMS系列文章中進(jìn)行講解赫粥。
2.系統(tǒng)窗口的添加過程
三大類窗口的添加過程會(huì)有所不同观话,這里以系統(tǒng)窗口StatusBar為例,StatusBar是SystemUI的重要組成部分越平,具體就是指系統(tǒng)狀態(tài)欄频蛔,用于顯示時(shí)間灵迫、電量和信號(hào)等信息。我們來查看StatusBar的實(shí)現(xiàn)類PhoneStatusBar的addStatusBarWindow方法晦溪,這個(gè)方法負(fù)責(zé)為StatusBar添加Window瀑粥,如下所示。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
private void addStatusBarWindow() {
makeStatusBarView();//1
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
mHeadsUpManager);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());//2
}
注釋1處用于構(gòu)建StatusBar的視圖三圆。在注釋2處調(diào)用了StatusBarWindowManager的add方法狞换,并將StatusBar的視圖(StatusBarWindowView)和StatusBar的傳進(jìn)去。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
public void add(View statusBarView, int barHeight) {
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
barHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,//1
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp);//2
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
}
首先通過創(chuàng)建LayoutParams來配置StatusBar視圖的屬性嫌术,包括Width哀澈、Height、Type度气、 Flag、Gravity膨报、SoftInputMode等磷籍,不了Window屬性的請(qǐng)查看Android解析WindowManager(二)Window的屬性這篇文章。 關(guān)鍵在注釋1處现柠,設(shè)置了TYPE_STATUS_BAR院领,表示StatusBar視圖的窗口類型是狀態(tài)欄。在注釋2處調(diào)用了WindowManager的addView方法够吩,addView方法定義在WindowManager的父類接口ViewManager中比然,而實(shí)現(xiàn)addView方法的則是WindowManagerImpl中,如下所示周循。
frameworks/base/core/java/android/WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
在WindowManagerImpl的addView方法中强法,接著會(huì)調(diào)用WindowManagerGlobal的addView方法:
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...//參數(shù)檢查
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);//1
} else {
...
}
ViewRootImpl root;
View panelParentView = null;
...
root = new ViewRootImpl(view.getContext(), display);//2
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);//3
mParams.add(wparams);
}
try {
root.setView(view, wparams, panelParentView);//4
} catch (RuntimeException e) {
...
}
}
首先會(huì)對(duì)參數(shù)view、params和display進(jìn)行檢查湾笛。注釋1處饮怯,如果當(dāng)前窗口要作為子窗口,就會(huì)根據(jù)父窗口對(duì)子窗口的WindowManager.LayoutParams類型的wparams對(duì)象進(jìn)行相應(yīng)調(diào)整嚎研。注釋2處創(chuàng)建了ViewRootImp并賦值給root蓖墅,緊接著在注釋3處將root存入到ArrayList<ViewRootImpl>
類型的mRoots中,除了mRoots临扮,mViews和mParams也是ArrayList類型的论矾,分別用于存儲(chǔ)窗口的view對(duì)象和WindowManager.LayoutParams類型的wparams對(duì)象。注釋4處調(diào)用了ViewRootImpl的setView方法。
ViewRootImpl身負(fù)了很多職責(zé):
- View樹的根并管理View樹
- 觸發(fā)View的測(cè)量、布局和繪制
- 輸入事件的中轉(zhuǎn)站
- 管理Surface
- 負(fù)責(zé)與WMS進(jìn)行進(jìn)程間通信
frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
...
}
setView方法中有很多邏輯罕偎,這里只截取了一小部分降淮,主要就是調(diào)用了mWindowSession的addToDisplay方法。mWindowSession是IWindowSession類型的哈踱,它是一個(gè)Binder對(duì)象箩退,用于進(jìn)行進(jìn)程間通信丑罪,IWindowSession是Client端的代理传黄,它的Server端的實(shí)現(xiàn)為Session杰扫,此前包含ViewRootImpl在內(nèi)的代碼邏輯都是運(yùn)行在本地進(jìn)程的,而Session的addToDisplay方法則運(yùn)行在WMS所在的進(jìn)程膘掰。
frameworks/base/services/core/java/com/android/server/wm/Session.java
@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);
}
addToDisplay方法中會(huì)調(diào)用了WMS的addWindow方法章姓,并將自身也就是Session,作為參數(shù)傳了進(jìn)去识埋,每個(gè)應(yīng)用程序進(jìn)程都會(huì)對(duì)應(yīng)一個(gè)Session凡伊,WMS會(huì)用ArrayList來保存這些Session。這樣剩下的工作就交給WMS來處理窒舟,在WMS中會(huì)為這個(gè)添加的窗口分配Surface系忙,并確定窗口顯示次序,可見負(fù)責(zé)顯示界面的是畫布Surface惠豺,而不是窗口本身银还。WMS會(huì)將它所管理的Surface交由SurfaceFlinger處理,SurfaceFlinger會(huì)將這些Surface混合并繪制到屏幕上洁墙。
窗口添加的WMS處理部分會(huì)在后續(xù)介紹WMS的系列文章進(jìn)行講解蛹疯,系統(tǒng)窗口的添加過程的時(shí)序圖如下所示。
3.Activity的添加過程
無論是哪種窗口热监,它的的添加過程在WMS處理部分中基本是類似的捺弦,只不過會(huì)在權(quán)限和窗口顯示次序等方面會(huì)有些不同。但是在WindowManager處理部分會(huì)有所不同孝扛,這里以最典型的應(yīng)用程序窗口Activity為例列吼,Activity在啟動(dòng)過程中,如果Activity所在的進(jìn)程不存在則會(huì)創(chuàng)建新的進(jìn)程疗琉,創(chuàng)建新的進(jìn)程之后就會(huì)運(yùn)行代表主線程的實(shí)例ActivityThread冈欢,不了解的請(qǐng)查看Android應(yīng)用程序進(jìn)程啟動(dòng)過程(前篇)這篇文章。ActivityThread管理著當(dāng)前應(yīng)用程序進(jìn)程的線程盈简,這在Activity的啟動(dòng)過程中運(yùn)用的很明顯凑耻,不了解的請(qǐng)查看Android深入四大組件(一)應(yīng)用程序啟動(dòng)過程(后篇)這篇文章。當(dāng)界面要與用戶進(jìn)行交互時(shí)柠贤,會(huì)調(diào)用ActivityThread的handleResumeActivity方法香浩,如下所示。
frameworks/base/core/java/android/app/ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r = performResumeActivity(token, clearHide, reason);//1
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();//2
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);//3
}
...
}
注釋1處的performResumeActivity方法最終會(huì)調(diào)用Activity的onResume方法臼勉。在注釋2處得到ViewManager類型的wm對(duì)象邻吭,在注釋3處調(diào)用了wm的addView方法,而addView方法的實(shí)現(xiàn)則是在WindowManagerImpl中宴霸,此后的過程在上面的系統(tǒng)窗口的添加過程已經(jīng)講過囱晴,唯一需要注意的是addView的第一個(gè)參數(shù)是DecorView膏蚓。
結(jié)語
ViewManager不只定義了addView方法用來添加窗口,還定義了updateViewLayout和removeView方法用來更新和刪除窗口畸写,如下所示驮瞧。
package android.view;
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
其定義的updateViewLayout和removeView方法的處理流程和addView方法是類似的,都是要經(jīng)過WindowManagerGlobal處理枯芬,最后通過Session與WMS進(jìn)行跨進(jìn)程通信论笔,將更新和刪除窗口的工作交由WMS來處理,這里不會(huì)對(duì)其進(jìn)行介紹千所,想了解可以查看源碼或者查看《Android開發(fā)藝術(shù)探索》第八章狂魔。
參考資料
《深入理解Android內(nèi)核設(shè)計(jì)思想》第二版
《深入理解Android:卷III》
《Android開發(fā)藝術(shù)探索》