今天看啥  ›  专栏  ›  jkwen

Android 进阶解密阅读笔记9

jkwen  · 简书  ·  · 2021-02-18 15:19

新年开工第一天就是干!前面学习整理了系统,应用进程及四大组件的启动过程,接下来是从另外一个模块开始学习。与 AMS 类似,WMS 也涉及到很多知识点,先了解 WindowManager,为后面的 WindowManagerService 做准备。

WindowManager

WindowManager 是个接口,描述了一些和 Window 相关的行为。一个 WindowManager 实例和一个特定的 Display 相关联的( Display 可以理解为屏幕,实际就是一个用于显示的数据对象)。如果说已知 Display 对象,那么可以通过 Context 的方法 createDisplayContext 来获取相关联的 Context 对象,然后再调用 getSystemService(Context.WINDOW_SERVICE) 来获取相关联的 WindowManager 对象。

WindowManager 继承自 ViewManager,后者是用来管理 View 的,即 View 的增,删,改操作,所以前者也就同样有对 View 管理的功能。而 WindowManager 从字面上看是对 Window 进行管理,那么 Window 又是怎么个定义?

Window

Window 是个抽象类,描述了一个顶层视窗以及相关的行为。它的实例应该说作为一个顶层 View 被添加到 WindowManager 里,我们常说视图树,所以 Window 实例可以理解为 WindowManager 管理的根 View。这个根 View 规范了 UI 展示(例如就是手机屏幕上显示的区域)包括背景啊,标题栏范围啊,默认按键处理等等。

Android 对 Window 的实现类就是 PhoneWindow。

还是从启动 Activity 说起

启动 Activity 的过程中最后会走到 ActivityThread 的 performLaunchActivity 方法来创建 Activity,

//ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    }
    if (activity != null) {
        appContext.setOuterContext(activity);
        //这个 attach 方法之前在分析 mBase 变量赋值时分析过。
        activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback,r.assistToken);
    }
}
//Activity
final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,NonConfigurationInstances lastNonConfigurationInstances,Configuration config, String referrer, IVoiceInteractor voiceInteractor,Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    attachBaseContext(context);
    //mWindow 就是 Window 类型,右边通过 PhoneWindow 进行实例化
    //入参的 window 是指之前保存的 Window 实例,这里我们假设没有保存过,是新创建的
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    //这句实现了 Window 与 WindowManager 对象的关联。
    mWindow.setWindowManager((WindowManager)context.getSystemService(
        Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags &              ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    //在前面设置 WindowManager 之后,获得 WindowManager 对象引用
    mWindowManager = mWindow.getWindowManager();
}
//Window
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        //通过 getSystemService 的方式确保 wm 肯定不为 null
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    //对 WindowManager 类型的 wm 进行强转后,调用方法并返回 WindowManager
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

mContext.getSystemService(Context.WINDOW_SERVICE) 实际上调用的是 ContextImpl 的方法,内部的逻辑实际上是通过 SystemServiceRegistry 类在初始化静态语句块时提前注册好一些服务,例如 ACTIVITY_SERVICE, AUDIO_SERVICE, WINDOW_SERVICE 都在这里注册。完了之后,ContextImpl 对象会先从自己的缓存数组中获取指定服务,若发现没有就会创建,并缓存。这里需要 WINDOW_SERVICE,就创建了 WindowManagerImpl 对象。作为 WindowManager 返回了,所以后面能强制转换为 WindowManagerImpl。

createLocalWindowManager 方法内部其实是 new 了一个 WindowManagerImpl 对象,区别在于这个对象和当前这个 PhoneWindow 对象有了关联,我的理解是,通过 getSystemService 获取到的仅作为创建具体有效 WindowManager 使用,而不会用于管理具体的 Window。

这样一来,Window 具体化为 PhoneWindow 对象,WindowManager 也具体化为 WindowManagerImpl 对象,并做了关联,至于 Display,则是通过 WindowManagerImpl 对象中的 Context 变量来建立关联的。

WindowManagerImpl

根据前面这么一套分析,WindowManagerImpl 对象中的 Context 变量实际就是创建 Activity 时的 ContextImpl 对象,另外要留意的就是 WindowManagerGlobal 类型的对象。

还有 Window 属性

WindowManager 里有个静态内部类 LayoutParams 用来描述 Window 的属性,它继承自 ViewGroup 里的静态内部类 LayoutParams,后者用于告诉父 View 该如何布局展示自己。所以 WindowManager 的 LayoutParams 大体也是如此。但有几个特殊属性要关注。

  • type
    窗口类型属性,主要分为三大类,1.应用窗口,这类窗口就是平常使用最多的 Activity,数值范围 [1, 99]。 2.子窗口,需要依附于应用窗口,数值范围 [1000, 1999]。3.系统窗口,这个只有系统能用,我们用不了,数值范围 [2000, 2999]。数值越大,越显示在上层,所以系统窗口才会那么大的值,因为系统的窗口往往都是比较重要的内容展示或者提醒。
  • flags
    这个用于控制窗口的显示模式,用的不多,但其中有个值应该用的很多,FLAG_FULLSCREEN,表示全屏显示窗口。
  • softInputMode
    用来控制软键盘窗口与其他窗口的叠加展示,例如弹起软键盘后是否需要遮挡当前窗口,或者上移,或者甚至不展示软键盘等。



原文地址:访问原文地址
快照地址: 访问文章快照