相關文章
Android系統啓動系列
Android深刻四大組件系列
Android應用進程啓動過程系列
Android解析WindowManager系列html
在本系列的上一篇文章中,咱們學習了WMS的誕生,WMS被建立後,它的重要的成員有哪些?Window添加過程的WMS部分作了什麼呢?這篇文章會給你解答。java
所謂WMS的重要成員是指WMS中的重要的成員變量,以下所示。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.javaandroid
final WindowManagerPolicy mPolicy;
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final AppOpsManager mAppOps;
final DisplaySettings mDisplaySettings;
...
final ArraySet<Session> mSessions = new ArraySet<>();
final WindowHashMap mWindowMap = new WindowHashMap();
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
WindowState[] mPendingRemoveTmp = new WindowState[20];
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
...
final H mH = new H();
...
final WindowAnimator mAnimator;
...
final InputManagerService mInputManager複製代碼
這裏列出了WMS的部分紅員變量,下面分別對它們進行簡單的介紹。windows
mPolicy:WindowManagerPolicy
WindowManagerPolicy(WMP)類型的變量。WindowManagerPolicy是窗口管理策略的接口類,用來定義一個窗口策略所要遵循的通用規範,並提供了WindowManager全部的特定的UI行爲。它的具體實現類爲PhoneWindowManager,這個實現類在WMS建立時被建立。WMP容許定製窗口層級和特殊窗口類型以及關鍵的調度和佈局。session
mSessions:ArraySet
ArraySet類型的變量,元素類型爲Session。在Android解析WindowManager(三)Window的添加過程這篇文章中我提到過Session,它主要用於進程間通訊,其餘的應用程序進程想要和WMS進程進行通訊就須要通過Session,而且每一個應用程序進程都會對應一個Session,WMS保存這些Session用來記錄全部向WMS提出窗口管理服務的客戶端。
mWindowMap:WindowHashMap
WindowHashMap類型的變量,WindowHashMap繼承了HashMap,它限制了HashMap的key值的類型爲IBinder,value值的類型爲WindowState。WindowState用於保存窗口的信息,在WMS中它用來描述一個窗口。綜上得出結論,mWindowMap就是用來保存WMS中各類窗口的集合。app
mFinishedStarting:ArrayList
ArrayList類型的變量,元素類型爲AppWindowToken,它是WindowToken的子類。要想理解mFinishedStarting的含義,須要先了解WindowToken是什麼。WindowToken主要有兩個做用:佈局
mFinishedStarting就是用於存儲已經完成啓動的應用程序窗口(好比Acitivity)的AppWindowToken的列表。
除了mFinishedStarting,還有相似的mFinishedEarlyAnim和mWindowReplacementTimeouts,其中mFinishedEarlyAnim存儲了已經完成窗口繪製而且不須要展現任何已保存surface的應用程序窗口的AppWindowToken。mWindowReplacementTimeout存儲了等待更換的應用程序窗口的AppWindowToken,若是更換不及時,舊窗口就須要被處理。學習
mResizingWindows:ArrayList
ArrayList類型的變量,元素類型爲WindowState。
mResizingWindows是用來存儲正在調整大小的窗口的列表。與mResizingWindows相似的還有mPendingRemove、mDestroySurface和mDestroyPreservedSurface等等。其中mPendingRemove是在內存耗盡時設置的,裏面存有須要強制刪除的窗口。mDestroySurface裏面存有須要被Destroy的Surface。mDestroyPreservedSurface裏面存有窗口須要保存的等待銷燬的Surface,爲何窗口要保存這些Surface?這是由於當窗口經歷Surface變化時,窗口須要一直保持舊Surface,直到新Surface的第一幀繪製完成。動畫
mAnimator:WindowAnimator
WindowAnimator類型的變量,用於管理窗口的動畫以及特效動畫。ui
mH:H
H類型的變量,系統的Handler類,用於將任務加入到主線程的消息隊列中,這樣代碼邏輯就會在主線程中執行。
mInputManager:InputManagerService
InputManagerService類型的變量,輸入系統的管理者。InputManagerService(IMS)會對觸摸事件進行處理,它會尋找一個最合適的窗口來處理觸摸反饋信息,WMS是窗口的管理者,所以,WMS「理所應當」的成爲了輸入系統的中轉站,WMS包含了IMS的引用不足爲怪。
咱們知道Window的操做分爲兩大部分,一部分是WindowManager處理部分,另外一部分是WMS處理部分,以下所示。
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);//1
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);//2
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
...
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {//3
parentWindow = windowForClientLocked(null, attrs.token, false);//4
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
...
}
...
}複製代碼
WMS的addWindow返回的是addWindow的各類狀態,好比添加Window成功,無效的display等等,這些狀態被定義在WindowManagerGlobal中。
註釋1處根據Window的屬性,調用WMP的checkAddPermission方法來檢查權限,具體的實如今PhoneWindowManager的checkAddPermission方法中,若是沒有權限則不會執行後續的代碼邏輯。註釋2處經過displayId來得到窗口要添加到哪一個DisplayContent上,若是沒有找到DisplayContent,則返回WindowManagerGlobal.ADD_INVALID_DISPLAY這一狀態,其中DisplayContent用來描述一塊屏幕。註釋3處,type表明一個窗口的類型,它的數值介於FIRST_SUB_WINDOW和LAST_SUB_WINDOW之間(1000~1999),這個數值定義在WindowManager中,說明這個窗口是一個子窗口,不瞭解窗口類型取值範圍的請閱讀Android解析WindowManager(二)Window的屬性這篇文章。註釋4處,attrs.token是IBinder類型的對象,windowForClientLocked方法內部會根據attrs.token做爲key值從mWindowMap中獲得該子窗口的父窗口。接着對父窗口進行判斷,若是父窗口爲null或者type的取值範圍不正確則會返回錯誤的狀態。
...
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);//1
final int rootType = hasParent ? parentWindow.mAttrs.type : type;//2
boolean addToastWindowRequiresToken = false;
if (token == null) {
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
...
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);//3
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {//4
atoken = token.asAppWindowToken();//5
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
...複製代碼
註釋1處經過displayContent的getWindowToken方法來獲得WindowToken。註釋2處,若是有父窗口就將父窗口的type值賦值給rootType,若是沒有將當前窗口的type值賦值給rootType。接下來若是WindowToken爲null,則根據rootType或者type的值進行區分判斷,若是rootType值等於TYPE_INPUT_METHOD、TYPE_WALLPAPER等值時,則返回狀態值WindowManagerGlobal.ADD_BAD_APP_TOKEN,說明rootType值等於TYPE_INPUT_METHOD、TYPE_WALLPAPER等值時是不容許WindowToken爲null的。經過屢次的條件判斷篩選,最後會在註釋3處隱式建立WindowToken,這說明當咱們添加窗口時是能夠不向WMS提供WindowToken的,前提是rootType和type的值不爲前面條件判斷篩選的值。WindowToken隱式和顯式的建立確定是要加以區分的,註釋3處的第4個參數爲false就表明這個WindowToken是隱式建立的。接下來的代碼邏輯就是WindowToken不爲null的狀況,根據rootType和type的值進行判斷,好比在註釋4處判斷若是窗口爲應用程序窗口,在註釋5處會將WindowToken轉換爲專門針對應用程序窗口的AppWindowToken,而後根據AppWindowToken的值進行後續的判斷。
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);//1
if (win.mDeathRecipient == null) {//2
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) {//3
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
mPolicy.adjustWindowParamsLw(win.mAttrs);//4
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = mPolicy.prepareAddWindowLw(win, attrs);//5
...
win.attach();
mWindowMap.put(client.asBinder(), win);//6
if (win.mAppOp != AppOpsManager.OP_NONE) {
int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
win.getOwningPackage());
if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
(startOpResult != AppOpsManager.MODE_DEFAULT)) {
win.setAppOpVisibilityLw(false);
}
}
final AppWindowToken aToken = token.asAppWindowToken();
if (type == TYPE_APPLICATION_STARTING && aToken != null) {
aToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
+ " startingWindow=" + win);
}
boolean imMayMove = true;
win.mToken.addWindow(win);//7
if (type == TYPE_INPUT_METHOD) {
win.mGivenInsetsPending = true;
setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
displayContent.computeImeTarget(true /* updateImeTarget */);
imMayMove = false;
} else {
if (type == TYPE_WALLPAPER) {
displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
...複製代碼
在註釋1處建立了WindowState,它存有窗口的全部的狀態信息,在WMS中它表明一個窗口。從WindowState傳入的參數,能夠發現WindowState中包含了WMS、Session、WindowToken、父類的WindowState、LayoutParams等信息。緊接着在註釋2和3處分別判斷請求添加窗口的客戶端是否已經死亡、窗口的DisplayContent是否爲null,若是是則不會再執行下面的代碼邏輯。註釋4處調用了WMP的adjustWindowParamsLw方法,該方法的實如今PhoneWindowManager中,會根據窗口的type對窗口的LayoutParams的一些成員變量進行修改。註釋5處調用WMP的prepareAddWindowLw方法,用於準備將窗口添加到系統中。
註釋6處將WindowState添加到mWindowMap中。註釋7處將WindowState添加到該WindowState對應的WindowToken中(實際是保存在WindowToken的父類WindowContainer中),這樣WindowToken就包含了相同組件的WindowState。
addWindow方法分了3個部分來進行講解,主要就是作了下面4件事:
在本篇文章中咱們首先學習了WMS的重要成員,瞭解這些成員有利於對WMS的進一步分析。接下來咱們又學習了Window的添加過程的WMS部分,將addWindow方法分爲了3個部分來進行講解,從addWindow方法咱們得知WMS有3個重要的類分別是WindowToken、WindowState和DisplayContent,關於它們會在本系列後續的文章中進行介紹。
參考資料
《深刻理解Android卷III》