基于 Android 9.0 源码。

SystemUI 的 StatusBarNavigationBar 是在 PhoneWindowManager 中布局的。PhoneWindowManagermLastSystemUiFlags 字段中保存着 SystemUI 的 visibility,visibility 指的是可见、半透明、透明等等。

mLastSystemUiFlags 字段是在 updateSystemUiVisibilityLw() 方法中赋值的,而新的 visibility 值是调用 updateSystemBarsLw() 方法返回的值。

1. winCandidate

private int updateSystemUiVisibilityLw() {
    // If there is no window focused, there will be nobody to handle the events
    // anyway, so just hang on in whatever state we're in until things settle down.
    WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
            : mTopFullscreenOpaqueWindowState;
    if (winCandidate == null) {
        return 0;
    }
    if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
        // The immersive mode confirmation should never affect the system bar visibility,
        // otherwise it will unhide the navigation bar and hide itself.
        winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
        if (winCandidate == null) {
            return 0;
        }
    }
    final WindowState win = winCandidate;
    ...
}

SystemUI 的 visibility 受 winCandidate 的影响,当 mFocusedWindow(获得焦点的 Window)不为 null 时,winCandidatemFocusedWindow,否则是 mTopFullscreenOpaqueWindowStatemTopFullscreenOpaqueWindowState 是布局在最上层,且不透明的全屏的应用的 Window。它是在 applyPostLayoutPolicyLw() 方法中赋值的。

如果当前的 Window 是 immersive mode confirmation window,则使用 mTopFullscreenOpaqueWindowState,因为沉浸式确认窗口应该影响 system bar 的 visibility。

2. tmpVisibility

private int updateSystemUiVisibilityLw() {
    ...
    int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
            & ~mResettingSystemUiFlags
            & ~mForceClearedSystemUiFlags;
    if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
        tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
    }
    ...
}

tmpVisibility 由三个部分设置。第一和第二是在 PolicyControl.getSystemUiVisibility() 方法中。第三部分是需求强制取消的 flag,如 mResettingSystemUiFlagsmForceClearedSystemUiFlagsSYSTEM_UI_CLEARABLE_FLAGS 常量包含隐藏 StatusBar 和 NavigationBar 的 flag。

public static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
    attrs = attrs != null ? attrs : win.getAttrs();
    int vis = win != null ? win.getSystemUiVisibility()
            : (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility);
    if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
        vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.STATUS_BAR_TRANSLUCENT);
    }
    if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) {
        vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
        vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.NAVIGATION_BAR_TRANSLUCENT);
    }
    return vis;
}

为什么说该方法有两个部分,因为当 win != null 时,会调用 win 的 getSystemUiVisibility() 方法,这其实就是当一个 View 调用 setSystemUiVisibility() 方法设置的 flag,这是一部分。

另一部分是 sImmersiveStatusFiltersImmersiveNavigationFilter 相关的逻辑,这是与强制沉浸模式(immersive mode)或沉浸模式无效相关。

3. fullscreenVisibility 和 dockedVisibility

fullscreenVisibility 主要由 mTopFullscreenOpaqueWindowState 影响,dockedVisibility 主要由 mTopDockedOpaqueWindowState 影响。第一个 WindowState 指的是最上层全屏覆盖(占满应用 Frame)的窗口,第二个 WindowState 指的是分屏状态下被固定的那一侧的窗口(按 Home 后不会回到 Home 的那一侧)。

private int updateSystemUiVisibilityLw() {
    ...
    final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
            mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
    final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
            mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
    mWindowManagerFuncs.getStackBounds(
            WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
    mWindowManagerFuncs.getStackBounds(
            WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
    ...
}

然后调用 getStackBounds() 方法设置 mNonDockedStackBoundsmDockedStackBounds。该方法会根据 windowingModeactivityMode 查找对应的 TaskStack 对象,然后用该对象的 getBounds() 设置传递的 bounds。

4. updateSystemBarsLw()

updateSystemBarsLw() 方法是计算新的 visibility 的方法,传入参数 winCandidatemLastSystemUiFlagstmpVisibility