1.窗口Window
一个app从启动到主窗口显示出来,需要app、AMS、WMS、SurfaceFlinger(SF)等几个模块相互合作。app负责业务逻辑,绘制自己的视图;AMS管理组件、进程信息和Activity的堆栈及状态等;WMS管理Activity对应的窗口、子窗口以及系统窗口等(也就是控制ViewRoot之间的组合排版);SF用于管理图形缓冲区,将app绘制的东西(也就是ViewRoot)合成渲染在屏幕上。
Android中真正展示给用户的并不是Activity,而是Window和View,Activity的作用就是处理一些逻辑问题,比如生命周期管理以及建立窗口。
view不能单独存在,它必须要附着在Window这个抽象概念上。每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此有视图的地方就有Window,比如Activity、Dialog、Toast等。说明View是Window存在的实体,Window是实际view的直接管理者。在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。
Android的窗口分为三种类型:
①系统窗口(System Window):系统设计的,可以单独存在,不依附于任何应用的窗口,比如状态栏(Status Bar)、导航栏(Navigation Bar)、壁纸(Wallpaper)、来电显示窗口(Phone)、锁屏窗口(KeyGuard)、信息提示窗口(Toast)、音量调整窗口、鼠标光标等。
②子窗口(Sub Window):比如应用自定义的对话框或者输入法窗口,子窗口必须依附于某个应用窗口(设置相同的token)。
③应用程序窗口 (Application Window):对应一个Activity。
Window是一个抽象类,它的具体实现是PhoneWindow。创建一个Window需要通过WindowManager来完成,WindowManager是外界访问Window的入口,WindowManager的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。
整个Android的窗口机制是基于WindowManager接口的,这个接口可以添加view到屏幕,也可以从屏幕删除view。它面向的对象一端是屏幕,另一端就是View,直接忽略Activity或Dialog(其实Activity和Dialog的底层实现也是通过WindowManager)。WindowManager是全局的,它是显示View的最底层了。因此使用WindowManager可以实现在桌面上可移动的悬浮窗,比如流量统计、桌面歌词等。
2.WindowManager
WindowManager主要用来管理Window,它的实现类是WindowManagerImpl,如果想对Window进行添加、删除和更新操作,就可以使用WindowManager,具体工作是由WMS处理的。
Window、WindowManager和WMS:
Window是一个抽象类,具体的实现类为PhoneWindow,它对View进行管理;
WindowManager是一个接口类,继承自接口ViewManager,用来管理Window,它的实现类为WindowManagerImpl;
WindowManger最终会将具体的工作交给WMS来处理,WindowManager和WMS通过Binder来进行跨进程通信。
总的来说就是WindowManger将工作交给WMS来处理,并且对Window进行管理,也就是对View进行管理。
Window包含了View并对View进行管理,Window用虚线来表示是因为Window是一个抽象概念,并不是真实存在的,Window的实体其实也是View。WindowManager用来管理Window,而WindowManager所提供的功能最终会由WMS来进行处理。
WindowManager使用方法:
①获取WindowManager对象
WindowManager mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
②设置WindowManager.LayoutParams,主要是type参数,这个参数决定了窗口的类型。比如定义成一个Toast窗口,Toast属于系统窗口,不需要处理父窗口、子窗口之类的事
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
wmParams.format = PixelFormat.RGBA_8888;
wmParams.width = 800;
wmParams.height = 800;
③添加View到WindowManager
mWindowManager.addView(mView, wmParams);
3.Window的内部机制
WindowManager对Window主要有三大操作:添加、更新和删除。这三个方法定义在ViewManager接口中:
public interface ViewManager{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
WindowManager也是一个接口,它继承了ViewManager接口:
public interface WindowManager extends ViewManager {
}
WindowManager也继承了这三个方法,而这些方法传入的参数都是View,说明WindowManager具体管理的是Window中的view。WindowManager在继承ViewManager的同时,又加入很多功能,包括Window的类型和层级相关的常量、内部类以及一些方法,其中有两个方法是根据Window的特性加入的:
public Display getDefaultDisplay();
public void removeViewImmediate(View view);
getDefaultDisplay方法会得知这个WindowManager实例将Window添加到哪个屏幕上,换句话说,就是得到WindowManager所管理的屏幕(Display)。removeViewImmediate方法则规定在这个方法返回前要立即执行View.onDetachedFromWindow()来完成传入的View相关的销毁工作。
WindowManager的具体实现类是WindowManagerImpl:
public final class WindowManagerImpl implements WindowManager{
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(View view, ViewGroup.LayoutParams params){
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params){
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view){
mGlobal.removeView(view, false);
}
}
WindowManagerImpl并没有直接实现Window的三大操作,而是交给了WindowManagerGlobal。WindowManagerGlobal是一个单例,即在一个进程中只有一个实例。
WindowManagerImpl的这种工作模式是典型的桥接模式,Window为抽象部分,WindowManagerImpl为实现部分,WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给WindowManagerGlobal来处理。
最终通过WindowManagerGlobal的addView()、updateViewLayout()、removeView()实现了WindowManager对Window的添加、删除和修改。
4.Window三大操作
(1)添加过程addView
WindowManagerGlobal.java:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
……
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
synchronized (mLock) {
……
// 1.构建ViewRootImpl,作为native层与java层view系统通信的桥梁
root = new ViewRootImpl( view.getContext(), display);
//2.给view设置布局参数
view.setLayoutParams(wparams);
//3.存储ViewRootImpl、View以及LayoutParams到对应的列表中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//4.调用ViewRootImpl的setView方法将view显示到手机窗口中
root.setView(view, wparams, panelParentView);
}
}
ViewRootImpl是一个视图层次结构的顶部,它实现了View与WindowManager之间所需要的协议,作为WindowManagerGlobal中大部分的内部实现。因此在WindowManagerGlobal的实现方法中,都可以见到ViewRootImpl,也就是说WindowManagerGlobal方法最后还是调用到了ViewRootImpl(ViewRootImpl并不是一个View,它是作为native层与java层View系统通信的桥梁)。
接下来看一下ViewRootImpl如何通过setView()方法将视图添加到WindowManager的。
ViewRootImpl.java:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
requestLayout(); //1.请求布局
res = mWindowSession.addToDisplay( mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); //2.向WMS发起显示Window的请求
}
}
先看requestLayout方法:
ViewRootImpl.java:
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();//发送DO_TRAVERSAL
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
就是往handler发送一个DO_TRAVERSAL消息,这个消息会触发整个视图树的绘制操作,也就是最终会执行performTraversals函数,这是一个极为复杂的但又非常重要的函数。
private void performTraversals() {
... ...
//1.获取Surface对象,用于图形绘制
//2.丈量整个视图树的各个View的大小,performMeasure函数
//3.布局整个视图树,performLayout函数
//4.绘制整棵视图树,performDraw函数
}
在performDraw函数中,Framework会获取到图形绘制表面Surface对象,然后获取它的可绘制区域,也就是Canvas对象,然后Framework在这个Canvas对象上绘制,通知SurfaceFlinger更新视图。
其实requestLayout()方法通过Handler发送一个Message,排在所有WMS发送过来的消息之前先布局绘制一次,之后才会处理WMS传来的各种事件,比如触摸事件等。毕竟要首先将各个View的布局、位置处理好才能准确的处理WMS传来的事件。接着通过mWindowSession.addToDisplay真正的添加窗口,虽然requestLayout()执行在前,但是用的是Handler发消息的方式来处理,其Runable一定是在addToDisplay之后执行。
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
addToDisplay方法中会调用WMS的addWindow方法,并将自身也就是Session作为参数传进去,每个应用程序进程都会对应一个Session,WMS会用ArrayList来保存这些Session。这样