在android系统中窗口系统是一个很庞大很复杂的系统,对系统中的所有窗口进行管理是由窗口管理服务WindowManagerService来负责处理的,它是一个系统服务,扮演C/S模式中的服务端角色,而上层应用就是客户端,客户端负责请求创建窗口和使用窗口,服务端完成窗口的维护,窗口显示等。
一、什么是窗口?
我们通常所说的窗口是指屏幕上显示视图的一块区域,它主要负责展示界面及事件交互,比如说打开一个应用后看到的主界面就是一个窗口,手机上的顶部状态栏、底部导航栏及弹出的对话框、Taost提示框等都是一个个窗口。而我们在framework代码中看到的Window类及window类型的对象并不是我们所说的窗口概念。我们在手机屏幕上看到的显示效果通常会有多个窗口组成,有顶部状态栏、底部导航栏和上层应用的窗口,而这多个窗口都是通过WindowManagerService来布局和控制在z轴上的顺序的,然后交给SurfaceFlinger来对多个窗口内容进行合成,SurfaceFlinger把合成好的图像信息送给硬件帧缓冲区显示出来。
二、窗口类型
Framework在WindowManager中定义了三种窗口类型:
1、应用窗口。应用窗口是指该窗口对应着一个Activity,Activity是由AMS来统一调度的。Activity通过调用setContentView来加载自定义的视图结构,然后找到视图树的根DecorView,通过调用WindowManager.addView()来将此DecorView添加到WMS中,显示到窗口区域。
2、子窗口。子窗口指该窗口必须依赖一个父窗口,父窗口可以是一个应用类型的窗口,也可以是任何其他类型的窗口,子窗口和父窗口拥有相同的Token,父窗口被销毁时子窗口也必须被销毁。例如Menu菜单,Dialog等。
3、系统窗口。系统窗口不需要对应任何Activity,也不需要有父窗口,系统窗口是由系统进程创建出来的,例如有壁纸界面、顶部状态栏、底部导航栏,输入法窗口等,它们是通过直接调用WindowManager.addView()来把一个view添加到WindowManager中。
三、窗口管理
窗口管理基于C/S模式来控制的,Client端就是上层应用,Server端则是WindowManagerService,简称WMS。WMS是系统服务,运行在system_server进程里,Client端通过进程间通信的方式请求WMS创建窗口,由WMS向Client返回窗口相关的结果。所有应用的窗口都由WMS来管理和控制,WMS具有以下职责:1、窗口添加与删除。2、窗口显示与隐藏。3、z-order管理。4、焦点窗口管理。5、输入法与壁纸窗口管理。6、窗口动画。7、系统消息分发。下面看几个重要的概念。
1、WindowToken:它是一个句柄,保存了所有具有同一个token的WindowState。Activity是由AMS来管理和控制的,一个Activity会在AMS里生成一个对应的token对象,当应用请求添加窗口的时候,wms会包装这个token对象,生成一个WindowToken对象。所以,WindowState的token相同则使用同一个WindowToken。
2、AppWindowToken:继承于WindowToken,用于标识一个Activity。子窗口与父窗口拥有相同的AppWindowToken对象。
3、Session:表示一个客户端和服务端的交互会话。一般来说不同的应用通过不同的会话来和WindowManagerService交互,但是处于同一个进程的不同应用使用同一个Session来交互。
由于窗口管理的职责较多,需要对每一个职责进行细化分析,今天我们首先来分析窗口是如何被创建出来的,在后面的文章里会继续分析WMS管理窗口的每一个职责。
四、应用窗口创建过程
启动一个应用,首先会执行主线程ActivityThread的main函数,通过Handler发送消息给主线程执行handleLaunchActivity函数。
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
//调用Activity中的onResume方法
handleResumeActivity(r.token, false, r.isForward);
...
}
继续查看performLaunchActivity方法。
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
//执行Activity的onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state);
...
return activity;
}
过程很简单,首先利用反射生成一个Activity对象,接着调用Activity对象的attach方法,最后调用Activity对象的onCreate方法。
final void attach(参数很多) {
attachBaseContext(context);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
mWindowManager = mWindow.getWindowManager();
}
public PhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
首先调用PolicyManager创建一个Window对象,它其实是一个PhoneWindow类型的对象,然后给Window对象设置回调函数,使Activity可以接收WMS分发的消息。接着创建Window对象里的WindowManager,这里的WindowManager是LocalWindowManager类型的对象,它是Window的内部类。
接着会调用Activity的onCreate方法,在onCreate方法里调用setContentView设置layout布局。我们继续查看setContentView函数
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
这个方法在之前的文章里介绍过,主要是加载UI布局并得到视图树根节点DecorView和装载应用视图的容器mContentParent。
接着主线程里调用Activity的onResume方法,在执行了onResume方法后便调用activity.makeVisible()方法显示窗口。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
方法很简单,得到WindowManager,把根视图DecorView添加到WindowManager里,并设置DecorView为可见状态。此处的wm是LocalManagerService类型的实例,我们来查看它的addView方法。
public final void addView(View view, ViewGroup.LayoutParams params) {
// Let this throw an exception on a bad params.
WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;
// 校验token等参数
...
if (wp.packageName == null) {
wp.packageName = mContext.getPackageName();
}
// 这儿的mWindowManager是WMS在客户端的代理对象WindowManagerImpl
mWindowManager.addView(view, params);
}
WindowManagerImpl实际上都是调用WindowManagerGlobal里面的方法,继续进入WindowManagerGlobal中的addView函数
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
最主要是构造ViewRootImpl对象,然后调用它的setView方法。那么ViewRoot是什么呢?它其实不是一个View,但它充当了视图树的根,所有的事件和消息都从它这儿往下分发。
接着看setView方法:
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {
} finally {
if (restore) {
attrs.restore();
}
}
}
WindowSession是与WMS通信的对象,这里调用sWindowSession.add其实是在调用Session.add方法,在Session类中,add方法调用mService.addWindow方法,这儿的mService就是WMS。
我们继续看WMS的addWindow方法。
boolean reportNewConfig = false;
WindowState attachedWindow = null;
WindowState win = null;
long origId;
final int type = attrs.type;
// 根据窗口类型做判断
win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = mPolicy.prepareAddWindowLw(win, attrs);
...
这样在服务端完成了与客户端对应的WindowState对象的初始化,并调用mPolicy调整窗口的属性,到此就实现了窗口的创建过程。
五、窗口创建过程总结
启动一个应用,应用进程初始化完成后,会执行主线程ActivityThread的main函数,在main函数里面给Handler发送消息执行LaunchActivity,首先生成一个Activity对象,此时该Activity对象还没有属于它的窗口。接着调用attach函数,在attach函数里面执行PolicyManager.makeNewWindow()创建一个PhoneWindow对象。然后Activity调用setContentView方法加载UI视图结构,将此视图结构放进一个framework预定义好的layout里,找到这个layout的根节点,也就是整个activity显示界面的根节点,赋值给PhoneWindow里面的mDecorView变量。最后在Activity显示出来之前,通过调用getWindow().getDecorView()得到DecorView对象,接着调用WindowManager.addView()将DecorView添加到WindowManager中。在WindowManager内部通过该Activity的token和该View关联起来,向WindowManagerService申请创建窗口的时候,再把token传递给WindowManagerService。WMS通过TokenMap查询该token的AppWindowToken,如果为空则抛出异常,否则创建一个WindowState并做相应的参数调整。WindowState申请一个显示缓存,交给SurfaceFlinger来与其他的Window合成处理,把合成好之后的图像信息送到硬件帧缓冲区打印显示出来。