我的
CSDN
博客同步发布:Window与WMS通信过程
转载请注明出处:【huachao1001的简书:http://www.jianshu.com/users/0a7e42698e4b/latest_articles】
上一篇文章【理清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
窗口要等到它的启动窗口显示了之后才可以显示出来。
参考资料: