Window与WMS通信过程
我的简书同步发布:Window与WMS通信过程
转载请注明出处:【huachao1001的专栏:http://blog.csdn.net/huachao1001】
上一篇文章【理清Activity、View及Window之间关系】我们大致知道了Window的绘制过程,但是比较笼统,本文主要介绍Window对象与(后面缩写为WMS)之间是如何通信。毫无疑问,肯定是通过IPC(Binder机制),这点肯定都知道,但是我们要学习是的是,哪些类参与了IPC调用过程。另外,本文没有研究源码,而是通过阅读其他研究源码的文章,然后总结出来,以更容易理解的方式展示。本文设计到的相关资料在文章最后一一列出。
1 Window添加的大致过程
Window的添加过程需要通过WindowManager的addView来实现,WindowManager是一个接口,真正的实现是WindowManagerImpl。而WindowManagerImpl全部是转移给WindowManagerGlobal来处理,WindowManagerImpl这种工作模式是典型的桥接模式。WindowManagerImpl内三大操作过程如下:
public void addView(View view,ViewGroup.LayoutParams params){ mGlobal.addView(view,params,mDisplay,mParantWindow); } public void updateViewLayout(View view,ViewGroup.LayoutParams params){ mGlobal.updateViewLayout(view,params); } public void removeView(View view){ mGlobal.removeView(view,false); }
从WindowManagerGlobal名称可以看出,它是一个全局的WindowManager,其内部维护如下几个列表:
private final ArrayList<View> mViews = new ArrayList<View>(); private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>() private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.layoutParams>(); private final ArraySet<View> mDyingViews = new ArraySet<View>();
其中:
mViews:存储所有Window所对应的View
mRoots:存储的是所有Window所对应的ViewRootImpl
mParams:存储的是所有Window所对应的布局参数
mDyingView:存储了那些正在被删除的View对象,或者说是那些已经调用了removeView方法但是删除操作还未完成的Window对象。
addView中通过如下方式将Window的一系列对象添加到列表中:
root=new ViewRootImpl(view.getContext(),display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams);
可以看到,到目前为止,只是把相应的对象存放到ArrayList列表中。后面还需要将View给显示出来。绘制View需要通过ViewRootImpl的setView方法来实现。在setView内部是通过requestLayout来完成一部刷新请求的。
public void requestLayout(){ if(!mHandlingLayoutInlayoutRequest){ checkThread(); mLayoutRequested=true; scheduleTraversals(); } }
其中scheduleTraversals是View绘制的入口!
下面我们看看ViewRootImpl是内部机制。
2 ViewRootImpl内部机制
ViewRootImpl用于管理窗口的根View,并和WMS进行交互。ViewRootImpl中有一个内部类: W,以及另一个内部类:ViewRootHandler。
W继承自IWindow.Stub。是一个Binder对象,用于接收WMS的各种消息, 如按键消息, 触摸消息等。
ViewRootHandler,是Handler的子类, W会通过Looper把消息传递给ViewRootHandler。
ViewRootImpl有一个W类型的成员mWindow,ViewRootImpl在构造函数中创建一个W的实例并赋值给mWindow。
在ViewRootImpl的setView方法(此方法运行在UI线程)中,会通过IPC的方式跨进程向WMS发起一个远程调用,从而将DecorView最终添加到Window上,在这个过程中,ViewRootImpl、DecorView和WMS会彼此向关联.
另外,WMS有时也需要向ViewRootImpl发送远程请求,比如,点击事件是由用户的触摸行为所产生的,因此它必须要通过硬件来捕获,跟硬件之间的交互自然是Android系统自己把握,Android系统将点击事件交给WMS来处理。WMS通过远程调用将事件发送给ViewRootImpl,在ViewRootImpl中,有一个方法,叫做dispatchInputEvent,最终将事件传递给DecorView。
3 Window与WMS之间的双向通信
接下来,由WindowSession来完成最后的Window添加过程。mWindowSession本身是一个IWindowSession类型对象,通过内部代理类Proxy访问远程Session类(Binder机制) 。在Session内部通过WMS来实现Window的添加。如此一来Window的添加请求就交给了WMS去处理了,如下图所示:
WindowManagerService
内部为每个应用保留一个单独的Session
,如下图所示:
前面我们说过,每个Window对应一个ViewRootImpl及一个View Tree。也就是说,每个Activity对应的Window(一个或多个)内通过其内置的ViewRootImpl完成向WMS的请求过程。
一个应用中的所有Activity共用一个Session,一个Window在WMS内部对应一个WindowState,WindowState维护窗口的状态以及根据适当的机制来调整窗口的状态。
如果一个Activity多个Window,如对话框、Popup类型、或者通过ViewManager将View直接加入WMS,等等。在这些情况下,一个Activity就会创建多个Window,相应的WMS中也会对应多个WindowState,如下图所示:
4 WMS控制窗口的显示
以下内容来自老罗的的博客,后面附有资料链接。
WMS服务大致按照以下方式来控制哪些窗口需要显示的以及要显在哪里:
每一个Activity窗口的大小都等于屏幕的大小,因此,只要对每一个Activity窗口设置一个不同的Z轴位置,然后就可以使得位于最上面的,即当前被激活的Activity窗口,才是可见的。
每一个子窗口的Z轴位置都比它的父窗口大,但是大小要比父窗口小,这时候Activity窗口及其所弹出的子窗口都可以同时显示出来。
对于非全屏Activity窗口来说,它会在屏幕的上方留出一块区域,用来显示状态栏。这块留出来的区域称对于屏幕来说,称为装饰区(decoration),而对于Activity窗口来说,称为内容边衬区(Content Inset)。
输入法窗口只有在需要的时候才会出现,它同样是出现在屏幕的装饰区或者说Activity窗口的内容边衬区的。
对于壁纸窗口,它出现需要壁纸的Activity窗口的下方,这时候要求Activity窗口是半透明的,这样就可以将它后面的壁纸窗口一同显示出来。
两个Activity窗口在切换过程,实际上就是前一个窗口显示退出动画而后一个窗口显示开始动画的过程,而在动画的显示过程,窗口的大小会有一个变化的过程,这样就导致前后两个Activity窗口的大小不再都等于屏幕的大小,因而它们就有可能同时都处于可见的状态。事实上,Activity窗口的切换过程是相当复杂的,因为即将要显示的Activity窗口可能还会被设置一个启动窗口(Starting Window)。一个被设置了启动窗口的Activity窗口要等到它的启动窗口显示了之后才可以显示出来。
参考资料:
1. 【Android窗口管理服务WindowManagerService的简要介绍和学习计划 】
2. 《Android开发艺术探索》
3. 【Android 应用程序建立与WMS服务之间的通信过程】
4. 【Android Window Manager Subsystem Research 】