一、窗口管理机制整体架构
Android窗口管理以WindowManagerService(WMS)为核心,通过客户端-服务端(C/S)模式实现。其核心组件包括:
- WindowManagerService(WMS):全局窗口管理者,负责窗口的添加、移除、层级排序、输入事件分发等
-
PhoneWindowManager(PWMS):窗口策略控制者,定义窗口显示规则(如折叠屏适配、窗口类型权限校验)
-
WindowToken与WindowState:窗口的标识与状态载体,用于逻辑分组与属性管理
二、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 启动流程
-
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
知识点延伸
1.**AMS与WMS协同机制**
2.**SurfaceFlinger合成流程**
3.**Input系统整合**
4.。。。。。
AGV与AS/RS系统集成:
学习如何通过WMS与自动化存储检索系统(AS/RS)协同,实现货架动态分配与路径规划
IoT传感器网络:
集成温湿度/RFID传感器数据,结合WMS的库存管理模块实现冷链物流的实时监控
.....
-
库存预测模型:
应用LSTM神经网络分析历史销售数据,优化WMS的补货策略 -
路径优化算法:
在拣货场景中,使用蚁群算法或A*算法动态生成最优拣货路径......