一、WMS的作用
窗口管理
WMS是窗口的管理者,它負責窗口的啟動、添加和刪除,另外窗口的大小和層級也是由WMS進行管理的。窗口管理的核心成員有DisplayContent、WindowToken和WindowState。
窗口動畫
窗口間進行切換時,使用窗口動畫可以顯得更炫一些,窗口動畫由WMS的動畫子系統來負責,動畫子系統的管理者為WindowAnimator。
輸入系統的中轉站
通過對窗口的觸摸從而產生觸摸事件,InputManagerService(IMS)會對觸摸事件進行處理,它會尋找一個最合適的窗口來處理觸摸反饋信息,WMS是窗口的管理者,因此,WMS“理所應當”的成為了輸入系統的中轉站。
Surface管理
窗口并不具備有繪制的功能,因此每個窗口都需要有一塊Surface來供自己繪制。為每個窗口分配Surface是由WMS來完成的。
二、WMS的核心成員介紹
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
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
-
mPolicy:WindowManagerPolicy
WindowManagerPolicy(WMP)類型的變量。WindowManagerPolicy是窗口管理策略的接口類,用來定義一個窗口策略所要遵循的通用規范,并提供了WindowManager所有的特定的UI行為。它的具體實現類為PhoneWindowManager,這個實現類在WMS創建時被創建。WMP允許定制窗口層級和特殊窗口類型以及關鍵的調度和布局。
-
mSessions:ArraySet<Session>
ArraySet類型的變量,元素類型為Session.它主要用于進程間通信,其他的應用程序進程想要和WMS進程進行通信就需要經過Session,并且每個應用程序進程都會對應一個Session,WMS保存這些Session用來記錄所有向WMS提出窗口管理服務的客戶端。
-
mWindowMap:WindowHashMap
WindowHashMap類型的變量,WindowHashMap繼承了HashMap,它限制了HashMap的key值的類型為IBinder,value值的類型為WindowState。WindowState用于保存窗口的信息,在WMS中它用來描述一個窗口。綜上得出結論,mWindowMap就是用來保存WMS中各種窗口的集合。
-
mFinishedStarting:ArrayList
ArrayList類型的變量,元素類型為AppWindowToken,它是WindowToken的子類。
WindowToken主要有兩個作用:
- 可以理解為窗口令牌,當應用程序想要向WMS申請新創建一個窗口,則需要向WMS出示有效的WindowToken。AppWindowToken作為WindowToken的子類,主要用來描述應用程序的WindowToken結構,應用程序中每個Activity都對應一個AppWindowToken(AMS的ActivityRecord)。
- WindowToken會將相同組件(Acitivity)的窗口(WindowState)集合在一起,方便管理。
mFinishedStarting就是用于存儲已經完成啟動的應用程序窗口(Acitivity)的AppWindowToken的列表。 - mFinishedEarlyAnim和mWindowReplacementTimeouts,其中mFinishedEarlyAnim存儲了已經完成窗口繪制并且不需要展示任何已保存surface的應用程序窗口的AppWindowToken。
- mWindowReplacementTimeout存儲了等待更換的應用程序窗口的AppWindowToken,如果更換不及時,舊窗口就需要被處理。
mResizingWindows:ArrayList
- ArrayList類型的變量,元素類型為WindowState。
- mResizingWindows是用來存儲正在調整大小的窗口的列表。
- mPendingRemove是在內存耗盡時設置的,里面存有需要強制刪除的窗口。
- mDestroySurface里面存有需要被Destroy的Surface。mDestroyPreservedSurface里面存有窗口需要保存的等待銷毀的Surface。
mInputManager:InputManagerService
InputManagerService類型的變量,輸入系統的管理者。InputManagerService(IMS)會對觸摸事件進行處理,它會尋找一個最合適的窗口來處理觸摸反饋信息,WMS是窗口的管理者,因此,WMS“理所應當”的成為了輸入系統的中轉站。
三、WMS的作用
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService .java
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy) {
...
mInputManager = inputManager;
...
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
//通過DisplayManager的getDisplays方法得到Display數組,每個顯示設備都有一個Display實例
mDisplays = mDisplayManager.getDisplays();
for (Display display : mDisplays) {
createDisplayContentLocked(display);
}
...
mActivityManager = ActivityManager.getService();
...
//創建了WindowAnimator,它用于管理所有的窗口動畫
mAnimator = new WindowAnimator(this);
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
LocalServices.addService(WindowManagerInternal.class, new LocalService());
initPolicy();//6
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
...
}
2、Window的添加過程
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
1、對所要添加的窗口進行檢查,如果窗口不滿足一些條件,就不會再執行下面的代碼邏輯。
2、WindowToken相關的處理,比如有的窗口類型需要提供WindowToken,沒有提供的話就不會執行下面的代碼邏輯,有的窗口類型則需要由WMS隱式創建WindowToken。
3、WindowState的創建和相關處理,將WindowToken和WindowState相關聯。
4、創建和配置DisplayContent,完成窗口添加到系統前的準備工作。
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);
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;
}
...
//type代表一個窗口的類型,它的數值介于FIRST_SUB_WINDOW和LAST_SUB_WINDOW之間(1000~1999),
//這個數值定義在WindowManager中,說明這個窗口是一個子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//attrs.token是IBinder類型的對象,windowForClientLocked方法內部會根據attrs.token作為key值
//從mWindowMap中得到該子窗口的父窗口
parentWindow = windowForClientLocked(null, attrs.token, false);
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;
}
}
}
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
//得到WindowToken保存窗口相關信息
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
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();
//這說明當我們添加窗口時是可以不向WMS提供WindowToken的,前提是rootType和type的值不為前面條件判斷篩選的值。
//WindowToken隱式和顯式的創建肯定是要加以區分的,注釋3處的第4個參數為false就代表這個WindowToken是隱式創建的
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow);
//處判斷如果窗口為應用程序窗口
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
//將WindowToken轉換為專門針對應用程序窗口的AppWindowToken,AppWindowToken對應ActivityRecord
atoken = token.asAppWindowToken();
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;
}
}
.....
//創建了WindowState,它存有窗口的所有的狀態信息,在WMS中它代表一個窗口
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
//窗口的客戶端是否已經死亡
if (win.mDeathRecipient == null) {
// 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;
}
//窗口的DisplayContent是否為null,沒有展示內容
if (win.getDisplayContent() == null) {
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//調用了WindowManagerPolicy的adjustWindowParamsLw方法,該方法的實現在PhoneWindowManager中,
//會根據窗口的type對窗口的LayoutParams的一些成員變量進行修改
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = mPolicy.prepareAddWindowLw(win, attrs);
.....
win.attach();
mWindowMap.put(client.asBinder(), win);
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);
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;
}
}
}
3、Window的刪除過程
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
//找到view的索引
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
//刪除當前的view
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);//4
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
四、WMS主要類介紹:
1、WindowManagerService類
WindowManagerService服務就可以通過它在內部所創建的WindowState對象的成員變量mClient來 要求運行在應用程序進程這一側的Activity組件來配合管理窗口的狀態,例如:
- 當一個Activity組件的窗口的大小發生改變后,WindowManagerService服務就會調用這個
IWindow接口的成員函數resized來通知該Activity組件,它的大小發生改變了。 - 當一個Activity組件的窗口的可見性之后,WindowManagerService服務就會調用這個Iwindow
接口的成員函數dispatchAppVisibility來通知該Activity組件,它的可見性發生改變了。 - 當一個Activity組件的窗口獲得或者失去焦點之后,WindowManagerService服務就會調用這個
IWindow接口的成員函數windowFoucusChanged來通知該Activity組件,它的焦點發生改變了。
2、Window類
- 定義Callback接口,它包含一系列dispatchXxxx方法和一系列onXxxx方法,用于處理UI事件。
- 定義了一些接口,如setContentView、findViewById()等。由PhoneWindow來實現。
3、WindowManager類
- WindowManager繼承自ViewManager這個接口,這個接口主要有以下的實現子接口:
addView()、updateViewLayout()、removeView();
WindowManager可以添加view到屏幕,也可以從屏幕刪除view。它面向的對象一端是屏幕,另一端就是View,通過WindowManager的 addView方法創建View,這樣產生出來的View根據WindowManager.LayoutParams屬性不同,效果也就不同了,比如創建系統頂級窗口,實現懸浮窗口效果。
4、ViewRootImpl類
ViewRootImpl這個類在android的UI結構中扮演的是一個中間者的角色,連接的是PhoneWindow 和WindowManagerService,也就是窗口管理系統與窗口呈現系統之間的橋梁。
它的主要作用有兩個:
1.向DecorView分發收到的用戶發起的event事件,如按鍵,觸屏等事件;
2.與WindowManagerService交互,完成整個Activity的GUI的繪制。
3.里面兩個重要的變量 mWindowSessoin和mWindow。
- mWindowSessoin是ViewRootImpl和WindowManagerService之間的一個會話層,它的實體是在WMS中定義,作為ViewRootImpl 向WMS發送請求的的橋梁。
- mWindow是ViewRootImpl提供給WMS,以便WMS反向通知ViewRootImpl的接口。由于ViewRootImpl處在application端,而WMS處在system_server端,它們處在不同的進程,因此需要添加這個W接口,便于WMS向ViewRootImpl傳遞信息。
5、WindowState類
- WMS中最基本的元素,描述WMS中的一個窗口。它既可以是由App添加過來的View,也可以是系統創建的系統窗口。mAttrs為WindowManager.LayoutParams類型,描述布局參數。mClient為IWindow類型,也就是App端的ViewRootImpl::W。為了查找方便,WMS中的mWindowMap保存了IWindow到WindowState的映射,mTokenMap保存了IApplicationToken到WindowToken的映射。
6、Session類
- 向App提供IWindowSession接口讓其可以和WMS通信,每個App在WMS有一個Session對象,App就是通過這個Session來向WMS發出窗口管理申請的,命令dumpsys window sessions可以查看系統中的Session。
7、WindowToken類
- 描述WM中一組相關的窗口,這些Window對應的WindowState放在其成員變量windows里。其主要繼承類AppWindowToken,它是針對App的WindowToken結構。WindowState中的mAppToken指向所屬的AppWindowToken,如果是系統窗口,mAppToken為空,mToken指向WindowToken對象。
8、AppWindowToken
每個App的Activity對應一個AppWindowToken。其中的appToken為IApplicationToken類型,連接著對應的AMS中的ActivityRecord::Token對象,有了它就可以順著AppWindowToken找到AMS中相應的ActivityRecord。其中allAppWindows是一個無序的列表,包含該Activity中所有的窗口。用dumpsys window display可以查看z-ordered的AppWindowToken列表。
8、TaskStack類
- AppWindowToken保存了屬于它的WindowState的有序列表,而它本身也作為一個列表被管理在TaskStack中的mTasks成員中。
- TaskStack中有個重要的變量mBounds,在相同Task里的AppWindowToken對應的Activity的大小是相同的,所以mBounds代表的就是Activity對應的大小。
9、DisplayContent類
表示一個顯示設備上的內容,這個顯示設備可以是外接顯示屏,也可以是虛擬顯示屏。其中 mWindows是一個WindowState的有序(Z-ordered,底部最先)列表。mStackBoxes包含了若干個StackBox,其中 一個為HomeStack,另一個是App的StackBox。所有的StackBox被組織成二叉樹,StackBox是其中的節點,其中有三個重要成 員變量,mFirst和mSecond指向左和右子結點(也是StackBox),StackBox的成員mStack才是我們真正關心的東西 -TaskStack。可以看到,為了要把TaskStack存成樹的結構,需要一個容器,這個容器就是StackBox。
五、AMS和WMS交互圖
1、AMS主要類關系圖
2、WMS主要類關系圖
3、AMS、WMS數據結構關系
參考鏈接:http://liuwangshu.cn/tags/Android%E6%A1%86%E6%9E%B6%E5%B1%82/
出版的“Android進階解密”這本書值得看