Android窗口管理机制深度解析(基于AOSP源码)

0 阅读5分钟

​一、窗口管理机制整体架构​

Android窗口管理以​​WindowManagerService(WMS)​​为核心,通过​​客户端-服务端(C/S)模式​​实现。其核心组件包括:

  1. ​WindowManagerService(WMS)​​:全局窗口管理者,负责窗口的添加、移除、层级排序、输入事件分发等
  • ​PhoneWindowManager(PWMS)​​:窗口策略控制者,定义窗口显示规则(如折叠屏适配、窗口类型权限校验)

  • ​WindowToken与WindowState​​:窗口的标识与状态载体,用于逻辑分组与属性管理

image.png

​二、WMS核心机制源码解析​

1. ​​窗口生命周期管理​
  • ​源码路径​​:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

  • ​关键流程​​:

    • ​窗口添加(addWindow)​​:通过IPC调用IWindowSession.add(),创建WindowState并注册到mWindowMap

      `public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs) {
          WindowState win = new WindowState(this, session, client, attrs);
          mWindowMap.put(client.asBinder(), win); // 全局存储窗口状态
          win.attach(); // 创建Surface并绑定输入通道
      }`
      
      

窗口移除(removeWindow)​​:通过WindowState.removeIfPossible()释放资源并更新层级

2. ​​输入事件分发​
  • ​源码路径​​:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
  • ​流程​​:输入事件通过InputDispatcher分发至目标窗口的InputChannel,若主线程5秒未响应则触发ANR
3. ​​层级(Z-Order)管理​
  • ​实现逻辑​​:WMS通过mWindowList维护窗口Z轴顺序,使用assignLayersLocked()动态调整:

    `void assignLayersLocked() {
        int layer = 0;
        for (WindowState win : mWindows) {
            win.mLayer = layer++;
        }
    }`
    

系统窗口(如状态栏)默认位于上层(TYPE_SYSTEM_OVERLAY

3. ​​层级(Z-Order)管理​
  • ​实现逻辑​​:WMS通过mWindowList维护窗口Z轴顺序,使用assignLayersLocked()动态调整:

    `void assignLayersLocked() {
        int layer = 0;
        for (WindowState win : mWindows) {
            win.mLayer = layer++;
        }
    }`
    

系统窗口(如状态栏)默认位于上层(TYPE_SYSTEM_OVERLAY

​DisplayContent 布局引擎​

多屏管理​​:
每个物理屏对应一个 DisplayContent,内部维护 WindowState 列表

  • ​铰链分割逻辑​​:
    折叠屏展开时,通过 computeScreenConfiguration() 动态分割布局区域
void computeScreenConfiguration(Configuration config) {
    final DisplayInfo displayInfo = updateDisplayAndOrientation(config);
    final int dw = displayInfo.logicalWidth;
    final int dh = displayInfo.logicalHeight;
    mTmpRect.set(0, 0, dw, dh);
    config.windowConfiguration.setBounds(mTmpRect);
    config.windowConfiguration.setMaxBounds(mTmpRect);
    config.windowConfiguration.setWindowingMode(getWindowingMode());
    config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());

    computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation);

    config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
            | ((displayInfo.flags & Display.FLAG_ROUND) != 0
            ? Configuration.SCREENLAYOUT_ROUND_YES
            : Configuration.SCREENLAYOUT_ROUND_NO);

    config.densityDpi = displayInfo.logicalDensityDpi;

    config.colorMode =
            ((displayInfo.isHdr() && mWmService.hasHdrSupport())
                    ? Configuration.COLOR_MODE_HDR_YES
                    : Configuration.COLOR_MODE_HDR_NO)
                    | (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport()
                    ? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
                    : Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);

    // Update the configuration based on available input devices, lid switch,
    // and platform configuration.
    config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
    config.keyboard = Configuration.KEYBOARD_NOKEYS;
    config.navigation = Configuration.NAVIGATION_NONAV;

    int keyboardPresence = 0;
    int navigationPresence = 0;
    final InputDevice[] devices = mWmService.mInputManager.getInputDevices();
    final int len = devices != null ? devices.length : 0;
    for (int i = 0; i < len; i++) {
        InputDevice device = devices[i];
        // Ignore virtual input device.
        if (device.isVirtual()) {
            continue;
        }

        // Check if input device can dispatch events to current display.
        if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
            continue;
        }

        final int sources = device.getSources();
        final int presenceFlag = device.isExternal()
                ? WindowManagerPolicy.PRESENCE_EXTERNAL : WindowManagerPolicy.PRESENCE_INTERNAL;

        if (mWmService.mIsTouchDevice) {
            if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) {
                config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
            }
        } else {
            config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
        }

        if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
            config.navigation = Configuration.NAVIGATION_TRACKBALL;
            navigationPresence |= presenceFlag;
        } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
                && config.navigation == Configuration.NAVIGATION_NONAV) {
            config.navigation = Configuration.NAVIGATION_DPAD;
            navigationPresence |= presenceFlag;
        }

        if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            keyboardPresence |= presenceFlag;
        }
    }

    if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) {
        config.navigation = Configuration.NAVIGATION_DPAD;
        navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
    }

    // Determine whether a hard keyboard is available and enabled.
    // TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
    boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
    if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) {
        mWmService.mHardKeyboardAvailable = hardKeyboardAvailable;
        mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
        mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
    }

    mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();

    // Let the policy update hidden states.
    config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
    config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
    config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
    mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
3. ​​窗口动画子系统​
  • ​动画驱动​​:
    WindowAnimator 通过 Choreographer 同步 VSYNC 信号
private void animate(long frameTimeNs) {
    if (!mInitialized) {
        return;
    }

    // Schedule next frame already such that back-pressure happens continuously.
    scheduleAnimation();

    final RootWindowContainer root = mService.mRoot;
    final boolean useShellTransition = root.mTransitionController.isShellTransitionsEnabled();
    final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
    boolean rootAnimating = false;
    mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
    mBulkUpdateParams = 0;
    root.mOrientationChangeComplete = true;
    if (DEBUG_WINDOW_TRACE) {
        Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
    }

    ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
    try {
        // Remove all deferred displays, tasks, and activities.
        root.handleCompleteDeferredRemoval();

        final AccessibilityController accessibilityController =
                mService.mAccessibilityController;
        final int numDisplays = root.getChildCount();
        for (int i = 0; i < numDisplays; i++) {
            final DisplayContent dc = root.getChildAt(i);
            // Update animations of all applications, including those associated with
            // exiting/removed apps.
            dc.updateWindowsForAnimator();
            dc.prepareSurfaces();
        }

        for (int i = 0; i < numDisplays; i++) {
            final DisplayContent dc = root.getChildAt(i);

            dc.checkAppWindowsReadyToShow();
            if (accessibilityController.hasCallbacks()) {
                accessibilityController.drawMagnifiedRegionBorderIfNeeded(dc.mDisplayId,
                        mTransaction);
            }

            if (dc.isAnimating(animationFlags, ANIMATION_TYPE_ALL)) {
                rootAnimating = true;
                if (!dc.mLastContainsRunningSurfaceAnimator) {
                    dc.mLastContainsRunningSurfaceAnimator = true;
                    dc.enableHighFrameRate(true);
                }
            } else if (dc.mLastContainsRunningSurfaceAnimator) {
                dc.mLastContainsRunningSurfaceAnimator = false;
                dc.enableHighFrameRate(false);
            }
            mTransaction.merge(dc.getPendingTransaction());
        }

        cancelAnimation();

        if (mService.mWatermark != null) {
            mService.mWatermark.drawIfNeeded();
        }

    } catch (RuntimeException e) {
        Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
    }

    final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
    final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
            && root.copyAnimToLayoutParams();
    if (hasPendingLayoutChanges || doRequest) {
        mService.mWindowPlacerLocked.requestTraversal();
    }

    if (rootAnimating && !mLastRootAnimating) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
    }
    if (!rootAnimating && mLastRootAnimating) {
        mService.mWindowPlacerLocked.requestTraversal();
        Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
    }
    mLastRootAnimating = rootAnimating;

    // APP_TRANSITION, SCREEN_ROTATION, TYPE_RECENTS are handled by shell transition.
    if (!useShellTransition) {
        updateRunningExpensiveAnimationsLegacy();
    }

    Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "applyTransaction");
    mTransaction.apply();
    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
    mService.mWindowTracing.logState("WindowAnimator");
    ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");

    mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
    executeAfterPrepareSurfacesRunnables();

    if (DEBUG_WINDOW_TRACE) {
        Slog.i(TAG, "!!! animate: exit"
                + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
                + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
    }
}

​三、PhoneWindowManager(PWMS)策略控制​

1. ​​折叠屏适配​
  • ​源码路径​​:frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

  • ​关键方法​​:

        `public void adjustWindowParamsLw(WindowState win, LayoutParams attrs) {
            if (isFolded()) {
                attrs.flags |= FLAG_LAYOUT_IN_SCREEN; // 折叠态强制全屏
            }
        }`
        
    

PWMS监听铰链角度(通过Sensor.TYPE_HINGE_ANGLE),动态调整窗口布局

2. ​​Jetpack WindowManager 集成​
  • ​显示特征获取​​:
    通过 WindowInfoTracker 监听 FoldingFeature 状态

    `WindowInfoTracker.getOrCreate(this).windowLayoutInfo.collect { layoutInfo ->
        layoutInfo.displayFeatures.forEach { feature ->
            if (feature is FoldingFeature) updateLayout(feature.bounds)
        }
    }`
    
    

四、输入事件分发机制​

1. ​​事件传递链路​
  • ​硬件层​​:InputReader 读取事件,存入 InputDispatcher 队列

  • ​焦点窗口判定​​:
    InputDispatcher.findFocusedWindowTargetsLocked() 通过 mFocusedApp 确定目标窗口

  • ​ANR 触发​​:
    若窗口 5 秒未响应事件,调用 appNotResponding() 生成 ANR 日志

2. ​​InputChannel 与同步屏障​
  • ​跨进程通信​​:
    InputChannel 基于共享内存和 epoll 实现高效事件传递
// frameworks/native/libs/input/InputTransport.cpp

五,WMS 核心架构与初始化流程​

1. ​​系统服务定位与依赖关系​

WMS 是 Android 系统的核心服务,驻留在 system_server 进程,通过 Binder IPC 与客户端(如应用进程)交互。其架构依赖以下组件:

  • ​InputManagerService (IMS)​​:处理输入事件分发。
  • ​SurfaceFlinger​​:管理窗口的 Surface 合成。
  • ​ActivityManagerService (AMS)​​:协调 Activity 生命周期与窗口状态
  • ​PhoneWindowManager (PWMS)​​:定义窗口策略(如折叠屏适配
2. ​​WMS 启动流程​
  1. ​SystemServer 初始化​​:
    WMS 在 SystemServer.startOtherServices() 中通过 WindowManagerService.main() 创建

    `// SystemServer.java
    wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, new UiThread());`
    
    **​关键对象构建​**​:
    
  • ​DisplayContent​​:管理物理屏幕的窗口树

  • ​WindowAnimator​​:驱动窗口动画

  • ​RootWindowContainer​​:全局窗口树的根容器

  • ​策略初始化​​:
    调用 PhoneWindowManager.init() 绑定窗口策略

六、关键调试工具与性能优化​

1. ​​源码级调试命令​
  • ​窗口状态查看​​:

    `adb shell dumpsys window windows    # 查看所有WindowState的mFrame和mLayer
    adb shell dumpsys SurfaceFlinger    # 分析Surface合成性能`
    
    -   **​布局耗时分析​**​:  
    

    使用 Systrace 抓取 performLayoutAndPlaceSurfacesLocked() 耗时(网页18]。

2. ​​性能优化策略​
  • ​减少布局计算频率​​:
    合并多次 relayoutWindow() 调用,使用 ViewTreeObserver.OnPreDrawListener 批量更新

  • ​硬件加速优化​​:
    强制启用 ThreadedRenderer,分屏场景下使用 HardwareLayer 缓存复杂 UI

知识点延伸

一,Android Framework核心服务联动​

 1.**AMS与WMS协同机制**
 2.**SurfaceFlinger合成流程**
 3.**Input系统整合**
 4.。。。。。

二,​​跨进程通信优化​

三,WMS优化方案,分仓策略与自动流水化设计

四,智能仓储与自动化技术​

AGV与AS/RS系统集成​​:
学习如何通过WMS与自动化存储检索系统(AS/RS)协同,实现货架动态分配与路径规划

​IoT传感器网络​​:
集成温湿度/RFID传感器数据,结合WMS的库存管理模块实现冷链物流的实时监控

.....

五,AI驱动的预测与决策​

  • ​库存预测模型​​:
    应用LSTM神经网络分析历史销售数据,优化WMS的补货策略

  • ​路径优化算法​​:
    在拣货场景中,使用蚁群算法或A*算法动态生成最优拣货路径

    ......