Android 应用启动窗口(Splash Screen)的创建与销毁流程

Android 应用冷启动窗口(Splash Screen)的创建与销毁流程,涵盖从 ActivityManagerService(AMS)到 WindowManagerService(WMS)的完整交互过程:

一、冷启动窗口的核心作用

冷启动窗口是 Android 在应用冷启动时显示的预览窗口 ,其目的是:

减少用户感知延迟 :在应用进程创建和界面绘制完成前,显示一个临时界面(如 Logo 或主题背景)。

避免白屏/黑屏 :防止用户看到未初始化的空白界面。

兼容性控制 :通过统一的 API 管理启动窗口的生命周期。

二、冷启动窗口的创建流程

  1. AMS 触发应用启动

当用户点击应用图标时,ActivityManagerService 会通过 startActivity() 启动目标 Activity:

java 复制代码
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final int startActivity(...) {

    // 调用 startActivityMayWait 处理启动请求

    return startActivityMayWait(...);

}

关键逻辑:

如果应用进程未启动,AMS 会调用 startProcessLocked() 请求 Zygote 创建新进程。

在进程创建前,AMS 会通过 WMS 预先创建启动窗口。

  1. WMS 创建启动窗口

AMS 通过 IWindowManager 接口请求 WMS 创建启动窗口:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public void addAppToken(...) {

    // 创建启动窗口(Splash Screen)

    createSplashScreenWindow(appToken, info, windowingMode, requestedOrientation);

}

调用链:

AMS.realStartActivityLocked()

→ WMS.addAppToken()

→ WMS.createSplashScreenWindow()

核心代码:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/SplashScreenWindow.java

private void createSplashScreenWindow(...) {

    // 1. 加载应用的主题样式(来自 AndroidManifest.xml)

    final int theme = info.resolveThemeResId();


    // 2. 创建 SurfaceControl(用于绘制窗口)

    mSurfaceControl = new SurfaceControl.Builder()

        .setName("Splash Screen")

        .setParent(rootSurfaceControl)

        .build();
java 复制代码
 // 3. 设置窗口属性(如背景、动画) 
java 复制代码
 final WindowState windowState = new WindowState(this, session, client, token, type, displayContent);

    windowState.setWindowAnimations(...); // 设置启动动画

    windowState.setDimmingEnabled(false); // 禁用暗化效果

}

关键数据结构:

AppWindowToken :AMS 为每个 Activity 分配的令牌(Token),用于标识应用窗口。

SplashScreenWindow :WMS 中管理启动窗口的类,负责窗口的创建和销毁。

  1. 启动窗口的显示

WMS 将启动窗口添加到 DisplayContent 的窗口层级中。

SurfaceFlinger 将窗口合成到屏幕显示:

java 复制代码
// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::handleTransactionLocked(...) {

    // 合成所有窗口并更新屏幕

    rebuildLayerStacks();

}

三、冷启动窗口的销毁流程

  1. 应用进程完成绘制

应用进程启动后,ActivityThread 会执行 handleLaunchActivity():

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java

private void handleLaunchActivity(...) {

    // 1. 创建 Context 和 Activity

    Activity a = performLaunchActivity(...);


    // 2. 执行 Activity.onCreate() 和 setContentView()

    a.onCreate(...);


    // 3. 报告界面绘制完成

    WindowManagerGlobal.getInstance().reportDrawn();

}

核心逻辑:

通过 ViewRootImpl 向 WMS 报告界面已绘制完成:

java 复制代码
// frameworks/base/core/java/android/view/ViewRootImpl.java

public void reportDrawn() {

    mWindowSession.finishDrawing(mWindow);

}
  1. WMS 销毁启动窗口

WMS 收到 finishDrawing() 请求后,会移除启动窗口:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public void finishDrawingWindow(WindowState windowState) {

    // 1. 移除启动窗口

    windowState.removeSplashScreen();


    // 2. 通知 AMS 应用已就绪

    mActivityManager.notifyAppTransitionFinished();

}

销毁逻辑详解:

移除 SurfaceControl :释放启动窗口占用的图形资源。

触发动画结束 :如果启动窗口有退出动画,WMS 会执行动画并延迟销毁窗口。

更新窗口层级 :重新计算窗口层级(Z-Order),确保主 Activity 窗口显示在前台。

  1. AMS 清理启动状态

AMS 收到通知后,会清理冷启动标记:

java 复制代码
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public void notifyAppTransitionFinished(...) {

    // 清除冷启动窗口标记

    app.getWindowProcessController().setHasShownSplashScreen(true);

}

四、冷启动窗口的样式与资源加载

  1. 主题配置(Android 12+)

从 Android 12 开始,冷启动窗口由 SplashScreen API 控制:

XML 复制代码
<!-- AndroidManifest.xml -->

<activity

    android:theme="@style/AppTheme.SplashScreen">

    ...

</activity>

样式定义:

XML 复制代码
<!-- res/values/styles.xml -->

<style name="AppTheme.SplashScreen" parent="Theme.SplashScreen">

    <item name="windowSplashScreenBackground">@color/splash_background</item>

    <item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>

    <item name="postSplashScreenTheme">@style/AppTheme.NoActionBar</item>

</style>
  1. 旧版本兼容方案

在 Android 11 及以下版本,冷启动窗口通过 Theme.AppCompat.Light.NoActionBar 等主题模拟实现。

五、代码流程图

用户点击应用图标

↓

AMS.startActivity() → AMS.startProcessLocked()

↓

WMS.createSplashScreenWindow() → 加载主题样式,创建SurfaceControl

↓

SurfaceFlinger 合成窗口 → 显示启动界面

↓

应用进程启动 → ActivityThread.handleLaunchActivity()

↓

ViewRootImpl.reportDrawn() → WMS.finishDrawingWindow()

↓

WMS.removeSplashScreen() → 销毁SurfaceControl,触发动画结束

↓

AMS.notifyAppTransitionFinished() → 清理冷启动状态

六、关键代码路径与调试技巧

  1. 核心代码路径

模块 文件路径

AMS 启动流程 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

WMS 窗口管理 frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

启动窗口实现 frameworks/base/services/core/java/com/android/server/wm/SplashScreenWindow.java

应用端绘制完成通知 frameworks/base/core/java/android/view/ViewRootImpl.java

  1. 调试方法

查看窗口层级 :

adb shell dumpsys window windows | grep -i "splash"

强制关闭冷启动窗口 (测试用途):

adb shell am set-debug-app --wait <package_name>

日志跟踪 :

adb logcat -s "WindowManager" "ActivityManager"

七、Android 12+ 的 Splash Screen API 变化

从 Android 12 开始,冷启动窗口由 SplashScreen 类统一管理:

java 复制代码
// 应用端代码(Java/Kotlin)

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        // 1. 安装 Splash Screen

        installSplashScreen()

        super.onCreate(savedInstanceState)

        // 2. 设置主界面内容

        setContentView(R.layout.activity_main)

    }

}

新特性:

动画控制 :支持自定义启动动画(如图标旋转)。

延迟关闭 :通过 setKeepOnScreenCondition() 控制窗口关闭时机。

资源隔离 :启动窗口与主界面使用不同的 Theme,避免资源冲突。

八、总结

|----|------------------------|-------------------------------------|
| 阶段 | 核心组件 | 关键操作 |
| 创建 | ActivityManagerService | 请求启动应用进程,触发 WMS 创建冷启动窗口 |
| 创建 | WindowManagerService | 加载应用主题样式,创建 SurfaceControl 并添加到窗口层级 |
| 销毁 | ActivityThread | 完成界面绘制后调用 reportDrawn() 通知 WMS |
| 销毁 | WindowManagerService | 移除冷启动窗口,触发动画结束,释放 Surface 资源 |
| 销毁 | ActivityManagerService | 清理冷启动状态,标记应用为"已就绪" |

相关推荐
Starry-sky(jing)6 小时前
Vidda ADB 开启:R Pro 65 找不到开发者选项的处理与连接验收
android·adb·远程控制·电视·vidda
雨白7 小时前
从原理到实践:使用 Kotlin KSP 仿写 ButterKnifeLite
android·kotlin
疯华整猫8 小时前
把二十七个 init 从首帧前面搬走:用 Trae 把启动优化钉进 CI
android·性能优化·ai编程
传奇开心果8 小时前
【探秘 Jetpack Compose 框架深水区】 第8课:rememberSaveable 的边界在哪里?——Bundle、Saver 与什么值得跨进程存活
android·kotlin
Frankie_stand10 小时前
测试自动化实战指南:从脚本化、自动判据到 CI 集成的完整落地路径
android·ci/cd·自动化
恋猫de小郭10 小时前
Flutter 大小核优化又被回滚,然后又开新提案?
android·前端·flutter
Kapaseker11 小时前
领导问我 Kotlin 的 Nothing 是什么?
android·kotlin
韩曙亮11 小时前
【错误记录】安卓 adb 调试 5037 端口占用排查 ( adb 的三层结构 | 找出本机占用 5037 的进程 )
android·数据库·adb·安卓·错误记录
三少爷的鞋12 小时前
不改变异步本质,只改变代码组织:Telegram 项目的 Kotlin 协程实践
android
好的佩奇21 小时前
蓝牙之广播与扫描
android·flutter·蓝牙