WMS

WMS 职责/功能

窗口管理
WMS 是窗口的管理者,它负责窗口的启动、添加和删除,另外窗口的大小和层级也是由 WMS 进行管理的。窗口管理的核心成员有 DisplayContent、WindowToken 和 WindowState。

窗口动画
窗口间进行切换时,使用窗口动画可以显得更炫一些,窗口动画由 WMS 的动画子系统来负责,动画子系统的管理者为 WindowAnimator。

输入系统的中转站
通过对窗口的触摸从而产生触摸事件,InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS 是窗口的管理者,因此,WMS“理所应当” 的成为了输入系统的中转站。

Surface 管理
窗口并不具备有绘制的功能,因此每个窗口都需要有一块 Surface 来供自己绘制。为每个窗口分配 Surface 是由 WMS 来完成的。

WMS功能

WMS 的诞生

诞生过程中的线程切换

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService .java

public static WindowManagerService main(final Context context, final InputManagerService im,
           final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
           WindowManagerPolicy policy) {
       DisplayThread.getHandler().runWithScissors(() ->//1
               sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                       onlyCore, policy)//2
                                                  , 0);
       return sInstance;
   }

在注释 1 处调用了 DisplayThread 的 getHandler 方法,用来得到 DisplayThread 的 Handler 实例。DisplayThread 是一个单例的前台线程,这个线程用来处理需要低延时显示的相关操作,并只能由 WindowManager、DisplayManager 和 InputManager 实时执行快速操作。

在注释 2 处创建了 WMS 的实例,这个过程运行在 Runnable 的 run 方法中,而 Runnable 则传入到了 DisplayThread 对应 Handler 的 runWithScissors 方法中,说明 WMS 的创建是运行在 “android.display” 线程中。需要注意的是,runWithScissors 方法的第二个参数传入的是 0,后面会提到。来查看 Handler 的 runWithScissors 方法里做了什么:

frameworks/base/core/java/android/os/Handler.java

public final boolean runWithScissors(final 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");
       }
       if (Looper.myLooper() == mLooper) {//1
           r.run();
           return true;
       }
       BlockingRunnable br = new BlockingRunnable(r);
       return br.postAndWait(this, timeout);
   }

开头对传入的 Runnable 和 timeout 进行了判断,如果 Runnable 为 null 或者 timeout 小于 0 则抛出异常。注释 1 处根据每个线程只有一个 Looper 的原理来判断当前的线程("system_server" 线程)是否是 Handler 所指向的线程("android.display" 线程),如果是则直接执行 Runnable 的 run 方法,如果不是则调用 BlockingRunnable 的 postAndWait 方法,并将当前线程的 Runnable 作为参数传进去 ,BlockingRunnable 是 Handler 的内部类,代码如下所示。
frameworks/base/core/java/android/os/Handler.java

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.run();//1
            } finally {
                synchronized (this) {
                    mDone = true;
                    notifyAll();
                }
            }
        }
        public boolean postAndWait(Handler handler, long timeout) {
            if (!handler.post(this)) {//2
                return false;
            }
            synchronized (this) {
                if (timeout > 0) {
                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
                    while (!mDone) {
                        long delay = expirationTime - SystemClock.uptimeMillis();
                        if (delay <= 0) {
                            return false; // timeout
                        }
                        try {
                            wait(delay);
                        } catch (InterruptedException ex) {
                        }
                    }
                } else {
                    while (!mDone) {
                        try {
                            wait();//3
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
            return true;
        }
    }

注释 2 处将当前的 BlockingRunnable 添加到 Handler 的任务队列中。前面 runWithScissors 方法的第二个参数为 0,因此 timeout 等于 0,这样如果 mDone 为 false 的话会一直调用注释 3 处的 wait 方法使得当前线程(”system_server” 线程)进入等待状态,那么等待的是哪个线程呢?我们往上看,注释 1 处,执行了传入的 Runnable 的 run 方法(运行在”android.display” 线程),执行完毕后在 finally 代码块中将 mDone 设置为 true,并调用 notifyAll 方法唤醒处于等待状态的线程,这样就不会继续调用注释 3 处的 wait 方法。因此得出结论,”system_server” 线程线程等待的就是”android.display” 线程,一直到”android.display” 线程执行完毕再执行”system_server” 线程,这是因为”android.display” 线程内部执行了 WMS 的创建,显然 WMS 的创建优先级更高些。

WMS的拉起.png

“system_server” 线程中会调用 WMS 的 main 方法,main 方法中会创建 WMS,创建 WMS 的过程运行在”android.display” 线程中,它的优先级更高一些,因此要等创建 WMS 完毕后才会唤醒处于等待状态的”system_server” 线程。
WMS 初始化时会执行 initPolicy 方法,initPolicy 方法会调用 PWM 的 init 方法,这个 init 方法运行在”android.ui” 线程,并且优先级更高,因此要先执行完 PWM 的 init 方法后,才会唤醒处于等待状态的”android.display” 线程。
PWM 的 init 方法执行完毕后会接着执行运行在”system_server” 线程的代码,比如本文前部分提到 WMS 的
systemReady 方法。

WMS的重要成员

com.android.server.wm.WindowManagerService

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

final WindowManagerPolicy mPolicy;//WindowManagerPolicy是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了WindowManager所有的特定的UI行为。它的具体实现类为PhoneWindowManager,这个实现类在WMS创建时被创建。WMP允许定制窗口层级和特殊窗口类型以及关键的调度和布局。
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final AppOpsManager mAppOps;
final DisplaySettings mDisplaySettings;
...
final ArraySet<Session> mSessions = new ArraySet<>();//元素类型为Session,它主要用于进程间通信,其他的应用程序进程想要和WMS进程进行通信就需要经过Session,并且每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端。
final WindowHashMap mWindowMap = new WindowHashMap();//WindowHashMap继承了HashMap,它限制了HashMap的key值的类型为IBinder,value值的类型为WindowState。WindowState用于保存窗口的信息,在WMS中它用来描述一个窗口。综上得出结论,**mWindowMap就是用来保存WMS中各种窗口的集合**。
/**
WindowState用于保存窗口的信息,在WMS中它用来描述一个窗口。
*/
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();//用于存储已经完成启动的应用程序窗口(比如Acitivity)的AppWindowToken的列表。
/**
ArrayList类型的变量,元素类型为AppWindowToken,它是WindowToken的子类。要想理解mFinishedStarting的含义,需要先了解WindowToken是什么。WindowToken主要有两个作用:
可以理解为窗口令牌,当应用程序想要向WMS**申请新创建一个窗口**,则需要向WMS出示有效的WindowToken。AppWindowToken作为WindowToken的子类,主要用来描述应用程序的WindowToken结构,
应用程序中每个Activity都对应一个AppWindowToken。
WindowToken会将相同组件(比如Acitivity)的窗口(WindowState)集合在一起,方便管理。
*/
final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();//mFinishedEarlyAnim存储了已经完成窗口绘制并且不需要展示任何已保存surface的应用程序窗口的AppWindowToken。
final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();//mWindowReplacementTimeout存储了等待更换的应用程序窗口的AppWindowToken,如果更换不及时,旧窗口就需要被处理。
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();//ArrayList类型的变量,元素类型为WindowState。mResizingWindows是用来存储正在调整大小的窗口的列表。与mResizingWindows类似的还有mPendingRemove、mDestroySurface和mDestroyPreservedSurface等等.
final ArrayList<WindowState> mPendingRemove = new ArrayList<>();//mPendingRemove是在内存耗尽时设置的,里面存有需要强制删除的窗口。
WindowState[] mPendingRemoveTmp = new WindowState[20];
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();//mDestroySurface里面存有需要被Destroy的Surface。
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();//mDestroyPreservedSurface里面存有窗口需要保存的等待销毁的Surface,为什么窗口要保存这些Surface?这是因为当窗口经历Surface变化时,窗口需要一直保持旧Surface,直到新Surface的第一帧绘制完成。
...
final H mH = new H();//H类型的变量,系统的Handler类,用于将任务加入到主线程的消息队列中,这样代码逻辑就会在主线程中执行。
...
final WindowAnimator mAnimator;//WindowAnimator类型的变量,用于管理窗口的动画以及特效动画。
...
 final InputManagerService mInputManager//InputManagerService类型的变量,输入系统的管理者。InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS是窗口的管理者,因此,WMS“理所应当”的成为了输入系统的中转站。

Window的添加过程(WMS部分)

无论是系统窗口还是Activity,它们的Window的添加过程都会调用WMS的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) {...}//WMS的addWindow返回的是addWindow的各种状态,比如添加Window成功,无效的display等等,这些状态被定义在WindowManagerGlobal中。

这里的流程图待补充

addWindow方法总结

addWindow方法主要就是做了下面4件事:

  1. 对所要添加的窗口进行检查,如果窗口不满足一些条件,就不会再执行下面的代码逻辑。
  2. WindowToken相关的处理,比如有的窗口类型需要提供WindowToken,没有提供的话就不会执行下面的代码逻辑,有的窗口类型则需要由WMS隐式创建WindowToken。
  3. WindowState的创建和相关处理,将WindowToken和WindowState相关联。
  4. 创建和配置DisplayContent,完成窗口添加到系统前的准备工作。

Window的删除过程(全部流程)

删除窗口.png

removeView的主要过程如下:

  1. 检查删除线程的正确性,如果不正确就抛出异常。
  2. 从ViewRootImpl列表、布局参数列表和View列表中删除与V对应的元素。
  3. 判断是否可以直接执行删除操作,如果不能就推迟删除操作。
  4. 执行删除操作,清理和释放与V相关的一切资源。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容