基礎
每一個Activity(包括dialog)都有一個Window對象,而它們顯示的佈局又是添加到該Window對象中的mDecor中的。而mDecor又是通過WindowManager#addView()才展示出來的。這一點可查看AlertDialog,或者是見ActivityThread中的一部分代碼,如下:
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();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
這一段代碼會在startActivity()時調用,它截自ActivityThread#handleResumeActivity()。
從上面也可看出,它和AlertDialog的處理方式完全一樣,直接使用WindowManager#addView(decor,1);。因此,可以知道,要想展示某個View到Window上,必須通過WindowManager#addView()進行。也就是說WindowManager是View展示的橋樑。
WindowManager是接口,通過getSystemService()可知它的具體實現類爲WindowManagerImpl。而對於後者,有一成員變量mGlobal指向的是一個WindowManagerGlobal實例,其內部完全是使用WindowManagerGlobal進行操作。
構造函數
Activity#attach()->Window#setWindowManager()->WindowManagerImpl#createLocalWindowManager(this)->WindowManagerImpl#構造函數,在構造函數中爲mParentWindow成員變量賦值。
因此,WindowManagerImpl通過mParentWindow指向Window——這個Window對象也是Activity中mWindow指向的Window實例,而Window通過mWindowManager指向WindowManagerImpl實例。
WindowManagerGlobal
單例。其內部有三個變量如下:
private final ArrayList<View> mViews = new ArrayList<View>();//存儲所有addView時的view的
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();//存儲所有的ViewRootImpl的
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();//view對應的LayoutParams,是WindowManager.LayoutParams
有一點要注意:同一index下,它們三個對應的元素是對應的。addView
代碼如下:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//略。對view,display的非空判斷。且要求params必須是WindowManager.LayoutParams
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//略
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//略
//爲view設置LayoutParams,並存儲
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
root.setView(view, wparams, panelParentView);
} //略
}
邏輯很簡單,內容new一個ViewRootImpl,並調用其setView()方法。
getWindowSession
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
sWindowManagerService = getWindowManagerService();
ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
}
}
return sWindowManagerService;
}
}
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
首先通過getWindoeManagerService()拿到WMS在本進程中的代理對象。分兩步:1)通過ServiceManager#getService()拿到WMS在本地的IBinder對象;2)通過asInterface()爲IBinder對象創建一個代理類,本進程與WMS的交互就通過該代理類進行。
在getWindowSession()中,通過代理類調用WMS中的openSession(),後者返回的是一個Session對象。所以getWindowSession()返回的是Session對象在本地的代理,而它的具體實例在WMS所屬的進程中。
getWindowSession()拿到Session對象的代理類後,就可通過sWindowSession對象訪問Session實例中的方法。而Session中,一般的方法都是調用WMS實例完成的(兩者屬於同一進程,所以通過sWindowSession調用方法相當於間接地調用了WMS中的方法)。因此Session對象是本進程調用WMS的橋樑。
WindowManagerService
addView()->ViewRootImpl#setView()->Session#addToDisplay()->WMS#addWindow()。從addView()到setView()前半部分(到requestLayout()截止),都是對addView()中的View參數進行操作。從setView到最後,卻是對Window的顯示進行操作。
addWindow
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel)
第一個參數爲getWindowSession()方法返回值在WMS所在進程中的實例。
第二個參數爲IWindow對象,由ViewRootImpl構造函數中創建,在ViewRootImpl#setView()中調用Session#addToDisplay()時將該對象當作參數傳遞進去,所以WMS進程會得到一個W的代理對象,進而WMS可以使用它調用應用進程中的方法。
attrs爲WMG#addView()時View所使用的LayoutParams對象,是由應用進程傳遞到WMS進程中的。
對於client,addWindow()中有如下代碼:
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
將client又賦值給WindowState#mClient,而WindowState又使用mClient調用應用進程中的方法。由此要知client的作用就是提供一個讓WMS調用應用進程中方法的橋樑。
WMS進程由client調用用戶進程,而用戶進程通過Session對象調用WMS進程。