An article to understand the Window hierarchy order

I wonder if the app developers have discovered that the StatusBar has always been covered on the App. Whether it is changing the color or writing a floating frame, it cannot cover the StatusBar. Framework development, there will be some customizations, such as covering the StatusBar, if you don't understand it, you may use it incorrectly, and some unnecessary bugs will appear. The official document does not list the rules at the Window level. So I hope to share with you the following, how Android formulates display hierarchy rules.

About the concept of Window in Android

In fact, it is easy to understand. Just like using the Windows operating system often, when an application is opened and an interface appears, we can understand that a window appears, so Window ≠ View

An Activity can be understood to correspond to a Window, and students who understand the source code know that: ViewRootImpl corresponds to a Window.

How to see 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

Generally, the above windows will exist on mobile phones, and the hierarchical order is from high -> low.

Show PopWindow

 Window #11 Window{513f711 u0 PopupWindow:3e4bfb}:
   ty=APPLICATION_SUB_PANEL
   isOnScreen=true
   sVisible=true

Show Dialog

Window #11 Window{a08f90b ...}:
  ty=APPLICATION
  isOnScreen=true
  isVisible=true

It is not difficult to see that the Window level tyis related to tyis typethe abbreviation of .

Classification of Windows

Application Window: Application window

type value range [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;

Sub Window: child window

Child window: As the name suggests, there is a main window. The child window needs to be attached to the main window, such as PopWindow

type value range [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;

System Window : System window

type value range [2000,2999]

Such as Toast, ANR window, input method, 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 值越大,层级越高, this view is wrong .

The specific level is the following logic code, 返回值越大,层级越高, and the closer it is to the user when it is finally displayed on the screen.

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

The above method returns layer, type -> layer, the above code can get the following information.

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顺序.png 子 window 排序图示 SubWindow顺序.png

Guess you like

Origin juejin.im/post/7118934491631976461