深入WindowManager
WindowManager究竟起到了什么作用?
WindowManager
的重要程度不需要多说, 界面真正的显示, 外部事件的传递等. 这是一个子系统. 本章会梳理WindowManager
,WindowManagerService
,Surface
,SurfaceFlinger
之间的联系与交互的一个过程.
如果想了解不同的window级别的区别,如Activity,Dialog,Toast等请看另一篇blog
在此之前需要知道这么几件事情:
- 我们设置的
View
视图并不能直接显示在界面上. 而是需要依附在Window
窗口上. 通过WindowManager
操作才可以显示. Window
是一个抽象的概念, 它可以说以DecorView
的形式表现其存在.- 代码层面
Window
是一个抽象类, 具体实现类是PhoneWindow
应用WindowManager的由来
对于重要的系统服务在手机开机的时候进行初始化, 然后保存在了系统进程
中. 当我们应用启动的时候会触发ContextImpl
类的加载, 在类加载的时候通过静态代码块
对各个系统服务进行进行了注册,并保存到一个静态map容器
中, 之后就可以通过getSystemService(serviceName)
的形式获取不同的系统服务.
WM与Window的关联
那么当创建一个Dialog
, 就需要创建一个Window
, 并把Window
与WM
进行关联. 那么可以直接看看Dialog的构造函数
是如何做的.
Dialog(Context context, int theme, boolean createContextThemeWrapper) {
// 从ContextImpl的集合获取WM系统服务
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
// 构建Window对象
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
// 实现Window.Callback接口, 为了后续的事件的传递
w.setCallback(this);
// Window 和 WM 进行关联
w.setWindowManager(mWindowManager, null, null);
}
关联的代码已经看到, 可以看一下内部调用顺序
// Window类
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
// 继续跟踪
// WindowManagerImpl类
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}
最后创建了一个WindowManagerImpl
对象, 与ContextImpl
注册WindowManagerImpl
不同的是, 这里多了一个参数parentWindow
. 这也就说此时构建的WindowManagerImpl
对象是与具体的Window
关联的,
刚才说到了WindowManagerImpl
类. 看一下这个类的大体你会发现. 这个类没有具体的逻辑处理, 而是把所有的逻辑利用桥接模式
转给了WindowMangerGlobal
这个对象的对应方法. 例如addView()
,removeViewImmediate()
,updateViewLayout()
等.
View的显示过程
刚才说过WindowManager
的实现类WindowManagerImpl
只是通过桥接模式
将事件传递给了WindowManagerGlobal
. 那么对应的addView()
逻辑肯定也在WindowManagerGlobal
中.
// 手机窗口显示View的入口, View被添加到WM中
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
// 省略非关键代码
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// 1.构建了ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
// 2.设置View的布局参数
view.setLayoutParams(wparams);
// 3.分别对View, ViewRootImpl,param进行存储
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try{
// 4.通过ViewRootImpl#setView()将View显示到手机窗口
root.setView(view, wparams, panelParentView);
}
}
上面的代码中出现了一个ViewRootImpl
对象, 不要认为它是一个View
! 它继承自Handler
类,是native层和java层View系统通信的桥梁
. 例如performTraversals()
就是收到系统绘制的View消息后, 通过调用是视图树的各个节点的绘制流程方法如measure
,layout
,draw
来完成整棵树的视图.
WMS
是运行在Native层
的, 而现在只是在Framewrok层
. 那么之间的是如何建立联系的?
Native和Framework的连接
既然ViewRootImpl
是通信的桥梁, 那么就看看其构造函数的创建.
public ViewRootImpl(Context context, Display display) {
// 省略非重要代码
// 获取Window Session, 也就是与WindowManagerService建立连接
mWindowSession = WindowManagerGlobal.getWindowSession();
// 保存创建本对象时的线程, 用于在更新UI的时候只能是mThread线程的条件判断
// 这也就为什么需要在UI线程才可以更新, 因为`ViewRootImpl`是在UI线程创建的
mThread = Thread.currentThread();
}
继续看看getWinSession()
这个重要的函数
// WindowManagerGlobal
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
// 1. 获取了WindowManagerService
IWindowManager windowManager = getWindowManagerService();
// 2. 与WindowManagerService建立了一个Session, 并返回
sWindowSession = windowManager.openSession(...);
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
// 看一下获取WMS的实现
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
// 典型的Binder通信
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
}
return sWindowManagerService;
}
}
getWindowManagerService()
函数首先获取了IWindowManager
对象, 本质就是一个远程对应的Binder
对象. 而远程WMS的Binder
获取是通过ServiceManager.getService()
实现的.
这里就可以总结为: Android Framework
与WMS
之间的通信也是通过Binder
机制进行的. 这里就建立了与WMS
的通信. 最后通过openSession()
函数来与WMS
建立一个通信会话, 相当于建立了一个长期的处理中心
, 双方有什么需要够可以通过这个Session
来交换信息.
但是! 此时不管是Dialog
或者Activity
的View还没有显示在手机屏幕上, WMS
只是负责管理手机屏幕上的View的z-order
, 也就是WMS
管理当前状态下哪一个View
应该在最上层显示. 所以WMS
管理的并不是Window
而是View
, 只不过它管理的是属于某个Window
下的View
.
之前分析ViewRootImpl
的构造函数结束了, 继续回View
的显示问题上. 当与WMS
建立Session
之后就会调用ViewRootImpl#setView()
函数, 该方法会向WMS
发起显示Dialog
或者Activity的DecorView
的请求.
setView()
函数这里只关心两步:
requestLayout()
: 请求布局- 通过
WindowSession#add()
向WMS
发起显示Window
的请求
先看一个请求布局的方法
@Override // ViewRootImpl类
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 检测当前线程是否是创建ViewRootImpl对象的线程
// 通常子线程更新的崩溃是在这里抛出的异常
checkThread();
mLayoutRequested = true;
// 向 handler 发送 DO_TRAVERSAL 消息
scheduleTraversals();
}
}
在scheduleTraversals()
发送消息之后, 就会触发整个视图树的绘制操作, 最终会触发执行performTraversals()
. 这个函数主要做的操作如下:
- 获取
Surface
对象, 用于图形绘制 - 测量整个视图树的各个
View
的大小, 触发performMeasure()
- 布局整个视图树, 触发
performLayout()
- 绘制整颗视图树, 触发
performDraw()
在第四步中, Framework
会获取到图形绘制表面Surface
对象, 然后获取它的可绘制区域, 也就是常用的Canvas
对象, 然后Framework
在这个Canvas
对象上绘制. 而在调用过程中通过draw()
方法主要做了如下几个事情:
- 判断是使用
CPU
还是GPU
来进行绘制 - 获取绘制表面
Surface
对象 - 通过
Surface
对象获取并且锁住Canvas
绘图对象 - 从
DecorView
开始发起整颗视图树的绘制流程 Surface
对象解锁Canvas
, 并且通知SurfaceFlinger
当内容绘制完毕之后, 请求WMS
显示该窗口上的内容, 至此,Activity``Dialog
等组件的View
就显示到了屏幕上. 这里整理的只是比较高层次的整体脉络. 整个WMS
系统是即为复杂的, 涉及的概念和技术很是很多方面. 比如Surface
等.
最后提出<Android源码设计模式解析与实战>
一书的59页简化结构图