1.窗口层级关系(浮窗是如何“浮”的)?
2.浮窗有哪些限制,如何越过用户授权实现浮窗功能?
3.窗口与用户输入系统(Activity是如何接收到touch事件?)。
第一个问题:浮窗为何会浮。 浮窗之所以叫浮窗,是因为它能悬浮于应用或者桌面窗口之上,能脱离Activity而存在。为了研究其中区别,我们先来看看我们最熟悉的Activity是怎么显示出来的。
Activity是怎么显示出来的?
要弄清这个问题答案,我们先从Activity的setContentView()这个方法的源码开始找起,在Activity中看到setCententView的源码:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow是返回返回Activity的mWindow变量,指向一个Window的对象,Window是一个抽象类,这里返回的是PhoneWindow对象(PhoneWindow是Window的子类),PhoneWindow中有一个DecorView对象,decorView成员,这是一个FrameLayout,setContentView的子布局最终会添加到decorView中,这个decorView就是当前窗口的根视图,这个根视图是如何最终被绘制出来的?在ActivityThread中有这样一段代码:
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
这个decorView,最终会被WindowManager.addView添加到绘制系统中,并类型是WindowManager.LayoutParams.TYPE_BASE_APPLICATION,这个参数决定了要绘制的窗口的z轴层次,为了避免思维栈过深,这里就不贴出详细的源码跟踪过程了,直接给结论。
先来看看Activity和window的关系:
再来window和View的关系:
Activity窗口显示过程:
说Activity是怎么显示出来的,其实是说Activity管理的View是怎么显示出来的。最后再来总结一下:
一、Activity通过setContentView设置的视图是添加到PhoneWindow的根视图decor中。
二、Window是一个抽象的概念,Window关了了一个View(根视图),最终被WindowManager管理的还是一个View(根视图)和它的LayoutParams,视图绘制刷新都是通过WindowManager(WindowManagerGlobal)与WindowManagerServiceIPC交互调用底层绘制的。
三、Activity是四大组件中唯一和窗体紧密联系的组件(这是为什么会有初学者把Activity直接理解为绘制界面的原因),所有掌管的视图只不过是一种window和Dialog、Toast、墙纸所掌管的Window类型不一样。
浮窗为什么会“浮”?
上面讲到Activity的显示过程其实已经揭示了通用界面的显示过程,浮窗的显示过程更为简单:
做过浮窗的同学应该都明白了,为啥浮窗能脱离Activity而显示,本质上我们是把一个View交给WindowManager来管理了,LayoutParams.type类型决定了这个View显示窗口的类型,不同类型显示的窗口层次(z轴)是不一样的。大方面来讲可以分为应用窗口(APPLICATION_WINDOW)、子窗口(SUB_WINDOW)、系统窗口(SYSTEM_WINDOW)三种类型,应用窗口z轴范围是1~99,子窗口的范围是1001~1999,系统窗口是(2000~2999),所以要实现浮动窗口我们只能在系统窗口范围中实现。
更新SDK 29
类型 | 常量范围 | 子类 | 常量值 | 说明 | 例子 |
---|---|---|---|---|---|
APPLICATION_WINDOW | 1~99 | 应用窗口 | |||
FIRST_APPLICATION_WINDOW | 1 | 应用程序窗口。 | |||
TYPE_BASE_APPLICATION | 1 | 所有程序窗口的“基地”窗口,其他应用程序窗口都显示在它上面 | |||
TYPE_APPLICATION | 2 | 普通应用功能程序窗口。token必须设置为Activity的token,以指出该窗口属谁。 | 大部分的应用程序窗口 | ||
TYPE_APPLICATION_STARTING | 3 | 应用程序的Activity显示之前由系统显示的窗口 | 用于应用程序启动时所显示的窗口。应用本身不要使用这种类型。它用于让系统显示些信息,直到应用程序可以开启自己的窗口 | ||
TYPE_DRAWN_APPLICATION | 4 | TYPE_APPLICATION的一种变体,可确保窗口*管理器在显示应用程序之前将等待此窗口被绘制。仅在拥有用户的窗口上显示 | |||
LAST_APPLICATION_WINDOW | 99 | 应用程序窗口结束 | |||
SUB_WINDOW | 1000~1999 | 子窗口 | |||
FIRST_SUB_WINDOW | 1000 | ||||
TYPE_APPLICATION_PANEL | 1000 | 面板窗口,显示于宿主窗口上层,遮挡其下面的应用窗口。 | 子窗口。子窗口的Z序和坐标空间都依赖于他们的宿主窗口。 | ||
TYPE_APPLICATION_MEDIA | 1001 | 媒体窗口,例如视频。显示于宿主窗口下层,如果应用窗口不挖洞,即不可见。SurfaceView,在小窗口显示时设为MEDIA, 全屏显示时设为PANEL | |||
TYPE_APPLICATION_SUB_PANEL | 1002 | 应用程序窗口的子面板。显示于所有面板窗口的上层。(GUI的一般规律,越“子”越靠上) | |||
TYPE_APPLICATION_ATTACHED_DIALOG | 1003 | 对话框。类似于面板窗口,绘制类似于顶层窗口,而不是宿主的子窗口。 | |||
TYPE_APPLICATION_MEIDA_OVERLAY | 1004 | 用于两个SurfaceView的合成,如果设为MEDIA,则上面的SurfaceView 挡住下面的SurfaceView | 媒体信息。显示在媒体层和程序窗口之间,需要实现透明(半透明)效果。(例如显示字幕) | ||
TYPE_APPLICATION_ABOVE_SUB_PANEL | 1005 | 位于应用程序窗口顶部的子面板窗口。这些窗口显示在其附加窗口TYPE_APPLICATION_SUB_PANEL面板的顶部 | |||
LAST_SUB_WINDOW | 1999 | 子窗口结束。( End of types of sub-windows ) | |||
SYSTEM_WINDOW | 2000~2999 | 系统窗口 | |||
FIRST_SYSTEM_WINDOW | 2000 | 系统窗口。非应用程序创建。 | |||
TYPE_STATUS_BAR | 2000 | 状态栏。只能有一个状态栏;它位于屏幕顶端,其他窗口都位于它下方。 | 顶部的状态栏 | ||
TYPE_SEARCH_BAR | 2001 | 搜索栏。只能有一个搜索栏;它位于屏幕上方 | |||
TYPE_PHONE | 2002 | 电话窗口。它用于电话交互(特别是呼入)。它置于所有应用程序之上,状态栏之下 | 电话窗口 | ||
TYPE_SYSTEM_ALERT | 2003 | 系统提示。它总是出现在应用程序窗口之上。 | 警告窗口,在所有其他窗口之上显示 电量不足提醒窗口 | ||
TYPE_KEYGUARD | 2004 | 锁屏界面 | |||
TYPE_TOAST | 2005 | 短时的文字提醒小窗口 | 信息窗口。用于显示toast。 | ||
TYPE_SYSTEM_OVERLAY | 2006 | 系统顶层窗口。显示在其他一切内容之上。此窗口不能获得输入焦点,否则影响锁屏。 | 没有焦点的浮动窗口 | ||
TYPE_PRIORITY_PHONE | 2007 | 电话优先,当锁屏时显示。此窗口不能获得输入焦点,否则影响锁屏。 | 紧急电话窗口,可以显示在屏保之上 | ||
TYPE_SYSTEM_DIALOG | 2008 | 系统信息弹出窗口对话框。 | 例如音量调节框、SIM插上后弹出的运营商信息窗口 | ||
TYPE_KEYGUARD_DIALOG | 2009 | 跟KeyGuard绑定的弹出对话框 锁屏时的滑动解锁窗口 | 锁屏时显示的对话框。 | ||
TYPE_SYSTEM_ERROR | 2010 | 系统错误提示窗口,显示于所有内容之上。 | ANR 窗口 | ||
TYPE_INPUT_METHOD | 2011 | 内部输入法窗口,显示于普通UI之上。应用程序可重新布局以免被此窗口覆盖。 | 输入法窗口,会挤占当前应用的空间 | ||
TYPE_INPUT_METHOD_DIALOG | 2012 | 弹出的输入法窗口,不会挤占当前应用窗口空间,在其之上显示 | |||
TYPE_WALLPAPER | 2013 | 墙纸 | |||
TYPE_STATUS_BAR_PANEL | 2014 | 从状态条下拉的窗口 | 状态栏滑动面板 | ||
TYPE_SECURE_SYSTEM_OVERLAY | 2015 | 只有系统用户可以创建的OVERLAY窗口 | |||
TYPE_DRAG | 2016 | 浮动的可拖动窗口 | 360安全卫士的浮动精灵 | ||
TYPE_STATUS_BAR_PANEL 2017 | |||||
TYPE_POINTER | 2018 | 光标 | |||
TYPE_NAVIGATION_BAR | 2019 | ||||
TYPE_VOLUME_OVERLAY | 2020 | 音量调节窗口 | |||
TYPE_BOOT_PROGRESS | 2021 | 启动进度,在所有窗口之上 | |||
TYPE_HIDDEN_NAV_CONSUMER | 2022 | 隐藏的导航栏 | |||
TYPE_DREAM | 2023 | 屏保动画 | |||
TYPE_NAVIGATION_BAR_PANEL | 2024 | Navigation bar 弹出的窗口 比如说应用收集栏 | |||
TYPE_UNIVERSAL_BACKGROUND | 2025 | ||||
TYPE_DISPLAY_OVERLAY | 2026 | 用于模拟第二显示设备 | |||
TYPE_MAGNIFICATION | 2027 | 用于放大局部 | |||
TYPE_RECENTS_OVERLAY | 2028 | 当前应用窗口,多用户情况下只显示在用户节目 | |||
TYPE_PRIVATE_PRESENTATION | 2030 | 在专用虚拟显示器上的演示窗口 | |||
TYPE_VOICE_INTERACTION | 2031 | 语音交互层中的Windows | |||
TYPE_ACCESSIBILITY_OVERLAY | 2032 | 无障碍使用的 | 如果存在可触摸的全屏可访问性覆盖,则可访问性服务将自检其下方的窗口即使*被可触摸窗口覆盖。 | ||
TYPE_VOICE_INTERACTION_STARTING | 2033 | 语音交互层的启动窗口 | |||
TYPE_DOCK_DIVIDER | 2034 | 显示用于调整停靠堆栈大小的句柄的窗口。该窗口由系统进程拥 | |||
TYPE_QS_DIALOG | 2035 | 类似于TYPE_APPLICATION_ATTACHED_DIALOG但由快速设置图块使用 | |||
TYPE_SCREENSHOT | 2036 | 与TYPE_DREAM具有相似的特征,保留该层以供选择屏幕快照区域。这些窗口不能占据输入焦点 | |||
TYPE_PRESENTATION | 2037 | 外部显示器上的演示窗口 | |||
TYPE_APPLICATION_OVERLAY | 2038 | 应用程序覆盖窗口显示在所有活动窗口上方。在FIRST_APPLICATION_WINDOW和LAST_APPLICATION_WINDOW之间的类型,但在关键系统窗口(例如状态栏或IME)下方。系统可以随时更改这些窗口的位置,大小或可见性以减少用户的视觉混乱并管理资源。需android.Manifest.permission.SYSTEM_ALERT_WINDOW}权限。系统将调整此窗口类型的进程的重要性,以减少低内存杀手杀死它们的机会。仅在拥有用户的屏幕上显示 | |||
LAST_SYSTEM_WINDOW | 2999 | 系统窗口结束。 |