赞
踩
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_APPLICATION
- 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=APPLICATION_SUB_PANEL
- isOnScreen=true
- sVisible=true
显示Dialog
- Window #11 Window{a08f90b ...}:
- ty=APPLICATION
- isOnScreen=true
- isVisible=true
不难看出,Window 层级与 ty 有关系的,ty 是 type 的简写。
type 取值范围 [1,99]
- /**
- * Start of window types that represent normal application windows.
- */
- public static final int FIRST_APPLICATION_WINDOW = 1;
- // activity 会使用 此 type
- public static final int TYPE_BASE_APPLICATION = 1;
- // dialog 会使用 此 type
- public static final int TYPE_APPLICATION = 2;
- // 冷启动会显示的 Window,真正启动页面显示之前的画面
- public static final int TYPE_APPLICATION_STARTING = 3;
- // 没玩过
- public static final int TYPE_DRAWN_APPLICATION = 4;
- /**
- * End of types of application windows.
- */
- public static final int LAST_APPLICATION_WINDOW = 99;

子窗口:顾名思义,对应有主窗口。子窗口需要附在主窗口上,如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_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;
- /**
- * End of types of sub-windows.
- */
- public static final int LAST_SUB_WINDOW = 1999;

type 取值范围 [2000,2999]
如Toast,ANR 窗口,输入法,StatusBar,NavigationBar 等。
- /**
- * Start of system-specific window types. These are not normally
- * created by applications.
- */
- 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_APPLICATION_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_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
- return APPLICATION_LAYER;// APPLICATION_LAYER = 2
- }
-
- switch (type) {
- case TYPE_WALLPAPER:
- // wallpaper is at the bottom, though the window manager may move it.
- return 1;
- case TYPE_PRESENTATION:
- case TYPE_PRIVATE_PRESENTATION:
- case TYPE_DOCK_DIVIDER:
- case TYPE_QS_DIALOG:
- case TYPE_PHONE:
- return 3;
- case TYPE_SEARCH_BAR:
- case TYPE_VOICE_INTERACTION_STARTING:
- return 4;
- case TYPE_VOICE_INTERACTION:
- // voice interaction layer is almost immediately above apps.
- return 5;
- case TYPE_INPUT_CONSUMER:
- return 6;
- case TYPE_SYSTEM_DIALOG:
- return 7;
- case TYPE_TOAST:
- // toasts and the plugged-in battery thing
- return 8;
- case TYPE_PRIORITY_PHONE:
- // SIM errors and unlock. Not sure if this really should be in a high layer.
- return 9;
- case TYPE_SYSTEM_ALERT:
- // like the ANR / app crashed dialogs
- // Type is deprecated for non-system apps. For system apps, this type should be
- // in a higher layer than TYPE_APPLICATION_OVERLAY.
- return canAddInternalSystemWindow ? 13 : 10;
- case TYPE_APPLICATION_OVERLAY:
- return 12;
- case TYPE_INPUT_METHOD:
- // on-screen keyboards and other such input method user interfaces go here.
- return 15;
- case TYPE_INPUT_METHOD_DIALOG:
- // on-screen keyboards and other such input method user interfaces go here.
- return 16;
- case TYPE_STATUS_BAR:
- return 17;
- case TYPE_STATUS_BAR_ADDITIONAL:
- return 18;
- case TYPE_NOTIFICATION_SHADE:
- return 19;
- case TYPE_STATUS_BAR_SUB_PANEL:
- return 20;
- case TYPE_KEYGUARD_DIALOG:
- return 21;
- case TYPE_VOLUME_OVERLAY:
- // the on-screen volume indicator and controller shown when the user
- // changes the device volume
- return 22;
- case TYPE_SYSTEM_OVERLAY:
- // the on-screen volume indicator and controller shown when the user
- // changes the device volume
- return canAddInternalSystemWindow ? 23 : 11;
- case TYPE_NAVIGATION_BAR:
- // the navigation bar, if available, shows atop most things
- return 24;
- case TYPE_NAVIGATION_BAR_PANEL:
- // some panels (e.g. search) need to show on top of the navigation bar
- return 25;
- case TYPE_SCREENSHOT:
- // screenshot selection layer shouldn't go above system error, but it should cover
- // navigation bars at the very least.
- return 26;
- case TYPE_SYSTEM_ERROR:
- // system-level error dialogs
- return canAddInternalSystemWindow ? 27 : 10;
- case TYPE_MAGNIFICATION_OVERLAY:
- // used to highlight the magnified portion of a display
- return 28;
- case TYPE_DISPLAY_OVERLAY:
- // used to simulate secondary display devices
- return 29;
- case TYPE_DRAG:
- // the drag layer: input for drag-and-drop is associated with this window,
- // which sits above all other focusable windows
- return 30;
- case TYPE_ACCESSIBILITY_OVERLAY:
- // overlay put by accessibility services to intercept user interaction
- return 31;
- case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
- return 32;
- case TYPE_SECURE_SYSTEM_OVERLAY:
- return 33;
- case TYPE_BOOT_PROGRESS:
- return 34;
- case TYPE_POINTER:
- // the (mouse) pointer layer
- return 35;
- default:
- Slog.e("WindowManager", "Unknown window type: " + type);
- return 3;
- }
- }

以上方法,返回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_APPLICATION_PANEL:
- case TYPE_APPLICATION_ATTACHED_DIALOG:
- return APPLICATION_PANEL_SUBLAYER;// 1
- case TYPE_APPLICATION_MEDIA:
- return APPLICATION_MEDIA_SUBLAYER;// -2
- case TYPE_APPLICATION_MEDIA_OVERLAY:
- return APPLICATION_MEDIA_OVERLAY_SUBLAYER;// -1
- case TYPE_APPLICATION_SUB_PANEL:
- return APPLICATION_SUB_PANEL_SUBLAYER; // 2
- case TYPE_APPLICATION_ABOVE_SUB_PANEL:
- return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;// 3
- }
- Slog.e("WindowManager", "Unknown sub-window type: " + type);
- return 0;
- }

- 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 Comparator<WindowState> sWindowSubLayerComparator =
- new Comparator<WindowState>() {
- @Override
- public int compare(WindowState w1, WindowState w2) {
- final int layer1 = w1.mSubLayer;
- final int layer2 = w2.mSubLayer;
- if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
- // We insert the child window into the list ordered by
- // the sub-layer. For same sub-layers, the negative one
- // should go below others; the positive one should go
- // above others.
- return -1;
- }
- return 1;
- };
- };

根据上文 mSubLayer 的值排序,如果是新插入的 window ,如果 sublayer 相等且为负值,放在下方,如果 sublayer 相等且为正值,放在上方。
- 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 Comparator<WindowState> mWindowComparator =
- (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 排序图示
————————————————
版权声明:本文为CSDN博主「Jingle.zhang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_58994853/article/details/125493914
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。