Android 系统窗口管理机制简介

3,763 阅读4分钟

一、概述

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;
            }
            // 若取得的父窗口也是子窗口,则打印:添加了错误的子窗口
            if (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()) {
            Slog.w(TAG, "Attempted to add private presentation window to a non-private display.  Aborting.");
            return WindowManagerGlobal.ADD_PERMISSION_DENIED;
        }
        boolean addToken = false;
        //取出WindowToken
        WindowToken token = mTokenMap.get(attrs.token);
        
        if (token == null) {
             // 对于子窗口来说,WmS中必须有对应的Token才能添加
            if (type >= 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) {
                Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            // 墙纸窗口,WmS中必须有对应的Token才能添加  
            if (type == TYPE_WALLPAPER) {
                Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (type == TYPE_DREAM) {
                Slog.w(TAG, "Attempted to add Dream window with unknown token " + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
             // 创建窗口  
            token = new WindowToken(this, attrs.token, -1, false);
            addToken = true;
        } 
        //Activity类型窗口
        else if (type >= 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 " + token + ".  Aborting.");
                return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
            } 
            // 试图使用存在的应用token添加窗口  
            else if (atoken.removed) {
                Slog.w(TAG, "Attempted to add window with exiting application token " + token + ".  Aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }
            // 对于内置的输入方法窗口,token的windowType值要等于TYPE_INPUT_METHOD  
            if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
                // No need for this guy!
                if (localLOGV)
                    Slog.v(TAG, "**** NO NEED TO START: " + attrs.getTitle());
                return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
            }
        } 
        //输入法类型窗口
        else if (type == TYPE_INPUT_METHOD) {
            if (token.windowType != TYPE_INPUT_METHOD) {
                Slog.w(TAG, "Attempted to add input method window with bad token " + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } 
        //壁纸类型窗口
        else if (type == TYPE_WALLPAPER) {
            if (token.windowType != TYPE_WALLPAPER) {
                Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        } 
        //DREAM类型窗口
        else if (type == TYPE_DREAM) {
            if (token.windowType != TYPE_DREAM) {
                Slog.w(TAG, "Attempted to add Dream window with bad token " + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
        }

        //创建窗口
        win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
         
        //调整子窗口尺寸
        mPolicy.adjustWindowParamsLw(win.mAttrs);
        ......
        ......
         // 如果要添加Token,添加Token添加到WmS中  
        if (addToken) {
            mTokenMap.put(attrs.token, token);
        }
         // 将窗口添加到Session中  
            win.attach();           
            ......
            ......
    }

Session表示一个客户端和服务端的交互会话。一般来说不同的应用通过不同的会话来和WindowManagerService交互,但是处于同一个进程的不同应用通过同一个Session来交互。

结束语:关于WMS管理窗口的机制要远比我分析的要复杂,能力有限,我只能介绍这么多了.查看图片