基础
每一个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进程。