注:本文解析的源码基于 API 25,部分内容来自于《Android开发艺术探索》。
第一篇:《Window源码解析(一):与DecorView的那些事》
Header
在上一篇中,我们讲了 Window 和 DecorView 的那些事,如果没有看过的同学请点击这里:《Window源码解析(一):与DecorView的那些事》。
而今天就要来详细了解 Window 的添加机制了,到底在 WindowManager.addView 中做了什么事情?我们一起来看看吧!!
Window的添加机制
上面我们看到了在 makeVisible()
中调用了 wm.addView(mDecor, getWindow().getAttributes())
将 DecorView 视图添加到 Window 上。
那么调用这句代码之后究竟发生了什么呢,这就需要我们一步一步慢慢去揭开了。
WindowManager
WindowManager 是一个接口,继承了 ViewManager 。
1 | public interface ViewManager |
可以看到,ViewManager 中定义的方法非常熟悉,也是平时我们经常使用的,就是对 View 的增删改。
对 WindowManager 具体的实现就是 WindowManagerImpl 这个类了。在后面我们会接触到它的。
那么,我们就开始吧。
WindowManagerImpl
addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
1 | @Override |
发现在 WindowManagerImpl 也没有直接实现 View 的添加,而是转交给了 WindowManagerGlobal 类来做这件事。其实除了 addView
之外,updateViewLayout
和 removeView
也都是通过 WindowManagerGlobal 来实现的,这是桥接模式的体现。
那么我们继续跟下去。
WindowManagerGlobal
addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)
1 | public void addView(View view, ViewGroup.LayoutParams params, |
在 addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)
中,我们捋一捋它干了什么事:
- 检查了参数,如果是子 Window 的话,还要调整参数;
- 创建 ViewRootImpl ,然后将当前界面的参数保存起来;
- 调用 ViewRootImpl 的 setView 来更新界面并完成 Window 的添加;
可以看出,Window 的添加还需要我们到 ViewRootImpl.setView
中去看,同时也即将开启 View 三大工作流程。
ViewRootImpl
setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
1 | public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { |
在这里,View 也开始了测量、布局、绘制的三大流程。
之后,利用 mWindowSession
来添加 window ,mWindowSession
的类型是 IWindowSession ,它是一个 Binder 对象,其真正的实现类是 Session 。所以这是一个 IPC 的过程。这步具体的实现我们下面再看。
在添加完成后,根据返回值 res 来判断添加 window 是否成功。若不是 WindowManagerGlobal.ADD_OKAY 则说明添加失败了,抛出对应的异常。
Session
addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel)
1 | @Override |
在 Session 中,发现添加 Window 的操作交给了 mService ,而 mService 其实就是 WindowManagerService 。终于来到了最终 boss 这里了,那我们直击要害吧!
WindowManagerService
addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel)
1 | public int addWindow(Session session, IWindow client, int seq, |
在 WindowManagerService 中做的事情有很多,一开始利用 mPolicy.checkAddPermission
检查了权限,这里面可大有文章,利用 type = WindowManager.LayoutParams.TYPE_TOAST
来跳过权限显示悬浮窗的故事就来自于这里。想详细了解的同学请看《Android 悬浮窗权限各机型各系统适配大全》。
然后就是校验了一些参数,比如 token 。token 是用来表示窗口的一个令牌,其实是一个 Binder 对象。只有符合条件的 token 才能被 WindowManagerService 通过并添加到应用上。
再然后就是创建了一个 WindowState 对象,利用这个对象按照显示次序插入 mWindows 列表中,最后就是依据排序来确定窗口的最终显示次序。并返回了 Window 添加的结果 res 。
到这,整个添加 Window 的过程就结束了。
Footer
Window 添加其实就是一个 IPC 的过程,而更新和删除 Window 也是如此,基本上步骤都是相似的。
接下来就顺便把 Window 更新和删除的流程都梳理一遍吧。
静静等待此系列第三篇出炉!