作者:劉望舒
鏈接:http://www.reibang.com/p/aadfb70f25e2
前言
在此前的系列文章中我們學(xué)習(xí)了WindowManager體系和Window的屬性售貌,這一篇我們接著來(lái)講Window的添加過(guò)程置鼻。建議閱讀此篇文章前先閱讀本系列的前兩篇文章驯妄。
1.概述
WindowManager對(duì)Window進(jìn)行管理,說(shuō)到管理那就離不開對(duì)Window的添加横侦、更新和刪除的操作,在這里我們把它們統(tǒng)稱為Window的操作簿透。對(duì)于Window的操作羹令,最終都是交由WMS來(lái)進(jìn)行處理。窗口的操作分為兩大部分描焰,一部分是WindowManager處理部分媳否,另一部分是WMS處理部分。我們知道Window分為三大類荆秦,分別是:Application Window(應(yīng)用程序窗口)、Sub Windwow(子窗口)和System Window(系統(tǒng)窗口)力图,對(duì)于不同類型的窗口添加過(guò)程會(huì)有所不同步绸,但是對(duì)于WMS處理部分,添加的過(guò)程基本上是一樣的吃媒, WMS對(duì)于這三大類的窗口基本是“一視同仁”的瓤介。
本篇主要會(huì)講解Window的操作的WindowManager處理部分吕喘,至于WMS處理部分會(huì)在后續(xù)的解析WMS系列文章中進(jìn)行講解。
2.系統(tǒng)窗口的添加過(guò)程
三大類窗口的添加過(guò)程會(huì)有所不同刑桑,這里以系統(tǒng)窗口StatusBar為例氯质,StatusBar是SystemUI的重要組成部分,具體就是指系統(tǒng)狀態(tài)欄祠斧,用于顯示時(shí)間闻察、電量和信號(hào)等信息。我們來(lái)查看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);
}
首先通過(guò)創(chuàng)建LayoutParams來(lái)配置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來(lái)保存這些Session。這樣剩下的工作就交給WMS來(lái)處理论泛,在WMS中會(huì)為這個(gè)添加的窗口分配Surface揩尸,并確定窗口顯示次序,可見(jiàn)負(fù)責(zé)顯示界面的是畫布Surface屁奏,而不是窗口本身岩榆。WMS會(huì)將它所管理的Surface交由SurfaceFlinger處理,SurfaceFlinger會(huì)將這些Surface混合并繪制到屏幕上坟瓢。
窗口添加的WMS處理部分會(huì)在后續(xù)介紹WMS的系列文章進(jìn)行講解勇边,系統(tǒng)窗口的添加過(guò)程的時(shí)序圖如下所示。
3.Activity的添加過(guò)程
無(wú)論是哪種窗口折联,它的的添加過(guò)程在WMS處理部分中基本是類似的粒褒,只不過(guò)會(huì)在權(quán)限和窗口顯示次序等方面會(huì)有些不同。但是在WindowManager處理部分會(huì)有所不同诚镰,這里以最典型的應(yīng)用程序窗口Activity為例奕坟,Activity在啟動(dòng)過(guò)程中,如果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)過(guò)程(前篇)這篇文章。ActivityThread管理著當(dāng)前應(yīng)用程序進(jìn)程的線程抠艾,這在Activity的啟動(dòng)過(guò)程中運(yùn)用的很明顯沙合,不了解的請(qǐng)查看Android深入四大組件(一)應(yīng)用程序啟動(dòng)過(guò)程(后篇)這篇文章。當(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中脸狸,此后的過(guò)程在上面的系統(tǒng)窗口的添加過(guò)程已經(jīng)講過(guò)最仑,唯一需要注意的是addView的第一個(gè)參數(shù)是DecorView。
結(jié)語(yǔ)
ViewManager不只定義了addView方法用來(lái)添加窗口炊甲,還定義了updateViewLayout和removeView方法用來(lái)更新和刪除窗口泥彤,如下所示。
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)過(guò)WindowManagerGlobal處理吟吝,最后通過(guò)Session與WMS進(jìn)行跨進(jìn)程通信,將更新和刪除窗口的工作交由WMS來(lái)處理颈娜,這里不會(huì)對(duì)其進(jìn)行介紹剑逃,想了解可以查看源碼或者查看《Android開發(fā)藝術(shù)探索》第八章。
參考資料
《深入理解Android內(nèi)核設(shè)計(jì)思想》第二版
《深入理解Android:卷III》
《Android開發(fā)藝術(shù)探索》