Android T StartingWindow 源码深度解析:从 ActivityStarter 到 WMShell 的完整绘制与移除流程

Android T StartingWindow 源码深度解析:从 ActivityStarter 到 WMShell 的完整绘制与移除流程 Android T StartingWindow 源码解析与实现机制当我们在Android设备上点击一个应用图标时系统会立即展示一个过渡界面这个界面就是StartingWindow启动窗口。作为Android系统窗口管理的重要组成部分StartingWindow机制在提升用户体验方面发挥着关键作用。本文将深入剖析Android T版本中StartingWindow的实现原理从系统架构到具体实现细节为开发者提供全面的技术解析。1. StartingWindow 核心概念与设计初衷StartingWindow是Android系统在应用Activity真正显示前展示的过渡窗口主要用于解决以下几个核心问题视觉连续性在Activity完成布局加载和绘制前提供平滑的视觉过渡性能感知优化掩盖进程创建、资源加载等耗时操作带来的延迟感品牌展示允许应用通过主题定制展示品牌标识和启动画面在Android T中StartingWindow的实现涉及多个系统组件的协作SystemServer进程AMS/WMS ↔ Binder通信 ↔ WMShell进程SystemUI这种跨进程设计将窗口管理的核心逻辑与具体实现分离提高了系统的模块化和可维护性。StartingWindow的三种类型类型常量值使用场景特点无启动窗口STARTING_WINDOW_TYPE_NONE应用内Activity切换不显示任何过渡窗口快照启动窗口STARTING_WINDOW_TYPE_SNAPSHOT任务切换到前台使用最近任务快照闪屏启动窗口STARTING_WINDOW_TYPE_SPLASH_SCREEN应用冷启动基于应用主题的空白窗口2. StartingWindow 创建流程深度解析StartingWindow的创建始于Activity的启动请求整个流程跨越多个系统组件。以下是关键步骤的详细分析2.1 启动入口与类型决策创建流程的起点是ActivityStarter.startActivityLocked()经过以下调用链ActivityStarter.startActivityLocked() → Task.startActivityLocked() → ActivityRecord.showStartingWindow() → ActivityRecord.addStartingWindow()类型决策的核心逻辑位于ActivityRecord.getStartingWindowType()该方法基于多个参数确定启动窗口类型private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean activityAllDrawn, TaskSnapshot snapshot) { // 特殊场景处理任务启动器(trampoline activity) if (!newTask taskSwitch processRunning !activityCreated task.intent ! null mActivityComponent.equals(task.intent.getComponent())) { if (topAttached ! null topAttached.isSnapshotCompatible(snapshot)) { return STARTING_WINDOW_TYPE_SNAPSHOT; } return STARTING_WINDOW_TYPE_NONE; } // 常规决策逻辑 if ((newTask || !processRunning || (taskSwitch !activityCreated)) !isActivityTypeHome()) { return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } if (taskSwitch allowTaskSnapshot) { if (isSnapshotCompatible(snapshot)) { return STARTING_WINDOW_TYPE_SNAPSHOT; } if (!isActivityTypeHome()) { return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } } return STARTING_WINDOW_TYPE_NONE; }2.2 跨进程协作实现确定启动窗口类型后系统通过以下流程完成实际创建SystemServer侧准备数据创建StartingData子类实例SplashScreenStartingData或SnapshotStartingData通过TaskOrganizerController建立与WMShell的通信通道WMShell侧创建窗口StartingWindowController接收创建请求StartingSurfaceDrawer根据类型执行具体创建逻辑对于SplashScreen类型构建ContentView并添加窗口对于Snapshot类型直接使用任务快照创建窗口关键数据结构对比数据结构所在进程职责StartingDataSystemServer抽象启动窗口数据模型StartingSurfaceControllerSystemServer控制系统与WMShell的交互StartingWindowControllerWMShell启动窗口操作入口StartingSurfaceDrawerWMShell具体实现窗口创建/移除3. StartingWindow 移除机制分析StartingWindow的移除与Activity的绘制状态密切相关主要触发点是主窗口完成绘制。以下是移除流程的关键节点3.1 移除触发路径应用绘制完成通知ViewRootImpl.reportDrawFinished() → WindowSession.finishDrawing() → WMS.finishDrawingWindow()窗口系统处理WMS.requestTraversal() → WindowSurfacePlacer.performSurfacePlacement() → RootWindowContainer.applySurfaceChangesTransaction()启动窗口移除决策WindowState.performShowLocked() → ActivityRecord.onFirstWindowDrawn() → ActivityRecord.removeStartingWindow()3.2 移除动画处理Android T引入了启动窗口退出动画机制其实现依赖于应用是否设置了退出动画监听器// 应用侧设置退出动画 splashScreen.setOnExitAnimationListener { view - // 自定义退出动画 view.animate().alpha(0f).setDuration(300).start() }系统处理流程如下动画触发ActivityRecord.removeStartingWindow() → transferSplashScreenIfNeeded() → requestCopySplashScreen()跨进程协作WMShell复制SplashScreen视图通过ActivityTaskManagerService通知应用进程应用接收视图并执行动画最终移除 动画完成后系统调用removeStartingWindowAnimation()完成窗口移除。4. 关键实现细节与优化技巧4.1 性能优化设计异步创建机制 StartingWindow的创建通过AddStartingWindow异步任务执行避免阻塞主线程private class AddStartingWindow implements Runnable { Override public void run() { // 异步执行窗口创建 StartingSurface surface startingData.createStartingSurface(); // 处理结果... } }资源复用策略相同Activity的连续启动会复用已有StartingWindow快照类型窗口直接复用任务快照减少资源创建开销4.2 常见问题解决方案问题1温启动白屏当应用保活机制导致Welcome Activity被跳过时可能出现白屏。解决方案是在getStartingWindowType()中添加特殊处理if (isWarmStart snapshot ! null) { return STARTING_WINDOW_TYPE_NONE; }问题2启动图标定制修改SplashScreen的图标显示逻辑// 在SplashscreenContentDrawer.makeSplashScreenContentView中 if (attrs.mSplashScreenIcon null) { attrs.mSplashScreenIcon new ColorDrawable(Color.TRANSPARENT); }问题3窗口统计异常修正ActivityRecord中的窗口统计逻辑boolean isInterestingWindow(WindowState w) { return w.mAttrs.type ! TYPE_APPLICATION_STARTING w.isVisibleNow(); }5. 调试与定制开发实践5.1 调试技巧日志过滤adb logcat -s WindowManager | grep StartingWindow关键属性检查adb shell dumpsys window windows | grep -A 10 StartingWindowWMShell调试adb shell dumpsys activity service SystemUIService WMShell5.2 定制开发指南编译与部署# 系统侧修改 make framework adb push framework.jar /system/framework/ # WMShell侧修改 make SystemUI adb push SystemUI.apk /system_ext/priv-app/SystemUI/主题定制示例style nameAppStartingTheme parentAndroid:Theme.Light.NoTitleBar item nameandroid:windowBackgrounddrawable/app_starting_bg/item item nameandroid:windowSplashScreenAnimatedIcondrawable/app_icon/item item nameandroid:windowSplashScreenAnimationDuration300/item /style在实际项目中修改StartingWindow行为时需要特别注意跨进程交互的同步问题以及窗口Z-order的管理。通过合理利用现有扩展点可以在不修改系统核心代码的情况下实现大多数定制需求。