一、概述
Android系统窗口管理是由WindowManagerService负责实现的.WindowManagerService(后面简称WMS)的代码位于
frameworks/base/services/java/com/android/server/wm/WindowManagerService.java.
什么是窗口?
窗口就是屏幕上的一块矩形区域,可以显示UI和与用户交互.常见的比如:Dialog,Activity界面,状态栏、Toast界面.站在系统的角度来说,
窗口其实是一个Surface(画布).一个屏幕有多个窗口,而这多个窗口的布局和顺序以及窗口动画是由WMS管理的,然后由一个叫SurfaceFlinger的服务来对多个画布内容混合和显示出来.
WMS和SurfaceFlinger的关系如下图
图中的Z轴大小就是不同窗口显示的顺序,在Android里叫Z-order.SurfaceFlinger将多块Surface的内容按照Z-order进行混合并输出到FrameBuffer(帧缓冲).
二、WMS的启动
和AMS、PMS一样,WMS也是在SystemServer的initAndLoop方法里启动的.
主要有3个阶段:
1.创建WMS
2.做显示准备工作
3.SystemServer启动之后通知WMS
先看第一个阶段
1.创建WMS
wm = WindowManagerService.main(context, power, display, inputManager, wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
新建WMS实例,然后往ServiceManager注册WMS.
看下main方法
public static WindowManagerService main(final Context context, final PowerManagerService pm, final DisplayManagerService dm, final InputManagerService im, final Handler wmHandler, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
wmHandler.runWithScissors(new Runnable() {
@Override
public void run() {
holder[0] = new WindowManagerService(context, pm, dm, im, haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
}
新建了一个WMS类型的数组,大小为1,然后new一个WMS实例,赋值给该数组,最后返回这个数组.
调用了WMS的有参构造函数
private WindowManagerService(Context context, PowerManagerService pm, DisplayManagerService displayManager, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
......
......
//新建窗口动画实例
mAnimator = new WindowAnimator(this);
//在UI线程中初始化WindowManagerPolicy
initPolicy(UiThread.getHandler());
// 添加自己到Watchdog中
Watchdog.getInstance().addMonitor(this);
......
......
主要是新建窗口动画,在UI线程中初始化WindowManagerPolicy,添加自己到Watchdog中.
这里介绍下WindowManagerPolicy,WindowManagerPolicy是一个接口,它只有一个实现类PhoneWindowManager.
它相当于WMS的中介,会为WMS处理窗口信息.比如计算窗口尺寸等.
2.做显示准备工作
public void displayReady() {
//显示默认的尺寸、像素等配置
displayReady(Display.DEFAULT_DISPLAY);
synchronized (mWindowMap) {
//获取屏幕
final DisplayContent displayContent = getDefaultDisplayContentLocked();
//读取屏幕尺寸和像素等信息
readForcedDisplaySizeAndDensityLocked(displayContent);
mDisplayReady = true;
}
......
......
}
会先配置默认的尺寸、像素等显示信息.然后获取屏幕,读取屏幕尺寸和像素等信息.这里要介绍下DisplayContent和mWindowMap.
DisplayContent是一块屏幕.用列表保存的.
SparseArray
mDisplayContents = new SparseArray
(2);
DisplayContent会根据窗口的位置显示出窗口,属于同一个DisplayContent的窗口就显示在同一个屏幕里.
mWindowMap一个HashMap,保存了所有窗口的状态信息.
键为IBinder,值为WindowToken.
WindowToken是窗口令牌的意思,用来标示该窗口的类别.窗口有Activity、InputMethod、Wallpaper以及Dream这几种.不同的类别对应不同的WindowToken.
3.SystemServer启动之后通知WMS
try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
调用的是WindowManagerPolicy的systemReady方法
public void systemReady() {
mPolicy.systemReady();
}
三、窗口的添加和删除
WMS主要的功能如下
1. 窗口的添加和删除
2. 窗口的显示和隐藏控制
3. Z-order顺序管理
4. 焦点窗口和焦点应用的管理
5. 输入法窗口管理和墙纸窗口管理
6. 窗口动画管理
7. 系统消息收集和分发
这里暂时只介绍Activity窗口的添加.
AMS启动一个应用时,会在服务端生成一个AppWindowToken,AppWindowToken继承自WindowToken,标示为Activity的WindowToken.
Activity在请求WMS添加窗口时,提供这个AppWindowToken给WMS.
添加窗口的方法是addWindow.看代码吧.
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) {
......
......
//通过WindowManagerPolicy检查权限,是否能添加窗口
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
//添加子窗口的父窗口的窗口状态
WindowState attachedWindow = null;
//子窗口的窗口状态
WindowState win = null;
long origId;
//窗口类型,比如Toast、StatusBar
final int type = attrs.type;
synchronized (mWindowMap) {
//屏幕设置未初始化
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
//如果重复添加了
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果是子窗口
if (type >= FIRST_SUB_WINDOW && type <= last_sub_window)="" {="" 返回该子窗口在wms对应的父窗口="" attachedwindow="windowForClientLocked(null," attrs.token,="" false);="" 如果父窗口不存在="" if="" (attachedwindow="=" null)="" slog.w(tag,="" "attempted="" to="" add="" window="" with="" token="" that="" is="" not="" a="" window:="" "="" +="" attrs.token="" ".="" aborting.");="" return="" windowmanagerglobal.add_bad_subwindow_token;="" }="" 若取得的父窗口也是子窗口,则打印:添加了错误的子窗口="" (attachedwindow.mattrs.type="">= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= last_sub_window)="" {="" slog.w(tag,="" "attempted="" to="" add="" window="" with="" token="" that="" is="" a="" sub-window:="" "="" +="" attrs.token="" ".="" aborting.");="" return="" windowmanagerglobal.add_bad_subwindow_token;="" }="" if="" (type="=" type_private_presentation="" &&="" !displaycontent.isprivate())="" private="" presentation="" non-private="" display.="" windowmanagerglobal.add_permission_denied;="" boolean="" addtoken="false;" 取出windowtoken="" windowtoken="" (token="=" null)="" 对于子窗口来说,wms中必须有对应的token才能添加="">= FIRST_APPLICATION_WINDOW && type <= last_application_window)="" {="" slog.w(tag,="" "attempted="" to="" add="" application="" window="" with="" unknown="" token="" "="" +="" attrs.token="" ".="" aborting.");="" return="" windowmanagerglobal.add_bad_app_token;="" }="" 如果是内置的输入方法窗口,wms中必须有对应的token才能添加="" if="" (type="=" type_input_method)="" input="" method="" 墙纸窗口,wms中必须有对应的token才能添加="" type_wallpaper)="" wallpaper="" type_dream)="" dream="" 创建窗口="" windowtoken(this,="" attrs.token,="" -1,="" false);="" addtoken="true;" activity类型窗口="" else="">= FIRST_APPLICATION_WINDOW && type <= last_application_window)="" {="" appwindowtoken="" atoken="token.appWindowToken;" appwindowtoken值为空则打印信息="" if="" (atoken="=" null)="" slog.w(tag,="" "attempted="" to="" add="" window="" with="" non-application="" token="" "="" +="" ".="" aborting.");="" return="" windowmanagerglobal.add_not_app_token;="" }="" 试图使用存在的应用token添加窗口="" else="" (atoken.removed)="" exiting="" application="" windowmanagerglobal.add_app_exiting;="" 对于内置的输入方法窗口,token的windowtype值要等于type_input_method="" (type="=" type_application_starting="" &&="" atoken.firstwindowdrawn)="" no="" need="" for="" this="" guy!="" (locallogv)="" slog.v(tag,="" "****="" start:="" attrs.gettitle());="" windowmanagerglobal.add_starting_not_needed;="" 输入法类型窗口="" type_input_method)="" (token.windowtype="" !="TYPE_INPUT_METHOD)" input="" method="" bad="" attrs.token="" windowmanagerglobal.add_bad_app_token;="" 壁纸类型窗口="" type_wallpaper)="" wallpaper="" dream类型窗口="" type_dream)="" dream="" 创建窗口="" win="new" windowstate(this,="" session,="" client,="" token,="" attachedwindow,="" appop[0],="" seq,="" attrs,="" viewvisibility,="" displaycontent);="" 调整子窗口尺寸="" mpolicy.adjustwindowparamslw(win.mattrs);="" ......="" 如果要添加token,添加token添加到wms中="" (addtoken)="" mtokenmap.put(attrs.token,="" token);="" 将窗口添加到session中="" win.attach();="" }<="" code="">
Session表示一个客户端和服务端的交互会话。一般来说不同的应用通过不同的会话来和WindowManagerService交互,但是处于同一个进程的不同应用通过同一个Session来交互。
结束语:关于WMS管理窗口的机制要远比我分析的要复杂,能力有限,我只能介绍这么多了.查看图片