Android-WindowManagerService源码流程

一、WindowManagerService相关概念

1.Activity与Window的概念

  • Activity只负责生命周期和事件处理
  • Window只控制视图
  • 一个Activity包含一个Window,如果Activity没有Window,就相当于Service
  • AMS统一调度所有应用程度的Activity
  • WMS控制所有Window的显示与隐藏以及要显示的位置

2.Window

Window表明它是和窗口相关的,“窗口”是一个抽象的概念,从用户的角度来讲,其实就是一个“界面”;从SurfaceFlinger的角度来看,它是一个layer,承载着和界面有关的数据和属性;从WMS的角度来看,它是一个WindowState,用于管理和界面有关的状态。

  • 表示一个窗口的概念,是所有View的直接管理者,任何视图都通过Window呈现(点击事件由Window->DecorView->View;Activity的setContentView底层通过Window完成)
  • Window是一个抽象类,具体实现是PhoneWindow
  • 创建Window需要通过WindowManager创建
  • WindowManager是外界访问Window的入口
  • Window的具体实现位于WindowManagerService中
  • WindowManager和WindowManagerService的交互是通过IPC进程间通信完成的
  • 定义窗口样式和行为的抽象基类,用于作为顶层的View加到WindowManager中,其实现类是PhoneWindow
  • 每个Window都需要指定一个Type(应用窗口、子窗口、系统窗口)。Activity对应的窗口是应用窗口;PhoneWindow,ContextMenu,OptionMenu是常用的子窗口;像Toast和系统警告提示框(如ANR)就是系统窗口,还有很多应用的悬浮窗也属于系统窗口。

3.Surface

在Android中,一个窗口会独占一个Surface,Surface其实就是一个画布,应用程序通过Canvas或者OpenGL在Surface上绘制内容
在Surface上绘制后,通过SurfaceFlinger将多块Surface的内容按照Z-order进行混合并输出到FrameBuffer,从而将Android页面展示给用户。
每个窗口都有一块Surface用于显示自己的ui,必然需要一个角色对窗口进行统一管理,这个时候,WMS应运而生。WMS为所有窗口分配Surface,掌管z-order以及位置、尺寸、窗口的进场出场动画,并且还是输入系统的中转站。

  • 布局系统:计算管理窗口的位置和层次
  • 动画系统:根据布局系统的计算渲染窗口动画

4.Window的Type

  • 应用窗口:层级范围是1~99
  • 子窗口:层级范围是1000~1999
  • 系统窗口:层级范围是2000~2999
    各级别type值在WindowManager中的定义分别为:
  • 应用窗口(1~99)
        /**
         * 第一个应用窗口
         */
        public static final int FIRST_APPLICATION_WINDOW = 1;

        /**
         * 所有程序窗口的base窗口,其他应用程序窗口都显示在它上面
         */
        public static final int TYPE_BASE_APPLICATION   = 1;

        /**
         * 所有activity的窗口,只能配合activity在当前app使用
         */
        public static final int TYPE_APPLICATION        = 2;

        /**
         * 目标哦应用窗口未启动之前的那个窗口
         */
        public static final int TYPE_APPLICATION_STARTING = 3;

        /**
         * TYPE_APPLICATION的一种变体,可确保窗口管理器在显示该应用之前等待
         * 该窗口的绘制。 在多用户系统中,仅在拥有用户的窗口上显示。
         */
        public static final int TYPE_DRAWN_APPLICATION = 4;

        /**
         * 最后一个应用窗口
         */
        public static final int LAST_APPLICATION_WINDOW = 99;
  • 子窗口(1000~1999)
        /**
         * 第一个子窗口
         */
        public static final int FIRST_SUB_WINDOW = 1000;

        /**
         * 面板窗口,显示于宿主窗口的上层,只能配合activity在当前app使用
         */
        public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

        /**
         * 媒体窗口(例如视频),显示于宿主窗口下层
         */
        public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

        /**
         * 应用程序窗口的子面板,只能配合activity在当前app使用(PopupWindow默认就是这个type)
         */
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

        /** 
         * 对话框窗口,只能配合activity在当前app使用
         */
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

        /**
         * 用于在媒体窗口顶部显示覆盖的窗口。 这些窗口显示在TYPE_APPLICATION_MEDIA和应用程序窗口之间。 
         * 它们应该半透明才能有用。所以该窗口是隐藏的
         */
        @UnsupportedAppUsage
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;

        /**
         * 应用程序窗口顶部的子面板,它是子面板窗口。 这些窗口显示在其附加窗口
         * 和任何{@link #TYPE_APPLICATION_SUB_PANEL}面板的顶部。
         * @hide
         */
        public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;

        /**
         * 最后一个子窗口
         */
        public static final int LAST_SUB_WINDOW = 1999;
  • 系统窗口(2000~2999)
        /**
         * 系统窗口;非应用程序创建
         */
        public static final int FIRST_SYSTEM_WINDOW     = 2000;

        /**
         * 状态栏,只能有一个状态栏。位于屏幕顶端,其他窗口都位于它下方
         */
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;

        /**
         * 搜索栏,只能有一个搜索栏,位于屏幕上方
         */
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;

        /**
         * 电话窗口,它用于电话交互(特别是呼入),置于所有应用程序之上,状态栏之下
         * 属于悬浮窗(并且给一个Activity的话按下HOME键会出现看不到桌面上的
         * 图标异常情况)
         */
        @Deprecated
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;

        /**
         * 系统警告提示窗口,出现在应用程序窗口之上,属于悬浮窗,但是会被禁止
         */
        @Deprecated
        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;

        /**
         * 锁屏窗口,显示在所有应用程序之上
         */
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;

        /**
         * 信息窗口,用于显示toast,不属于悬浮窗口,只显示在当前用户窗口之上
         */
        @Deprecated
        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;

        /**
         * 系统顶层窗口,显示在其他一切内容之上,此窗口不能获取输入焦点,否则影响锁屏
         */
        @Deprecated
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;

        /**
         * 电话优先,当锁屏时显示,此窗口不能获得输入焦点,否则影响锁屏
         */
        @Deprecated
        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;

        /**
         * 系统对话窗口
         */
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;

        /**
         * 锁屏时显示的对话框
         */
        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;

        /**
         * 系统内部错误提示,显示在任何窗口之上
         */
        @Deprecated
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;

        /**
         * 内部输入法窗口,显示于普通UI之上,应用程序可以重新布局以避免被此窗口覆盖
         */
        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;

        /**
         * 内部输入法对话框,显示于当前输入法窗口之上
         */
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;

        /**
         * 墙纸窗口
         */
        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;

        /**
         * 状态栏的滑动面板
         */
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;

        /**
         * 安全系统覆盖窗口,这些窗口必须不带输入焦点,否则会干扰键盘
         */
        @UnsupportedAppUsage
        public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;

        /**
         * Window type: the drag-and-drop pseudowindow.  There is only one
         * drag layer (at most), and it is placed on top of all other windows.
         * In multiuser systems shows only on the owning user's window.
         * @hide
         */
        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;

        /**
         * Window type: panel that slides out from over the status bar
         * In multiuser systems shows on all users' windows. These windows
         * are displayed on top of the stauts bar and any {@link #TYPE_STATUS_BAR_PANEL}
         * windows.
         * @hide
         */
        public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;

        /**
         * Window type: (mouse) pointer
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;

        /**
         * Window type: Navigation bar (when distinct from status bar)
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;

        /**
         * Window type: The volume level overlay/dialog shown when the user
         * changes the system volume.
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;

        /**
         * Window type: The boot progress dialog, goes on top of everything
         * in the world.
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;

        /**
         * Window type to consume input events when the systemUI bars are hidden.
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;

        /**
         * Window type: Dreams (screen saver) window, just above keyguard.
         * In multiuser systems shows only on the owning user's window.
         * @hide
         */
        public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;

        /**
         * Window type: Navigation bar panel (when navigation bar is distinct from status bar)
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;

        /**
         * Window type: Display overlay window.  Used to simulate secondary display devices.
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        @UnsupportedAppUsage
        public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;

        /**
         * Window type: Magnification overlay window. Used to highlight the magnified
         * portion of a display when accessibility magnification is enabled.
         * In multiuser systems shows on all users' windows.
         * @hide
         */
        public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;

        /**
         * Window type: Window for Presentation on top of private
         * virtual display.
         */
        public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;

        /**
         * Window type: Windows in the voice interaction layer.
         * @hide
         */
        public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;

        /**
         * Window type: Windows that are overlaid <em>only</em> by a connected {@link
         * android.accessibilityservice.AccessibilityService} for interception of
         * user interactions without changing the windows an accessibility service
         * can introspect. In particular, an accessibility service can introspect
         * only windows that a sighted user can interact with which is they can touch
         * these windows or can type into these windows. For example, if there
         * is a full screen accessibility overlay that is touchable, the windows
         * below it will be introspectable by an accessibility service even though
         * they are covered by a touchable window.
         */
        public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;

        /**
         * Window type: Starting window for voice interaction layer.
         * @hide
         */
        public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;

        /**
         * Window for displaying a handle used for resizing docked stacks. This window is owned
         * by the system process.
         * @hide
         */
        public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;

        /**
         * Window type: like {@link #TYPE_APPLICATION_ATTACHED_DIALOG}, but used
         * by Quick Settings Tiles.
         * @hide
         */
        public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;

        /**
         * Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is
         * reserved for screenshot region selection. These windows must not take input focus.
         * @hide
         */
        public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;

        /**
         * Window type: Window for Presentation on an external display.
         * @see android.app.Presentation
         * @hide
         */
        public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;

        /**
         * Window type: Application overlay windows are displayed above all activity windows
         * (types between {@link #FIRST_APPLICATION_WINDOW} and {@link #LAST_APPLICATION_WINDOW})
         * but below critical system windows like the status bar or IME.
         * <p>
         * The system may change the position, size, or visibility of these windows at anytime
         * to reduce visual clutter to the user and also manage resources.
         * <p>
         * Requires {@link android.Manifest.permission#SYSTEM_ALERT_WINDOW} permission.
         * <p>
         * The system will adjust the importance of processes with this window type to reduce the
         * chance of the low-memory-killer killing them.
         * <p>
         * In multi-user systems shows only on the owning user's screen.
         */
        public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;

        /**
         * 最后一个系统窗口
         */
        public static final int LAST_SYSTEM_WINDOW      = 2999;

5.窗口flags显示属性

// 窗口特征标记
public int flags;
// 当该window对用户可见时,允许锁屏
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
// 窗口后面的所有内容都变暗
public static final int FLAG_DIM_BEHIND        = 0x00000002;
// 窗口后面的所有内容都变模糊
public static final int FLAG_BLUR_BEHIND        = 0x00000004;
// 窗口不能获得焦点
public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
// 窗口不接受触摸屏事件
public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
// 即使在该window可获得焦点情况下,允许该窗口之处的点击事件传递到当前窗口后面的窗口去
public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
// 当手机处于睡眠状态,如果屏幕被按下,那么该window将第一个收到触摸事件
public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
// 当该window对用户可见时,屏幕处于常亮状态
public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;
// 让window占满整个屏幕,不留任何边界
public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;
// 允许窗口超出整个手机屏幕
public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;
// window全名显示
public static final int FLAG_FULLSCREEN      = 0x00000400;
// 恢复window非全屏显示
public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;
// 开启窗口抖动
public static final int FLAG_DITHER             = 0x00001000;
// 安全内容窗口,该窗口显示时不允许截屏
public static final int FLAG_SECURE             = 0x00002000;
// 一种特殊模式,在该模式下,布局参数用于在将表面合成到屏幕时执行缩放。
public static final int FLAG_SCALED             = 0x00004000;

public static final int FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000;

public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;

public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
// 接收单个特殊的MotionEvent,以应对窗口外发生的触摸。 
// 不会收到完整的向下/向上/向上手势,而只会收到第一次向下的位置作为ACTION_OUTSIDE。
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;
// 锁屏时显示该窗口
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
// 系统的墙纸显示在该窗口之后
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
// 当window被显示的时候,系统将把它当做一个用户活动事件,以点亮手机屏幕
public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
// 该窗口显示,消失键盘
public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
// 当window在可以接受触摸屏情况下,让因在该window之外,而发送到后面的window的
// 触摸屏可以支持split touch
public static final int FLAG_SPLIT_TOUCH = 0x00800000;
// 对该window进行硬件加速,该flag必须在activity或者dialog的content view之前进行设置
public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000;
// 让该window占满整个手机屏幕,不留任何边界
public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000;
// 透明状态栏
public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
// 透明导航栏
public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;

6.WindowManagerService的相关类

(1)WindowManagerService概念

Framework层的窗口管理服务,职责是管理Android系统中所有window。窗口管理服务,继承IWindowManager.Stub,Binder服务端,因此WM与WMS的交互也是一个IPC过程。WMS主要做的事情如下:

  • Z-ordered的维护函数
  • 输入法管理
  • AddWindow/RemoveWindow
  • Layerout
  • Token管理,AppToken
  • 活动窗口管理(FocusWindow)
  • 活动应用管理(FocusApp)
  • 转场动画
  • 系统消息收集线程
  • 系统消息分发线程
(2)WindowManager

应用与窗口管理服务WindowManagerService交互的接口

(3)PhoneWindowManager

实现了窗口的各种策略,定义了窗口相关策略,比如:告诉WMS某一个类型Window的Z-Order的值是多少,帮助WMS矫正不合理的窗口属性,为WMS监听屏幕旋转的状态,预处理一些系统按键事件(例如HOME,BACK键等的默认行为就是在这里实现的)等

(4)Choreographer

用户控制窗口动画、屏幕选择等操作,它拥有从显示子系统获取Vsync同步事件的能力,从而可以在合适的时机通知渲染动作,避免在渲染的过程中因为发生屏幕重绘而导致的画面撕裂。WMS使用Choreographer负责驱动所有的窗口动画、屏幕旋转动画、墙纸动画的渲染。

(5)DisplayContent
  • 用于描述多屏输出相关信息
  • 根据窗口的显示位置将其分组。隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每个DisplayContent都对应着唯一ID,在添加窗口的时候可以通过指定这个ID决定其将显示在哪个屏幕中。
  • DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。
(6)WindowState

描述窗口的状态信息以及和WindowManagerService进行通信,一般一个窗口对应一个WindowState。它用来表示一个窗口的所有属性。

(7)WindowToken
  • 窗口Token,用来做Binder通信;同时也是一种标识
  • 在进行窗口Zorder排序时,属于同一个WindowToken的窗口会被安排在一起,而且在其中定义的一些属性将会影响所有属于此WindowToken的窗口,这些都表明了属于同一个WindowToken的窗口之间的紧密联系
  • 应用组件在需要新的窗口时,必须提供WindowToken以表明自己的身份,并且窗口的类型必须与所持有的WindowToken的类型一致
  • 在创建系统类型的窗口时不需要提供一个有效的Token,WMS会隐式地为其声明一个WindowToken,看起来谁都可以添加个系统级的窗口。难道Android为了内部使用方便而置安全于不顾吗?非也,addWindow()函数一开始的mPolicy.checkAddPermission()的目的就是如此。它要求客户端必须拥有SYSTEM_ALERT_WINDOW或INTERNAL_SYSTEM_WINDOW权限才能创建系
(8)Session

App进程通过建立Session代理对象和Session对象通信,进而和WMS建立连接

(9)SurfaceFlinger

SurfaceFlinger负责管理Android系统的帧缓冲区(Frame Buffer),Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger服务就是通过向这个帧缓冲区写入内容来绘制应用程序中的用户界面的

(10)InputManager

IMS实例。管理每个窗口的输入事件通道(InputChannel)以及向通道上派发事件

(11)Animator

所有窗口动画的总管(WindowStateAnimator对象)。在Choreographer的驱动下,逐个渲染所有的动画。

二、WMS启动流程

WMS的启动,依然是由SystemServer进行启动,在SystemServer的main函数中调用startOtherServices方法创建WMS。

1.SystemServer#startOtherServices

    private void startOtherServices() {
        ...
        try {
            ...
            // 创建WMS对象
            // 创建WMS对象需要依赖于InputManager、PhoneWindowManager、和AMS
            // IMS是用来管理每个窗口的输入事件通道以及向通道上派发事件
            // PhoneWindowManager是实现了窗口的各种策略,定义了窗口相关策略
            // 比如:告诉WMS某一个类型Window的Z-Order的值是多少,帮助WMS矫正不合理的窗口属性,
            // 为WMS监听屏幕旋转的状态,预处理一些系统按键事件(例如HOME、BACK键等的默认行为就是在这里实现的)等
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
            traceEnd();

            traceBeginAndSlog("SetWindowManagerService");
            // 将AMS与WMS做绑定
            mActivityManagerService.setWindowManager(wm);
            traceEnd();

            traceBeginAndSlog("WindowManagerServiceOnInitReady");
            // 这里主要是调用initPolicy用来初始化PhoneWindowManager
            wm.onInitReady();
            traceEnd();
            ...
        }
        ...
        traceBeginAndSlog("MakeDisplayReady");
        try {
            // 主要是初始化UI尺寸
            wm.displayReady();
        } catch (Throwable e) {
            reportWtf("making display ready", e);
        }
        traceEnd();
        ...
        traceBeginAndSlog("MakeWindowManagerServiceReady");
        try {
            // 准备工作已经完成
            wm.systemReady();
        } catch (Throwable e) {
            reportWtf("making Window Manager Service ready", e);
        }
        traceEnd();
        ...
    }

2.WindowManagerService#main

WMS的main方法:是运行在SystemServer进程的主线程,但是WMS的初始化是在DisplayThread线程构建的。DisplayThread其实就是一个HandlerThread。
Handler中有一个runWithScissors方法:这个方法其实就是Handler.post消息执行之后能够再继续往下执行。即runWithScissors后面的内容,需要等待消息的处理完成之后才会继续执行。其实就是实现一个同步执行的过程。DisplayThread其实是android.display线程,这是系统共享的单例前台线程,在该线程内部创建了WMS

    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm) {
        return main(context, im, showBootMsgs, onlyCore, policy, atm,
                SurfaceControl.Transaction::new);
    }

WMS的main方法是在SystemServer进程的主线程调用的,然后在调用WMS的main方法的时候,通过DisplayThread这个HandlerThread,实现异步处理;这里的异步处理,其实是通过跨线程的一个同步,即通过DisplayThread中的Handler对象,调用runWithScissors方法,该方法其实是可以实现发送消息等待消息处理完成以后再继续执行runWithScissors方法调用位置之后的代码,即等待消息处理完之后再进行return WMS对象的返回操作。

    @VisibleForTesting
    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
        // wms是运行在android.display线程
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
                        atm, transactionFactory), 0);
        return sInstance;
    }

3.Handler#runWithScissors

根据上面的调用,上面的runWithScissors传入的就是一个Runnable实例,只不过是采用了lambda表达式的做法,所以Runnable的run()方法的方法体是就是:
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, transactionFactory), 0);

public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
    if (r == null) {
        throw new IllegalArgumentException("runnable must not be null");
    }
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout must be non-negative");
    }
    // 如果调用runWithScissors方法的线程是与消息处理线程在同一个线程
    // 则直接执行Runnable的run方法。
    if (Looper.myLooper() == mLooper) {
        r.run();
        return true;
    }
    // 如果调用runWithScissors方法的线程与消息处理线程不是同一个,则创建一个BlockingRunnable
    BlockingRunnable br = new BlockingRunnable(r);
    // 并且调用BlockingRunnable.postAndWait方法
    // 这里的返回值会先等待,等待DisplayThread线程中的Handler处理完
    // 消息之后,然后唤醒BlockingRunnable
    // 该Handler处理消息是在DisplayThread线程,而BlockingRunnable
    // 而BlockingRunnable调用wait并不会让DisplayThread线程中的
    // Handler等待,Handler依然会处理消息,处理消息其实就是调用了
    // BlockingRunnable.run()方法,当BlockingRunnable.run()执行完成
    // 就会调用notifyAll唤醒BlockingRunnable,执行postAndWait的return
    return br.postAndWait(this, timeout);
}

4.BlockingRunnable

private static final class BlockingRunnable implements Runnable {
    private final Runnable mTask;
    private boolean mDone;

    public BlockingRunnable(Runnable task) {
        mTask = task;
    }

    @Override
    public void run() {
        try {
            // 这个mTask其实就是runWithScissors中传入的Runnable对象
            // BlockingRunnable的run方法的执行,其实就是需要Handler遍历到消息处理的时候
            // 然后处理这个BlockingRunnable消息,在处理BlockingRunnable消息
            // 的时候,就会处理runWithScissors的参数中的Runnable这个mTask
            // 处理完成之后,就会调用notifyAll唤醒等待线程,然后执行postAndWait的return
            // 因为BlockingRunnable的run方法是由Handler处理消息
            // 这个消息的处理与BlockingRunnable是在不同的线程
            // 所以BlockingRunnable等待的时候,其run方法是可以由handler消息处理
            mTask.run();
        } finally {
            synchronized (this) {
                mDone = true;
                notifyAll();
            }
        }
    }

    public boolean postAndWait(Handler handler, long timeout) {
        // 调用postAndWait的时候,先调用handler对象的post将消息发送出
        // 如果入队成功,则为true,则开始阻塞
        if (!handler.post(this)) {
            return false;
        }
        // 在上面的方法调用中,timeout=0,则阻塞。
        synchronized (this) {
            if (timeout > 0) {
                final long expirationTime = SystemClock.uptimeMillis() + timeout;
                // 如果mDone为false的时候,则会等待,只有为true的时候,会执行
                while (!mDone) {
                    long delay = expirationTime - SystemClock.uptimeMillis();
                    if (delay <= 0) {
                        return false; // timeout
                    }
                    try {
                        wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            } else {
                while (!mDone) {
                    try {
                        wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }
        return true;
    }
}

5.WindowManagerService构造器

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
        installLock(this, INDEX_WINDOW);
        mGlobalLock = atm.getGlobalLock();
        mAtmService = atm;
        mContext = context;
        mAllowBootMessages = showBootMsgs;
        mOnlyCore = onlyCore;
        mLimitedAlphaCompositing = context.getResources().getBoolean(
                com.android.internal.R.bool.config_sf_limitedAlpha);
        mHasPermanentDpad = context.getResources().getBoolean(
                com.android.internal.R.bool.config_hasPermanentDpad);
        mInTouchMode = context.getResources().getBoolean(
                com.android.internal.R.bool.config_defaultInTouchMode);
        mDrawLockTimeoutMillis = context.getResources().getInteger(
                com.android.internal.R.integer.config_drawLockTimeoutMillis);
        mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
        mMaxUiWidth = context.getResources().getInteger(
                com.android.internal.R.integer.config_maxUiWidth);
        mDisableTransitionAnimation = context.getResources().getBoolean(
                com.android.internal.R.bool.config_disableTransitionAnimation);
        mPerDisplayFocusEnabled = context.getResources().getBoolean(
                com.android.internal.R.bool.config_perDisplayFocusEnabled);
        mLowRamTaskSnapshotsAndRecents = context.getResources().getBoolean(
                com.android.internal.R.bool.config_lowRamTaskSnapshotsAndRecents);
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mDisplayWindowSettings = new DisplayWindowSettings(this);

        mTransactionFactory = transactionFactory;
        mTransaction = mTransactionFactory.make();
        mPolicy = policy;
        // 窗口动画,管理系统里所有动画
        mAnimator = new WindowAnimator(this);
        // 总管家,对应于ActivityDisplay,用来管理DisplayContent
        mRoot = new RootWindowContainer(this);

        mWindowPlacerLocked = new WindowSurfacePlacer(this);
        mTaskSnapshotController = new TaskSnapshotController(this);

        mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,
                Choreographer.getInstance());

        LocalServices.addService(WindowManagerPolicy.class, mPolicy);

        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);

        mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH);
        // //电源管理相关的服务
        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);

        if (mPowerManagerInternal != null) {
            mPowerManagerInternal.registerLowPowerModeObserver(
                    new PowerManagerInternal.LowPowerModeListener() {
                @Override
                public int getServiceType() {
                    return ServiceType.ANIMATION;
                }

                @Override
                public void onLowPowerModeChanged(PowerSaveState result) {
                    synchronized (mGlobalLock) {
                        final boolean enabled = result.batterySaverEnabled;
                        if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
                            mAnimationsDisabled = enabled;
                            dispatchNewAnimatorScaleLocked(null);
                        }
                    }
                }
            });
            mAnimationsDisabled = mPowerManagerInternal
                    .getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled;
        }
        mScreenFrozenLock = mPowerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
        mScreenFrozenLock.setReferenceCounted(false);

        mActivityManager = ActivityManager.getService();
        mActivityTaskManager = ActivityTaskManager.getService();
        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
        AppOpsManager.OnOpChangedInternalListener opListener =
                new AppOpsManager.OnOpChangedInternalListener() {
                    @Override public void onOpChanged(int op, String packageName) {
                        updateAppOpsState();
                    }
                };
        mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener);
        mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener);

        mPmInternal = LocalServices.getService(PackageManagerInternal.class);
        final IntentFilter suspendPackagesFilter = new IntentFilter();
        suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
        suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
        context.registerReceiverAsUser(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String[] affectedPackages =
                        intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                final boolean suspended =
                        Intent.ACTION_PACKAGES_SUSPENDED.equals(intent.getAction());
                updateHiddenWhileSuspendedState(new ArraySet<>(Arrays.asList(affectedPackages)),
                        suspended);
            }
        }, UserHandle.ALL, suspendPackagesFilter, null, null);

        final ContentResolver resolver = context.getContentResolver();
        // Get persisted window scale setting
        mWindowAnimationScaleSetting = Settings.Global.getFloat(resolver,
                Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
        mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
                Settings.Global.TRANSITION_ANIMATION_SCALE,
                context.getResources().getFloat(
                        R.dimen.config_appTransitionAnimationDurationScaleDefault));

        setAnimatorDurationScale(Settings.Global.getFloat(resolver,
                Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting));

        mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver,
                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;

        IntentFilter filter = new IntentFilter();
        // Track changes to DevicePolicyManager state so we can enable/disable keyguard.
        filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);

        mLatencyTracker = LatencyTracker.getInstance(context);

        mSettingsObserver = new SettingsObserver();

        mHoldingScreenWakeLock = mPowerManager.newWakeLock(
                PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
        mHoldingScreenWakeLock.setReferenceCounted(false);

        mSurfaceAnimationRunner = new SurfaceAnimationRunner(mPowerManagerInternal);

        mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);

        mTaskPositioningController = new TaskPositioningController(
                this, mInputManager, mActivityTaskManager, mH.getLooper());
        mDragDropController = new DragDropController(this, mH.getLooper());

        mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
                DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                        KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
        mSystemGestureExcludedByPreQStickyImmersive =
                DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                        KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                new HandlerExecutor(mH), properties -> {
                    synchronized (mGlobalLock) {
                        final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
                                properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
                        final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
                                DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                                KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
                        if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
                                || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
                            mSystemGestureExclusionLimitDp = exclusionLimitDp;
                            mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
                            mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
                        }
                    }
                });
        // LocalServices与ServiceManager类似,但是LocalServices只能是在同一个进程中使用
        // 并且LocalServices中注册的并不是一个Binder对象,注册进的都是对应的Service的静态内部类
        // 也就是LocalService,比如WMS有一个WindowManagerInternal,LocalService就是其子类实现
        LocalServices.addService(WindowManagerInternal.class, new LocalService());
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容