攻略大全
1. 粘贴攻略
2. 造火箭攻略
2.1 Window、WindowManager和WMS
2.2 WindowManager的关联类
从上图可以看出,PhoneWindow继承自Window,Window通过setWindowManager方法与WindowManager发生关联。WindowManager 继承自接口ViewManager,WindowManagerImpl是WindowManager 接口的实现类,但是具体的功能都会委托给WindowManagerGlobal来实现。
3. 拧螺丝攻略
3.1 Window、WindowManager和WMS
Window是一个抽象类,具体的实现类为PhoneWindow,它对View进行管理。
WindowManager是一个接口类,继承自接口ViewManager,从名称就知道它是用来管理Window的,它的实现类为WindowManagerImpl。
如果我们想要对Window(View)进行添加、更新和删除操作就可以使用WindowManager,WindowManager会将具体的工作交由WMS来处理,WMS的的主要功能就是管理Window。
WindowManager和WMS通过Binder来进行跨进程通信,WMS作为系统服务有很多API是不会暴露给WindowManager的,这一点与ActivityManager和AMS的关系有些类似。
Window、WindowManager和WMS的关系可以简略地用下图来表示。Window包含了View并对View进行管理,Window用虚线来表示是因为Window是一个抽象概念,用来描述一个窗口,并不是真实存在的,Window 的实体其实也是View。WindowManager用来管理Window,而WindowManager所提供的功能最终会由WMS进行处理。
3.2 WindowManager的关联类
WindowManager 是一个接口类,继承自接口ViewManager,ViewManager 中定义了3个方法,分别用来添加、更新和删除View:
/** Interface to let you add and remove child views to an Activity. To get an instance
* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*
* 允许您向 Activity 添加和删除子视图的界面。要获取此类的实例,
* 请调用 {@link android.content.ContextgetSystemService(java.lang.String) Context.getSystemService()}。
*/
public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
*
* 将传递的 LayoutParams 分配给传递的 View 并将视图添加到窗口中。
* <p>针对某些编程错误抛出 {@link android.view.WindowManager.BadTokenException},例如在不删除第一个视图的情况下向窗口添加第二个视图。
* <p>如果窗口在辅助 {@link Display} 上并且找不到指定的显示(请参阅 {@link android.app.Presentation}),
* 则抛出 {@link android.view.WindowManager.InvalidDisplayException}。
*
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
WindowManager 也继承了这些方法,而这些方法传入的参数都是View 类型,说明Window是以View的形式存在的。WindowManager在继承ViewManager的同时,又加入很多功能,包括Window 的类型和层级相关的常量、内部类以及一些方法,其中有两个方法是根据Window的特性加入的,如下所示:
/**
* Returns the {@link Display} upon which this {@link WindowManager} instance
* will create new windows.
* <p>
* Despite the name of this method, the display that is returned is not
* necessarily the primary display of the system (see {@link Display#DEFAULT_DISPLAY}).
* The returned display could instead be a secondary display that this
* window manager instance is managing. Think of it as the display that
* this {@link WindowManager} instance uses by default.
* </p><p>
* To create windows on a different display, you need to obtain a
* {@link WindowManager} for that {@link Display}. (See the {@link WindowManager}
* class documentation for more information.)
* </p>
*
* 返回此 {@link WindowManager} 实例将创建新窗口的 {@link Display}。
* <p> 尽管有此方法的名称,但返回的显示不一定是系统的主显示(请参阅 {@link DisplayDEFAULT_DISPLAY})。
* 返回的显示可能是此窗口管理器实例正在管理的辅助显示。将其视为此 {@link WindowManager} 实例默认使用的显示器。<p>
* <p> 要在不同的显示器上创建窗口,您需要为该 {@link Display} 获取一个 {@link WindowManager}。
* (有关详细信息,请参阅 {@link WindowManager} 类文档。)<p>
*
* @return The display that this window manager is managing.
* @deprecated Use {@link Context#getDisplay()} instead.
*/
@Deprecated
public Display getDefaultDisplay();
/**
* Special variation of {@link #removeView} that immediately invokes
* the given view hierarchy's {@link View#onDetachedFromWindow()
* View.onDetachedFromWindow()} methods before returning. This is not
* for normal applications; using it correctly requires great care.
*
* {@link removeView} 的特殊变体,在返回之前立即调用给定视图层次结构的
* {@link ViewonDetachedFromWindow() View.onDetachedFromWindow()} 方法。
* 这不适用于正常应用;正确使用它需要非常小心。
*
* @param view The view to be removed.
*/
public void removeViewImmediate(View view);
-
getDefaultDisplay方法能够得知这个WindowManager实例将Window添加到哪个屏幕上了,换句话说,就是得到WindowManager所管理的屏幕(Display)。
-
removeViewImmediate方法则规定在这个方法返回前要立即执行View.onDetachedFromWindow(),来完成传入的View相关的销毁工作。
Window是一个抽象类,它的具体实现类为PhoneWindow,PhoneWindow是何时创建的呢?在Activity 启动过程中会调用ActivityThread的performLaunchActivity 方法,performLaunchActivity方法中又会调用Activity的attach方法,PhoneWindow就是在Activity的attach方法中创建的,如下所示:
在注释1处创建了PhoneWindow,在注释2处调用了PhoneWindow的setWindowManager方法,这个方法在PhoneWindow的父类Window中实现。
如果传入的WindowManager为null,就会在注释1处调用Context的getSystemService方法,并传入服务的名称Context.WINDOW_SERVICE(值为window),具体在ContextImpl中实现,如下所示:
在getSystemService方法中会调用SystemServiceRegistry的getSystemServiceName方法:
SYSTEM_SERVICE_NAMES是一个HashMap类型的数据,它用来存储服务的名称,那么传入的Context.WINDOW_SERVICE到底对应着什么?我们接着往下看:
在SystemServiceRegistry 的静态代码块中会调用多个registerService方法。
registerService 方法内部会将传入的服务的名称存入到SYSTEM_SERVICE_NAMES中。
从注释1处可以看出,传入的Context.WINDOW_SERVICE对应的就是WindowManagerImpl实例,因此得出结论,Context的getSystemService方法得到的是WindowManagerImpl实例。
我们再回到Window的setWindowManager方法,在注释1处得到WindowManagerImpl 实例后转为WindowManager 类型,在注释2处调用了WindowManagerImpl的createLocalWindowManager方法:
createLocalWindowManager方法同样也是创建WindowManagerImpl,不同的是这次创建WindowManagerImpl 时将创建它的Window 作为参数传了进来,这样WindowManagerImpl就持有了Window的引用,可以对Window进行操作,比如在Window中添加View,会调用WindowManagerImpl的addView方法,如下所示:
在注释1处调用了WindowManagerGlobal的addView方法,其中最后一个参数mParentWindow就是上面提到的Window,可以看出WindowManagerImpl虽然是WindowManager的实现类,但是没有实现什么功能,而是将功能实现委托给了WindowManagerGlobal,这里用到的是桥接模式。
我们来查看在WindowManagerImpl中是如何定义WindowManagerGlobal的,如下所示:
在注释1处可以看出WindowManagerGlobal是一个单例,说明在一个进程中只有一个WindowManagerGlobal实例。
注释2处的代码结合注释3处的代码说明这个WindowManagerImpl实例会作为哪个Window的子Window,这也就说明在一个进程中WindowManagerImpl可能会有多个实例。
通过如上的源码分析,WindowManager的关联类如图所示。
从上图可以看出,PhoneWindow继承自Window,Window通过setWindowManager方法与WindowManager发生关联。WindowManager 继承自接口ViewManager,WindowManagerImpl是WindowManager 接口的实现类,但是具体的功能都会委托给WindowManagerGlobal来实现。
3.3 Window的属性
WMS是Window的最终管理者,Window好比是员工,WMS是老板,为了方便老板管理员工则需要定义一些“协议”,这些“协议”就是Window的属性,它们被定义在WindowManager的内部类LayoutParams中,了解Window的属性能够更好地理解WMS的内部原理。Window的属性有很多种,与应用开发最密切的有3种,它们分别是Type(Window的类型)、Flag(Window的标志)和SoftInputMode(软键盘相关模式),下面分别介绍这3种Window的属性。
3.3.1 Window的类型和显示次序
Window 的类型有很多种,比如应用程序窗口、系统错误窗口、输入法窗口、PopupWindow、Toast、Dialog等。总的来说Window分为三大类型,分别是Application Window (应用程序窗口)、Sub Window(子窗口)、System Window(系统窗口),每个大类型中又包含了很多种类型,它们都定义在WindowManager 的静态内部类LayoutParams 中,接下来分别对这三大类型进行讲解。
3.3.1.1 应用程序窗口
Activity就是一个典型的应用程序窗口,应用程序窗口包含的类型如下所示:
应用程序窗口共包含了以上几种Type值,其中注释1处的Type表示应用程序窗口类型初始值,注释2处的Type表示应用程序窗口类型结束值,也就是说应用程序窗口的Type值范围为1~99,这个数值的大小涉及窗口的层级。
3.3.1.2 子窗口
子窗口,顾名思义,它不能独立存在,需要附着在其他窗口才可以,PopupWindow就属于子窗口。子窗口的类型定义如下所示:
可以看出子窗口的Type值范围为1000~1999。
3.3.1.3 系统窗口
Toast、输入法窗口、系统音量条窗口、系统错误窗口都属于系统窗口。系统窗口的类型定义如下所示:
系统窗口的类型值有接近40个,这里只列出了一小部分,系统窗口的Type值范围为2000~2999。
3.3.1.4 窗口显示次序
当一个进程向WMS申请一个窗口时,WMS会为窗口确定显示次序。
为了方便窗口显示次序的管理,手机屏幕可以虚拟地用X、Y、Z轴来表示,其中Z轴垂直于屏幕,从屏幕内指向屏幕外,这样确定窗口显示次序也就是确定窗口在Z轴上的次序,这个次序称为Z-Oder。
Type值是Z-Oder排序的依据,我们知道应用程序窗口的Type值范围为1~99,子窗口1000~1999,系统窗口2000~2999,在一般情况下,Type值越大则Z-Oder排序越靠前,就越靠近用户。
当然窗口显示次序的逻辑不会这么简单,情况会比较多,举个常见的情况:当多个窗口的Type值都是TYPE_APPLICATION,这时WMS会结合各种情况给出最终的Z-Oder,这个逻辑不在本次的讨论范围,这里我们只需要知道窗口显示次序的基本规则就可以了。
3.3.2 Window的标志
Window的标志也就是Flag,用于控制Window的显示,同样被定义在WindowManager的内部类LayoutParams中,一共有20多个,这里给出几个比较常用的,如下表所示。
设置Window的Flag有3种方法:
- 第一种是通过Window的addFlags方法:
- 第二种通过Window的setFlags方法:
其实Window的addFlags方法内部会调用setFlags方法,因此这两种方法区别不大。
- 第三种则是给LayoutParams设置Flag,并通过WindowManager的addView方法进行添加:
3.3.3 软键盘相关模式
窗口和窗口的叠加是十分常见的场景,但如果其中的窗口是软键盘窗口,可能就会出现一些问题。
比如典型的用户登录界面,默认的情况弹出的软键盘窗口可能会盖住输入框下方的按钮,这样用户体验会非常糟糕。为了使得软键盘窗口能够按照期望来显示,WindowManager的静态内部类LayoutParams中定义了软键盘相关模式,这里给出常用的几个,如下表所示。
从上面给出的SoftInputMode,可以发现,它们与AndroidManifest中Activity的属性android:windowSoftInputMode是对应的。因此,除了在AndroidMainfest中为Activity设置android:windowSoftInputMode以外还可以在Java代码中为Window设置SoftInputMode,如下所示:
3.4 Window的操作
WindowManager对Window进行管理,说到管理那就离不开对Window的添加、更新和删除的操作,在这里我们把它们统称为Window 的操作。
对于Window 的操作,最终都是交由WMS来进行处理的。
窗口的操作分为两大部分,一部分是WindowManager处理部分,另一部分是WMS处理部分。
我们知道Window分为三大类,分别是Application Window (应用程序窗口)、Sub Window(子窗口)和System Window(系统窗口)。
对于不同类型的窗口添加过程会有所不同,但是对于WMS处理部分,添加的过程基本上是一样的,WMS对于这三大类的窗口基本是“一视同仁”的,如下图所示。
3.4.1 系统窗口的添加过程
系统窗口的添加过程也会根据不同的系统窗口有所区别,这里以系统窗口StatusBar为例。
StatusBar是SystemUI的重要组成部分,具体就是指系统状态栏,用于显示时间、电量和信号等信息。我们来查看StatusBar的addStatusBarWindow方法,这个方法负责为StatusBar添加Window,如下所示:
在注释1处用于构建StatusBar的视图。
在注释2处调用了StatusBarWindowManager的add方法,并将StatusBar的视图(StatusBarWindowView)和StatusBar的高度传进去,StatusBarWindowManager的add方法如下所示:
首先通过创建LayoutParams来配置StatusBar视图的属性,包括Width、Height、Type、Flag、Gravity、SoftInputMode 等。
关键在注释1处,设置了TYPE_STATUS_BAR,表示StatusBar视图的窗口类型是状态栏。
在注释2处调用了WindowManager的addView方法,addView方法定义在WindowManager的父类接口ViewManager中,而addView方法则是在WindowManagerImpl中实现的,如下所示:
addView 方法的第一个参数的类型为View,说明窗口也是以View 的形式存在的。
addView方法中会调用WindowManagerGlobal的addView方法,如下所示:
在介绍addView方法前我们首先要了解WindowManagerGlobal中维护的和Window操作相关的3个列表,在窗口的添加、更新和删除过程中都会涉及这3个列表,它们分别是View 列表(ArrayList<View> mViews)、布局参数列表(ArrayList<WindowManager.LayoutParams>mParams)和ViewRootImpl列表(ArrayList<ViewRootImpl>mRoots)。
了解了这3个列表后,我们接着分析addView方法,首先会对参数view、params和display进行检查。
在注释1处,如果当前窗口要作为子窗口,就会根据父窗口对子窗口的WindowManager.LayoutParams 类型的wparams 对象进行相应调整。
在注释3处将添加的View保存到View列表中。
在注释5处将窗口的参数保存到布局参数列表中。
在注释2处创建了ViewRootImp并赋值给root,紧接着在注释4处将root存入到ViewRootImpl列表中。
在注释6处将窗口和窗口的参数通过setView方法设置到ViewRootImpl中,可见我们添加窗口这一操作是通过ViewRootImpl来进行的。
ViewRootImpl身负了很多职责,主要有以下几点:
- View树的根并管理View树。
- 触发View的测量、布局和绘制。
- 输入事件的中转站。
- 管理Surface。
- 负责与WMS进行进程间通信。
了解了ViewRootImpl的职责后,我们接着来查看ViewRootImpl的setView方法:
在setView 方法中有很多逻辑,这里只截取了一小部分,主要就是调用了mWindowSession的addToDisplay方法,mWindowSession是IWindowSession类型的,它是一个Binder对象,用于进行进程间通信,IWindowSession是Client端的代理,它的Server端的实现为Session,此前的代码逻辑都是运行在本地进程的,而Session的addToDisplay方法则运行在WMS所在的进程(SystemServer进程)中,如下图所示。
从上图可以看出,本地进程的ViewRootImpl要想和WMS进行通信需要经过Session,那么Session为何包含在WMS中呢?我们接着往下看Session的addToDisplay方法,如下所示:
在addToDisplay方法中调用了WMS的addWindow方法,并将自身也就是Session作为参数传了进去,每个应用程序进程都会对应一个Session,WMS会用ArrayList来保存这些Session,这就是为什么图中的WMS包含Session的原因。
这样剩下的工作就交给WMS来处理,在WMS中会为这个添加的窗口分配Surface,并确定窗口显示次序,可见负责显示界面的是画布Surface,而不是窗口本身。WMS会将它所管理的Surface 交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制到屏幕上。
系统窗口StatusBar的添加过程的时序图如下图所示:
3.4.2 Activity的添加过程
无论是哪种窗口,它的添加过程在WMS 处理部分中基本是类似的,只不过会在权限和窗口显示次序等方面会有些不同。
但是在WindowManager处理部分会有所不同,这里以最典型的应用程序窗口Activity为例,Activity在启动过程中,如果Activity所在的进程不存在则会创建新的进程,创建新的进程之后就会运行代表主线程的实例ActivityThread,ActivityThread 管理着当前应用程序进程的线程,这在Activity的启动过程中运用得很明显,当界面要与用户进行交互时,会调用ActivityThread的handleResumeActivity方法,如下所示:
注释1处的performResumeActivity方法最终会调用Activity的onResume方法。
在注释2处得到ViewManager类型的wm对象。
在注释3处调用了ViewManager的addView方法,而addView方法则是在WindowManagerImpl中实现的,此后的过程在上面的系统窗口StatusBar的添加过程中已经讲过,唯一需要注意的是ViewManager的addView方法的第一个参数为DecorView,这说明Acitivty窗口中会包含DecorView。
3.4.3 Window的更新过程
Window 的更新过程和Window 的添加过程是类似的。需要调用ViewManager 的updateViewLayout 方法,updateViewLayout 方法在WindowManagerImpl中实现,WindowManagerImpl的updateViewLayout方法会调用WindowManagerGlobal的updateViewLayout方法,如下所示:
注释1处将更新的参数设置到View中。
注释2处得到要更新的窗口在View列表中的索引,
注释3处在ViewRootImpl列表中根据索引得到窗口的ViewRootImpl,
注释4和注释5处用于更新布局参数列表,
注释6处调用ViewRootImpl的setLayoutParams方法将更新的参数设置到ViewRootImpl 中。ViewRootImpl 的setLayoutParams 方法在最后会调用ViewRootImpl的scheduleTraversals方法,如下所示:
注释1处的Choreographer译为“舞蹈指导”,用于接收显示系统的VSync信号,在下一个帧渲染时控制执行一些操作。Choreographer的postCallback方法用于发起添加回调,这个添加的回调将在下一帧被渲染时执行。这个添加的回调指的是注释1处的TraversalRunnable类型的mTraversalRunnable,如下所示:
在TraversalRunnable的run方法中调用了doTraversal方法,如下所示:
在doTraversal 方法中又调用了performTraversals 方法,performTraversals 方法使得ViewTree开始View的工作流程,如下所示:
注释1处的relayoutWindow方法内部会调用IWindowSession的relayout方法来更新Window视图,最终会调用WMS的relayoutWindow 方法。
除此之外,performTraversals方法还会在注释2、3、4处分别调用performMeasure、performLayout和performDraw方法,它们的内部又会调用View的measure、layout和draw方法,这样就完成了View的工作流程。在performTraversals方法中更新了Window视图,又执行Window中的View的工作流程,这样就完成了Window的更新。