Me pregunto si los desarrolladores de la aplicación han descubierto que la barra de estado siempre ha estado cubierta en la aplicación. Ya sea cambiando el color o escribiendo un marco flotante, no puede cubrir la barra de estado. Desarrollo del marco, habrá algunas personalizaciones, como cubrir la barra de estado, si no lo entiende, puede usarlo incorrectamente y aparecerán algunos errores innecesarios.El documento oficial no enumera las reglas a nivel de ventana. Así que espero compartir con ustedes lo siguiente, cómo Android desarrolla reglas de jerarquía de visualización.
Sobre el concepto de Ventana en Android
De hecho, es fácil de entender, al igual que usar el sistema operativo Windows a menudo, cuando se abre una aplicación y aparece una interfaz, podemos entender que aparece una ventana, entonces Ventana ≠ Ver
Se puede entender que una Actividad corresponde a una Ventana, y los estudiantes que entienden el código fuente saben que: ViewRootImpl corresponde a una Ventana.
¿Cómo ver Ventana?
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
En general, las ventanas anteriores existirán en los teléfonos móviles y el orden jerárquico es de alto -> bajo.
Mostrar ventana emergente
Window #11 Window{513f711 u0 PopupWindow:3e4bfb}:
ty=APPLICATION_SUB_PANEL
isOnScreen=true
sVisible=true
Mostrar cuadro de diálogo
Window #11 Window{a08f90b ...}:
ty=APPLICATION
isOnScreen=true
isVisible=true
No es difícil ver que el nivel de la ventana ty
está relacionado con laty
abreviatura de .type
Clasificación de ventanas
Ventana de la aplicación: ventana de la aplicación
rango de valores de tipo [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;
Subventana: ventana secundaria
Ventana secundaria: como su nombre lo indica, hay una ventana principal. La ventana secundaria debe adjuntarse a la ventana principal, como PopWindow
rango de valores de tipo [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;
Ventana del sistema: ventana del sistema
rango de valores de tipo [2000,2999]
Como Toast, ventana ANR, método de entrada, StatusBar, NavigationBar, etc.
/**
* 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 值越大,层级越高
, esta vista es incorrecta .
El nivel específico es el siguiente código lógico, 返回值越大,层级越高
cuanto más cerca esté del usuario cuando finalmente se muestre en pantalla.
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;
}
}
El método anterior devuelve capa, tipo -> capa, el código anterior puede obtener la siguiente información.
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;
}
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 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 相等且为正值,放在上方。
主 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 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 排序图示