本章主要关注的是在APP应用内部页面内容的加载过程,不涉及远程服务端Wms内部逻辑(这部分内容详见《第14章 Wms工作原理》);
首先回顾下第6章所介绍过的一些有关窗口相关的内容:
- 对于Wms管理的窗口,具体指的是View,而不是Window类,Window类只是提供对窗口操作的一组抽象API而已;
- 每个Activity都会对应一个窗口IWindow,当Wms收到用户消息之后会将消息派发到具体的窗口,而具体的接受者是IWindow的实例ViewRootImpl.W,然后W将具体消息再转发给具体的view;即Wms具体管理的是View,但是却不能直接将消息传递给View,必须通过IWindow中转;
窗口的类型
Framework中定义了三种窗口类型,具体位于WindowManager.LayoutParams中,对于每一种类型系统都用一个int型type常量来表示,实际上就代表窗口对应的层Layer,也称为z-order;由于android系统是一个单窗口GUI系统,所以需要对窗口进行叠加,此时就会根据该type变量值的大小分配,值越大代表位置越靠上面,当Wms进行窗口叠加时会动态调整窗口对应的具体值;
- 应用窗口:简单来说就是普通应用中通过Activity加载的窗口,这是我们应用开发人员最主要的展示窗口;
type的具体值介于 FIRST_APPLICATION_WINDOW -- FIRST_APPLICATION_WINDOW之间,即1-99之间; - 子窗口:有父窗口的窗口即属于子窗口类型,其父窗口可以是应用窗口,也可以是系统窗口,或子窗口;比如PopupWindow、OptionMenu、ContextMenu等;
type的具体值介于 FIRST_SUB_WINDOW -- FIRST_SUB_WINDOW之间,即1000-1999之间; - 系统窗口:可理解为系统级别的窗口,他的type值是最高的,也就是显示在最上面的,确保用户一定可见;比如系统状态栏、来电显示、屏保、输入法、Toast等;书中说应用程序无法创建系统窗口应该是误导,因为我们经常在应用中创建Toast窗口,准确的说应该是部分系统类窗口应用程序无法创建,因为对应的type常量都是@hide的,外部无法使用;
type的具体值介于 FIRST_SYSTEM_WINDOW -- FIRST_SYSTEM_WINDOW之间,即2000-2999之间;
应用窗口的创建
之前提到应用窗口由Activity加载,因此我们就从Ams决定启动某个Activity开始梳理,具体流程如下:
- ActivityThread.ApplicationThread接收到Ams回调的启动某个Activity的IPC请求,执行scheduleLaunchActivity(…)方法,期间把具体请求通过Handler进行异步处理;
- ActivityThread.H消息处理中执行 handleLaunchActivity(…),期间会完成整个Activity的加载、以及页面内容显示,具体如下;
- 执行 performLaunchActivity(…);
- 执行 mInstrumentation.newActivity(…),通过ClassLoader实现对Activity的实例化;
mInstrumentation可以理解为每个APP应用中对所有Activity进行管理的管家(其实和XXManager的定位差不多,只是在android系统中Manager更多服务于远程接口封装,而这里是完全针对本地APP内部的,我猜这是两者命名规则不一的原因吧),有且只有一个实例,他用来收集和监测所有Activity执行的相关开销; - 执行 activity.attach(…),为构造好的activity实例设置内部变量,期间会调用 PolicyManager.makeNewWindow(.)完成PhoneWindow实例化,以及通过 context.getSystemService(.)获取WindowManager实例;
- 执行 mInstrumentation.callActivityOnCreate(…),实现对 activity.onCreate()回调,期间会调用 setContentView(.),进而调用 PhoneWindow.setContentView(.);
- 执行 PhoneWindow.installDecor(),期间完成DecorView的初始化;
- 执行 PhoneWindow.generateLayout(.),期间根据用户指定的参数(一般通过设置android:theme实现,比如 screen_title/ screen_action_bar/...)选择不同的窗口装饰,其实就是各种layout,layout中要求必须有一个id=content的FrameLayout容器,用于存放真正的用户设置的视图,并将inflate得到的View实例add到DecorView实例中;
- 执行 mLayoutInflater.inflate(…)加载activity中设置的layout,此时layout的parent已经被设置为窗口装饰布局中干的content容器;
- 执行 mInstrumentation.callActivityOnStart(…),实现对 activity.onStart()回调;
- 执行 mInstrumentation.callActivityOnPostCreate(…),实现对 activity.onPostCreate()回调;
- 执行 mInstrumentation.newActivity(…),通过ClassLoader实现对Activity的实例化;
- 执行 handleResumeActivity(…);
- 执行 mInstrumentation.callActivityOnResume(…),实现对 activity.onResume()、onPostResume();回调;
- 执行 WindowManager.addView(…),间接执行 WindowManagerGlobal. addView(…),期间初始化 ViewRootImpl实例mRootView,并执行 ViewRootImpl.setView(…)将之前获得的DecorView实例的parent设置为mRootView;
- 对mRootView实例的重要变量赋值;
- 执行 requestLayout(); 发出页面重绘异步消息请求,确保UI线程在响应窗口消息之前让窗口变得可见;
- 执行 mWindowSession.addToDisplay(…)实现真正的通知Wms添加窗口并进行展示;
注意:mWindowSession变量其实是通过 windowManager.openSession(…)获得的,至于为啥不直接调用Wms服务的疑问在之前的《第6章 应用框架Framework概述》已经有提到过;
- 执行 performLaunchActivity(…);
子窗口的创建
事实上,对于Wms而言并不在意客户端要创建的是什么类型的窗口,他都会一视同仁,所不同的仅仅是在Wms端会保存窗口间的父子关系,并根据这些信息调整拥有相同父窗口的层值;
添加窗口的标准过程为:准备WindowManager.LayoutParams对象 -- > 准备要添加的窗口内容View -- > 调用windowManager.addView();
典型的子窗口有:UI控件下拉列表PopupWindow、情景菜单ContextMenu触发的AlertDialog对话框、选项菜单OptionMenu触发的MenuView等;
有几点需要注意:
- 默认的Dialog其实不是子窗口类型,他属于应用窗口,和Activity处理的页面是完全一样的;但在一些特别场景中可以通过子类改变其WindowManager.LayoutParams.type值变为子窗口类型;
- 选项菜单OptionMenu的触发是通过物理按键完成的,其实际处理逻辑由PhoneWindow完成,而Activity中只是简单中转而已;
- 情景菜单ContextMenu触发的menu对象会随着长按视图的变化而变化,而选项菜单OptionMenu触发的menu对象却是一直保持在内存中的,他可以作为Activity中的持久变量使用;
系统窗口的创建
应用程序中默认情况下只能这几种类型的系统窗口(TYPE_TOAST/TYPE_INPUT_METHOD/TYPE_WALLPAPER/TYPE_DREAM),其余类型的若要创建(比如我们可以在屏幕最顶层显示一个悬浮窗口),则需要在AndroidManifest.xml中添加SYSTEM_ALERT_WINDOW的权限,具体权限的检查是在PhoneWindowManager.checkAddPermission(.)中完成的;
Toast系统窗口的添加和普通窗口大致相同,只是设置WindowManager.LayoutParams.type值不一样而已,需要注意的是,系统为了确保同一时刻只能有一个Toast窗口显示,引入了通知管理器NotificationManager服务,在SystemServer服务进程中NotificationManagerService对各客户端显示Toast的请求进行了串行化处理;
另外,系统窗口的创建一般不依赖于前台Activity,而是通过后台Service触发,比如当发现有WIFI连接时弹出一个系统窗口动态显示网络上传与下载流量与速度。这就解释了之前我们在《第9章 Framework的启动过程》最后提到的为什么service中可以创建PhoneStatusBar,因为状态栏属于系统窗口;
以上内容若有转载,请注明出处,欢迎访问老唐的专栏http://blog.csdn.net/sfdev