深入WindowManager

WindowManager究竟起到了什么作用?

Posted by Suzeyu on 2016-11-28

WindowManager的重要程度不需要多说, 界面真正的显示, 外部事件的传递等. 这是一个子系统. 本章会梳理WindowManager,WindowManagerService,Surface,SurfaceFlinger之间的联系与交互的一个过程.

如果想了解不同的window级别的区别,如Activity,Dialog,Toast等请看另一篇blog

在此之前需要知道这么几件事情:

  1. 我们设置的View视图并不能直接显示在界面上. 而是需要依附在Window窗口上. 通过WindowManager操作才可以显示.
  2. Window是一个抽象的概念, 它可以说以DecorView的形式表现其存在.
  3. 代码层面Window是一个抽象类, 具体实现类是PhoneWindow

应用WindowManager的由来

对于重要的系统服务在手机开机的时候进行初始化, 然后保存在了系统进程中. 当我们应用启动的时候会触发ContextImpl类的加载, 在类加载的时候通过静态代码块对各个系统服务进行进行了注册,并保存到一个静态map容器中, 之后就可以通过getSystemService(serviceName)的形式获取不同的系统服务.

WM与Window的关联

那么当创建一个Dialog, 就需要创建一个Window, 并把WindowWM进行关联. 那么可以直接看看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 FrameworkWMS之间的通信也是通过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()函数这里只关心两步:

  1. requestLayout(): 请求布局
  2. 通过WindowSession#add()WMS发起显示Window的请求

先看一个请求布局的方法

@Override // ViewRootImpl类
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 检测当前线程是否是创建ViewRootImpl对象的线程
// 通常子线程更新的崩溃是在这里抛出的异常
checkThread();
mLayoutRequested = true;
// 向 handler 发送 DO_TRAVERSAL 消息
scheduleTraversals();
}
}

scheduleTraversals()发送消息之后, 就会触发整个视图树的绘制操作, 最终会触发执行performTraversals(). 这个函数主要做的操作如下:

  1. 获取Surface对象, 用于图形绘制
  2. 测量整个视图树的各个View的大小, 触发performMeasure()
  3. 布局整个视图树, 触发performLayout()
  4. 绘制整颗视图树, 触发performDraw()

在第四步中, Framework会获取到图形绘制表面Surface对象, 然后获取它的可绘制区域, 也就是常用的Canvas对象, 然后Framework在这个Canvas对象上绘制. 而在调用过程中通过draw()方法主要做了如下几个事情:

  1. 判断是使用CPU还是GPU来进行绘制
  2. 获取绘制表面Surface对象
  3. 通过Surface对象获取并且锁住Canvas绘图对象
  4. DecorView开始发起整颗视图树的绘制流程
  5. Surface对象解锁Canvas, 并且通知SurfaceFlinger

当内容绘制完毕之后, 请求WMS显示该窗口上的内容, 至此,Activity``Dialog等组件的View就显示到了屏幕上. 这里整理的只是比较高层次的整体脉络. 整个WMS系统是即为复杂的, 涉及的概念和技术很是很多方面. 比如Surface等.

最后提出<Android源码设计模式解析与实战>一书的59页简化结构图

下一篇WindowManagerService的认知