技术博客
你知道 Android 是如何管理复杂的 Window 层级的?
admin2022-10-21 05:31
229人已围观
简介你知道 Android 是如何管理复杂的 Window 层级的?
App 开发者的不知有没有发现,StatusBar 一直是盖在 App 上面,不管是修改颜色,或者是写悬浮框,都无法盖住 StatusBar。
framework 开发,会出现一些定制,如盖住 StatusBar,不了解的可能用错,出现一些不必要的 bug,官方文档也没有列出 Window 层级的规则。
所以希望通过下文给大家分享,Android 是如何制定显示层级规则的。
大概说下 Window 在 Android 中的概念
其实也可以好理解,和经常使用 Windows 操作系统一样,打开一个应用,出现界面,我们可以理解出现了一个窗口,所以 Window ≠ View。
一个 Activity 可以理解 对应一个 Window,理解源码的同学知道:ViewRootImpl 是对应一个 Window。
怎么看 Window 呢?
adb shell dumpsys window 抽取了几个典型的Window如下: Window #2 Window{911875c u0 NavigationBar0}://导航栏 ty=NAVIGATION_BAR isOnScreen=true isVisible=true Window #4 Window{bf1a956 u0 StatusBar}://状态栏 ty=STATUS_BAR isOnScreen=true isVisible=true Window #11 Window{d377ae1 u0 InputMethod}://输入法,不显示 ty=INPUT_METHOD isOnScreen=false isVisible=false Window #12 Window{e190206 u0 com.android.settings/com.android.settings.Settings}://打开 App activity ty=BASE_LICATION isOnScreen=true isVisible=true Window #16 Window{abcabb9 u0 com.android.systemui.ImageWallpaper}://壁纸 ty=WALLPAPER isOnScreen=false isVisible=false
一般手机都会存在以上 Window,层级顺序从高 -> 低。
显示 PopWindow
Window #11 Window{513f711 u0 PopupWindow:3e4bfb}: ty=LICATION_SUB_PANEL isOnScreen=true sVisible=true
显示 Dialog
Window #11 Window{a08f90b }: ty=LICATION isOnScreen=true isVisible=true
不难看出,Window 层级与 ty 有关系的,ty 是 type 的简写。
Window 的分类
Application Window:应用程序窗口
type 取值范围 [1,99]
/** * Start of window types that represent normal lication windows. */ public static final int FIRST_LICATION_WINDOW = 1; // activity 会使用 此 type public static final int TYPE_BASE_LICATION = 1; // dialog 会使用 此 type public static final int TYPE_LICATION = 2; // 冷启动会显示的 Window,真正启动页面显示之前的画面 public static final int TYPE_LICATION_STARTING = 3; // 没玩过 public static final int TYPE_DRAWN_LICATION = 4; /** * End of types of lication windows. */ public static final int LAST_LICATION_WINDOW = 99;
Sub Window:子窗口
子窗口:顾名思义,对应有主窗口。子窗口需要附在主窗口上,如 PopWindow
type 取值范围 [1000,1999]
/** * Start of types of sub-windows. The {@link #token} of these windows * must be set to the window they are attached to. These types of * windows are kept next to their attached window in Z-order, and their * coordinate space is relative to their attached window. */ public static final int FIRST_SUB_WINDOW = 1000; public static final int TYPE_LICATION_PANEL = FIRST_SUB_WINDOW; public static final int TYPE_LICATION_MEDIA = FIRST_SUB_WINDOW + 1; public static final int TYPE_LICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2; public static final int TYPE_LICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3; public static final int TYPE_LICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4; public static final int TYPE_LICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5; /** * End of types of sub-windows. */ public static final int LAST_SUB_WINDOW = 1999;
System Window :系统窗口
type 取值范围 [2000,2999]
如 Toast,ANR 窗口,输入法,StatusBar,NavigationBar 等。
/** * Start of system-specific window types. These are not normally * created by lications. */ 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; /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999;
之前好像看过文章说 type 值越大,层级越高, 这个观点是错的。
具体层级是下面逻辑代码,返回值越大,层级越高,最终在屏幕上显示时就越靠近用户。
frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java /** * Returns the layer assignment for the window type. Allows you to control how different * kinds of windows are ordered on-screen. * * @param type The type of window being assigned. * @param canAddInternalSystemWindow If the owner window associated with the type we are * evaluating can add internal system windows. I.e they have * {@link Manifest.permission#INTERNAL_SYSTEM_WINDOW}. If true, alert window * types {@link android.view.WindowManager.LayoutParams#isSystemAlertWindowType(int)} * can be assigned layers greater than the layer for * {@link android.view.WindowManager.LayoutParams#TYPE_LICATION_OVERLAY} Else, their * layers would be lesser. * @param roundedCornerOverlay {#code true} to indicate that the owner window is rounded corner * overlay. * @return int An arbitrary integer used to order windows, with lower numbers below higher ones. */ default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow, boolean roundedCornerOverlay) { // Always put the rounded corner layer to the top most. if (roundedCornerOverlay && canAddInternalSystemWindow) { return getMaxWindowLayer(); } if (type >= FIRST_LICATION_WINDOW && type以上方法,返回 layer,type -> layer,以上代码可以得到如下信息。
layer 取值范围 【1,36】
App 对应 APPLICATION_LAYER,值为 2,仅比 TYPE_WALLPAPER 大
Window 层级具体是怎么计算的呢?
从 System Window 中 基本已经找到答案。本章节具体说下实现细节:
mBaseLayer & mSubLayer
用来计算层级的两个参数
mSubLayer:用来计算子窗口的层级,默认值为 0
mBaseLayer:用来计算主窗口的层级。
frameworks/base/services/core/java/com/android/server/wm/WindowState.java if mAttrs.type = FIRST_SUB_WINDOW && mAttrs.type = LAST_SUB_WINDOW { mBaseLayer = mPolicy.getWindowLayerLw(parentWindow) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;// layer * 10000 + 1000 mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type); } else { mBaseLayer = mPolicy.getWindowLayerLw(this) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;// layer * 10000 + 1000 mSubLayer = 0; }WindowState 中 mBaseLayer,mSubLayer
mBaseLayer:主窗口的 type 对应 value ,计算如下
如 Activity,type 是 TYPE_BASE_APPLICATION ,getWindowLayerLw 计算返回 APPLICATION_LAYER(2),mBaseLayer = 2 * 10000 + 1000
TYPE_LAYER_MULTIPLIER:为什么要 * 10000,将阈值扩大 10000 倍,系统中可能存在相同类型的窗口有很多。
TYPE_LAYER_OFFSET:为了移动同一层级的一组窗口
以上两个常量具体怎么使用,没有研究,该值不影响本文的分析。
mSubLayer:计算规则如下,取值范围 [-2,3],TYPE_APPLICATION_ATTACHED_DIALOG 值为 1,APPLICATION_MEDIA_SUBLAYER 值为 -2,看到这里就可以想到子窗口是可以在主窗口下方,主窗口如果可以看到子窗口,必须透明。
frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java default int getSubWindowLayerFromTypeLw(int type) { switch (type) { case TYPE_LICATION_PANEL: case TYPE_LICATION_ATTACHED_DIALOG: return LICATION_PANEL_SUBLAYER;// 1 case TYPE_LICATION_MEDIA: return LICATION_MEDIA_SUBLAYER;// -2 case TYPE_LICATION_MEDIA_OVERLAY: return LICATION_MEDIA_OVERLAY_SUBLAYER;// -1 case TYPE_LICATION_SUB_PANEL: return LICATION_SUB_PANEL_SUBLAYER; // 2 case TYPE_LICATION_ABOVE_SUB_PANEL: return LICATION_ABOVE_SUB_PANEL_SUBLAYER;// 3 } Slog.e("WindowManager", "Unknown sub-window type: " + type); return 0; }Sub Window 排序
frameworks/base/services/core/java/com/android/server/wm/WindowState.java /** * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms * of z-order and 1 otherwise. */ private static final ComparatorsWindowSubLayerComparator = new Comparator () { @Override public int compare(WindowState w1, WindowState w2) { final int layer1 = w1.mSubLayer; final int layer2 = w2.mSubLayer; if (layer1 根据上文 mSubLayer 的值排序,如果是新插入的 window ,如果 sublayer 相等且为负值,放在下方,如果 sublayer 相等且为正值,放在上方。
主 Window 排序
frameworks/base/services/core/java/com/android/server/wm/WindowToken.java /** * Compares two child window of this token and returns -1 if the first is lesser than the * second in terms of z-order and 1 otherwise. */ private final ComparatormWindowComparator = (WindowState newWindow, WindowState existingWindow) -> { final WindowToken token = WindowToken.this; if (newWindow.mToken != token) { throw new IllegalArgumentException("newWindow=" + newWindow + " is not a child of token=" + token); } if (existingWindow.mToken != token) { throw new IllegalArgumentException("existingWindow=" + existingWindow + " is not a child of token=" + token); } return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1; }; protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow, WindowState existingWindow) { // New window is considered greater if it has a higher or equal base layer. return newWindow.mBaseLayer >= existingWindow.mBaseLayer; } 与 Sub Window 排序类似,按照 mBaseLayer 大小排序,如果是新插入的,且相等,放在上方。
总结
主 window 排序图示
子 window 排序图示
本文来自微信公众号:TechMerger (ID:ELC-XTLS-QSW),作者:Jingle Zhang

微信公众号
很赞哦!(0)
相关文章
文章评论
评论0
站点信息
- 微信公众号:扫描二维码,关注我们

点击排行

标签云
-
php
网页设计
个人博客
JS
个人博客
Html
春节必看: 2020新春红包大战 全攻略
新增详细玩法攻略!
支付宝集五福5亿集分宝招商银行抽现金券抖
抖音 2020 发财中国年 攻略
支付宝集五福5亿集分宝招商银行抽现金券抖
最近购买威尔胜WTB0900复刻版和WT
mysql慢查询和php-fpm慢日志
PSR-2
基础代码规范
Thinkphp
响应式
公司
整站
源码
网络科技网站模板
1024
节日
百度收录
论坛
社区
2020
豆瓣
评分最高
电影
debugger
调试
Python
语法
高德
百度地图
MySQL
追寻
webpack
vue
oracle
服务器搭建
有趣
动物
人体
历史
天文
生活
名人
体育
地理
文化
科学
心理
植物
饮食
自然
图片
JVM
IDEA
Loader
Git
UNIAPP
股票
A股
同花顺
海尔
海天味业
半年报
股市总结
歌尔股份
乐普医疗
涪陵榨菜
餐饮
财报分析
酒店
年报分析
美锦能源
山煤国际
贵州茅台
张坤
腾讯
华鲁恒升
淮北矿业
药明康德
早盘关注
国电电力
北方华创
宝丰能源
TCL中环
兔宝宝
天润乳业
启明星辰
阳光电源
山西汾酒
迈瑞医疗
人福医药
比亚迪
宁德时代
汤臣倍健
伊利股份
通威股份
东鹏饮料
隆基股份
紫金矿业
五粮液
康龙化成
赣锋锂业
爱尔眼科
片仔癀
VR
永新股份
爱美客
美的集团
格力电器
科沃斯
云南白药
同仁堂
洋河股份
白云山
三体
狂飙 原著