写点什么

Framework 学习(十一)WindowManager 体系,学习指南

用户头像
Android架构
关注
发布于: 29 分钟前

//得到 WindowManager 所管理的屏幕(Display)


public Display getDefaultDisplay();


//完成传入的 View 相关的销毁工作


public void removeViewImmediate(View view);


可以看到这些方法传入的参数都是 View,说明 WindowManager 具体管理的是以 View 形式存在的 Window。


WindowManager 除了增加方法外,还定义了一个静态内部类 LayoutParams,这就是 Window 的属性,了解 Window 的属性能够更好的理解 WMS 的内部原理,下面来看看 Window 的属性。


Window 的属性




Window 的属性有很多种,与应用开发最密切的有三种,它们分别是 Type(Window 的类型)、Flag(Window 的标志)和 SoftInputMode(软键盘相关模式),下面分别介绍这三种 Window 的属性。

Window 的类型

Window 的类型有很多种,比如应用程序窗口、系统错误窗口、输入法窗口、PopupWindow、Toast、Dialog 等等。总的来说分为三大类:Application Window(应用程序窗口)、Sub Windwow(子窗口)、System Window(系统窗口),每个大类又包含了很多种类型,它们都定义在 WindowManager 的静态内部类 LayoutParams 中。


(1)应用程序窗口


Activity 就是一个典型的应用程序窗口,应用程序窗口包含的类型:


public static final int FIRST_APPLICATION_WINDOW = 1;//应用程序窗口类型初始值


public static final int TYPE_BASE_APPLICATION = 1;//窗口的基础值,其他的窗口值要大于这个值


public static final int TYPE_APPLICATION = 2;//普通的应用程序窗口类型


public static final int TYPE_APPLICATION_STARTING = 3;//应用程序启动窗口类型,用于系统在应用程序窗口启动前显示的窗口。


public static final int TYPE_DRAWN_APPLICATION = 4;


public static final int LAST_APPLICATION_WINDOW = 99;//应用程序窗口类型结束值


应用程序窗口的初始值是 1,结束值是 99,也就是说应用程序窗口的 Type 值范围为 1 到 99,这个数值的大小涉及到窗口的层级,一般情况下,Type 值越大则 Z-Oder 排序越靠前,就越靠近用户,在频幕越上层。


(2)子窗口


子窗口,顾名思义,它不能独立的存在,需要附着在其他窗口才可以,PopupWindow 就属于子窗口。子窗口的类型定义如下:


public static final int FIRST_SUB_WINDOW = 1000;//子窗口类型初始值


public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;


public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;


public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;


public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;


public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;


public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;


public static final int LAST_SUB_WINDOW = 1999;//子窗口类型结束值


子窗口的 Type 值范围为 1000 到 1999。


(3)系统窗口


Toast、输入法窗口、系统音量条窗口、系统错误窗口都属于系统窗口。系统窗口的类型定义如下:


public static final int FIRST_SYSTEM_WINDOW = 2000;//系统窗口类型初始值


public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//系统状态栏窗口


public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//搜索条窗口


public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//通话窗口


public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//系统 ALERT 窗口


public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//锁屏窗口


public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//TOAST 窗口


...


public static final int LAST_SYSTEM_WINDOW = 2999;//系统窗口类型结束值


系统窗口的 Type 值范围为 2000 到 2999。


Window 的标志




Window 的标志也就是 Flag,用于控制 Window 的显示,同样被定义在 WindowManager 的内部类 LayoutParams 中,一共有 20 多个,这里我们给出几个比较常用的。


// 只要窗口可见,就允许在开启状态的屏幕上锁屏.


public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;


//窗口不能获得输入焦点,设置该标志的同时,FLAG_NOT_TOUCH_MODAL 也会被设置


public static final int FLAG_NOT_FOCUSABLE = 0x00000008;


//窗口不接收任何触摸事件


public static final int FLAG_NOT_TOUCHABLE = 0x00000010;


//在该窗口区域外的触摸事件传递给其他的 Window,而自己只会处理窗口区域内的触摸事件


public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;


//只要窗口可见,屏幕就会一直亮着


public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;


//允许窗口超过屏幕之外


public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200;


//隐藏所有的屏幕装饰窗口,比如在游戏、播放器中的全屏显示


public static final int FLAG_FULLSCREEN = 0x00000400;


//窗口可以在锁屏的窗口之上显示


public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;


//当用户的脸贴近屏幕时(比如打电话),不会去响应此事件


public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;


//窗口显示时将屏幕点亮


public static final int FLAG_TURN_SCREEN_ON = 0x00200000;


设置 Window 的 Flag 有三种方法,第一种是通过 Window 的 addFlags 方法:


Window mWindow =getWindow();


mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);


第二种通过 Window 的 setFlags 方法,其实内部还是会调用 setFlags 方法:


Window mWindow =getWindow();


mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);


第三种则是给 LayoutParams 设置 Flag,并通过 WindowManager 的 addView 方法进行添加:


WindowManager.LayoutParams mWindowLayoutParams = new WindowManager.LayoutParams();


mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;


WindowManager mWindowManager =(WindowManager)getSystemService(Context.WINDOW_SERVICE);


TextView mTextView=new TextView(this);


mWindowManager.addView(mTextView, mWindowLayoutParams);


软键盘相关模式




窗口和窗口的叠加是非常常见的场景,但如果其中的窗口是软键盘窗口,可能就会出现一些问题,比如典型的用户登录界面,默认的情况弹出的软键盘窗口可能会盖住输入框,这样用户体验会非常糟糕。


为了使得软键盘窗口能够按照期望来显示,WindowManager 的静态内部类 LayoutParams 中还定义了软键盘相关模式,这里给出常用的几个:


//没有指定状态,系统会选择一个合适的状态或依赖于主题的设置


public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;


//不会改变软键盘状态


public static final int SOFT_INPUT_STATE_UNCHANGED = 1;


//当用户进入该窗口时,软键盘默认隐藏


public static final int SOFT_INPUT_STATE_HIDDEN = 2;


//当窗口获取焦点时,软键盘总是被隐藏


public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;


//当软键盘弹出时,窗口会调整大小


public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;


//当软键盘弹出时,窗口不需要调整大小,要确保输入焦点是可见的


public static final int SOFT_INPUT_ADJUST_PAN = 0x20;


从上面给出的 SoftInputMode ,可以发现,它们与 AndroidManifest 中 Activity 的属性 android:windowSoftInputMode 是对应的。因此,除了在 AndroidMainfest 中为 Activity 设置 android:windowSoftInputMode 以外还可以在 Java 代码中为 Window 设置 SoftInputMode:


getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);


Window 的添加过程




WindowManager 对 Window 的操作最终都是交由 WMS 来进行处理。窗口的操作分为两大部分,一部分是 WindowManager 处理部分,另一部分


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


是 WMS 处理部分。我们知道 Window 分为三大类,分别是:Application Window(应用程序窗口)、Sub Windwow(子窗口)和 System Window(系统窗口),对于不同类型的窗口添加过程会有所不同,但是对于 WMS 处理部分,添加的过程基本上是一样的。



无论是哪种窗口,它的添加过程在 WMS 处理部分中基本是类似的,只不过会在权限和窗口显示次序等方面会有些不同。但是在 WindowManager 处理部分会有所不同,这里以最典型的应用程序窗口 Activity 为例。


Activity 在启动过程中,会调用 ActivityThread 的 handleLaunchActivity()方法,具体可以参考 Framework 学习(五)应用程序启动过程这篇文章。


frameworks/base/core/java/android/app/ActivityThread.java


ActivityThread#handleLaunchActivity()


private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {


...


Activity a = performLaunchActivity(r, customIntent); //1


if (a != null) {


r.createdConfig = new Configuration(mConfiguration);


reportSizeConfigurations(r);


Bundle oldState = r.state;


//2


handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);


if (!r.activity.mFinished && r.startsNotResumed) {


performPauseActivityIfNeeded(r, reason);


if (r.isPreHoneycomb()) {


r.state = oldState;


}


}


} else {


...


}


}


注释 1 处的 performLaunchActivity 方法用来启动 Activity。


注释 2 处的代码用来添加 Activity 的 window 窗口。


ActivityThread#performLaunchActivity()


private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {


...


//1


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);


...


return activity;


}


注释 1 调用 Activity 的 attach 方法。


frameworks/base/core/java/android/app/Activity.java


Activity#attach()


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) {


attachBaseContext(context);


mFragments.attachHost(null /parent/);


mWindow = new PhoneWindow(this, window); //1


...


//2


mWindow.setWindowManager(


(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),


mToken, mComponent.flattenToString(),


(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);


...


}


注释 1 处创建了 PhoneWindow。


注释 2 处调用了 PhoneWindow 的 setWindowManager 方法,这个方法的具体的实现在 PhoneWindow 的父类 Window 中。


frameworks/base/core/java/android/view/Window.java


Window#setWindowManager()


public void setWindowManager(WindowManager wm, IBinder appToken, String appName,


boolean hardwareAccelerated) {


mAppToken = appToken;


mAppName = appName;


mHardwareAccelerated = hardwareAccelerated


|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);


if (wm == null) {


wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);//1


}


mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);//2


}


注释 1 处如果传入的 WindowManager 为 null,就会调用 Context 的 getSystemService 方法,并传入服务的名称 Context.WINDOW_SERVICE(”window”),Context 的实现类是 ContextImpl,具体的实现在 ContextImpl 中。


frameworks/base/core/java/android/app/ContextImpl.java


ContextImpl#getSystemService()


@Override


public Object getSystemService(String name) {


return SystemServiceRegistry.getSystemService(this, name);


}


frameworks/base/core/java/android/app/SystemServiceRegistry.java


SystemServiceRegistry#getSystemService()


private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<?>>();


...


public static Object getSystemService(ContextImpl ctx, String name) {


ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);


return fetcher != null ? fetcher.getService(ctx) : null;


}


SYSTEM_SERVICE_FETCHERS 是一个 HashMap 类型的数据,它是用来存储服务的,这里是取得地方,那存的地方在哪里呢?找啊找,终于在 SystemServiceRegistry 的静态代码块中找到了。


final class SystemServiceRegistry {


...


private SystemServiceRegistry() { }


static {


...


registerService(Context.WINDOW_SERVICE, WindowManager.class,


new CachedServiceFetcher<WindowManager>() {


@Override


public WindowManager createService(ContextImpl ctx) {


return new WindowManagerImpl(ctx);


}});


...


}


}


SystemServiceRegistry#registerService()


private static <T> void registerService(String serviceName, Class<T> serviceClass,


ServiceFetcher<T> serviceFetcher) {


SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);


SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); //1


}


注释 1 处就是将创建的服务添加到 SYSTEM_SERVICE_FETCHERS 中的。


从上面代码可以看出,传入的 Context.WINDOW_SERVICE 对应的就是 WindowManagerImpl 实例,因此得出结论,Context 的 getSystemService 方法得到的是 WindowManagerImpl 实例。我们再回到 Window 的 setWindowManager 方法,在注释 1 处得到 WindowManagerImpl 实例后转为 WindowManager 类型,在注释 2 处调用了 WindowManagerImpl 的 createLocalWindowManager 方法。


frameworks/base/core/java/android/view/WindowManagerImpl


WindowManagerImpl#createLocalWindowManager()


public WindowManagerImpl createLocalWindowManager(Window parentWindow) {


return new WindowManagerImpl(mContext, parentWindow);


}


createLocalWindowManager 方法同样也是创建 WindowManagerImpl,不同的是这次创建 WindowManagerImpl 时将创建它的 Window 作为参数传了进来,这样 WindowManagerImpl 就持有了 Window 的引用,就可以对 Window 进行操作。比如


在 Window 中添加 View,来查看 WindowManagerImpl 的 addView 方法。


WindowManagerImpl#addView()


@Override


public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {


applyDefaultToken(params);


mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); //1


}


注释 1 处调用了 WindowManagerGlobal 的 addView 方法,其中最后一个参数 mParentWindow 就是 Window,可以看出 WindowManagerImpl 虽然是 WindowManager 的实现类,但是却没有实现什么功能,而是将功能实现委托给了 WindowManagerGlobal。


来查看 WindowManagerImpl 中如何定义的 WindowManagerGlobal:


public final class WindowManagerImpl implements WindowManager {


private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); //1


private final Context mContext;


private final Window mParentWindow; //2


...


private WindowManagerImpl(Context context, Window parentWindow) {


mContext = context;


mParentWindow = parentWindow;


}


...


}


注释 1 可以看出 WindowManagerGlobal 是一个单例,说明在一个进程中只有一个 WindowManagerGlobal 实例。


注释 2 处说明 WindowManagerImpl 可能会实现多个 Window,也就是说在一个进程中 WindowManagerImpl 可能会有多个实例。


Window 和 WindowManager 的关系如下图所示。



PhoneWindow 继承自 Window,Window 通过 setWindowManager 方法与 WindowManager 发生关联。WindowManager 继承自接口 ViewManager,WindowManagerImpl 是 WindowManager 接口的实现类,但是具体的功能都会委托给 WindowManagerGlobal 来实现。

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Framework学习(十一)WindowManager体系,学习指南