SplashScreen实现原理-复杂版本

阅读: StartingWindow相关文章

详细剖析 Android 12+ 的 SplashScreen 实现原理,包括:

1. 系统如何插入 SplashScreenView

2. App 的 installSplashScreen() 是如何控制它的保留/移除

3. 系统如何等待应用绘制完毕并移除 Splash

4. 涉及的关键类、调用栈、跨进程通信

一、整体执行流程图(系统 + App)

                Launcher 启动应用
                       ↓
     ┌────────────────────────────────────┐
     │  ActivityTaskManagerService       │
     │  ↳ startActivity()                │
     │  ↳ 创建 Task & ActivityRecord      │
     └────────────────────────────────────┘
                       ↓
     ┌────────────────────────────────────┐
     │  WindowManagerService              │
     │  ↳ createStartingWindow()          │
     │      ↳ SplashScreenController      │
     │          ↳ addSplashScreenView()   │
     │              ↳ 创建 SplashScreenView|
     │              ↳ 添加到窗口最顶层     │
     └────────────────────────────────────┘
                       ↓
     应用进程 attach -> ActivityThread.handleLaunchActivity()
                       ↓
     MainActivity.onCreate() 中 installSplashScreen()
                       ↓
     ViewRootImpl.performTraversals() 触发首次绘制
     ↳ mReportNextDraw = true
     ↳ finishDrawing() 通知系统“我画完了”
                       ↓
     系统收到绘制完成 → 移除 SplashScreenView → 展示主窗口内容

二、系统插入 SplashScreenView 的过程

1. 启动阶段

在启动 Activity 的过程中:

ActivityTaskManagerService
 ↳ startActivity()
   ↳ ActivityStackSupervisor.realStartActivityLocked()
     ↳ ActivityRecord.createStartingWindow()

此处会触发:

// frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
boolean createStartingWindow(...) {
    ...
    mWmService.mStartingSurfaceController.addStartingWindow(this);
}

2. Splash 控制器

调用:

SplashScreenController.addSplashScreenStartingWindow()

最终会创建:

SplashScreenView splashView = new SplashScreenView(...);

并将其添加为:

WindowState.addChild(splashView);

这个 SplashScreenView 是一个系统控件,其布局由以下组成:

• 背景色(从 app 主题 android:windowSplashScreenBackground 读取)

• 中心图标(从 windowSplashScreenAnimatedIcon 读取)

• 可选动画(动画时间由 windowSplashScreenAnimationDuration 控制)

它插入在 应用窗口的上层,但还是由系统控制。

三、App installSplashScreen() 做了什么?

val splash = installSplashScreen()

来自 androidx.core:splashscreen 库,其本质作用是:

1. 获取系统注入的 SplashScreenView 的控制器(通过 ID 绑定)

SplashScreenViewProvider splashProvider = ...;

2. 设置过渡动画(进入主界面时淡出等)

splash.setOnExitAnimationListener { viewProvider ->
    viewProvider.view.animate().alpha(0f).withEndAction {
        viewProvider.remove()
    }
}

3. 设置保留条件(阻止自动移除)

splash.setKeepOnScreenCondition {
    viewModel.isLoading
}

该条件由 Jetpack 实现轮询,每帧检查 return 值是否为 false。一旦为 false,则进入过渡逻辑,Splash 被系统移除。

四、系统如何判断“可以移除 SplashScreen”?

条件一:App 完成第一次绘制

在 ViewRootImpl 中,绘制流程:

// frameworks/base/core/java/android/view/ViewRootImpl.java
private void performDraw() {
    ...
    // 绘制结束后
    if (mReportNextDraw) {
        mReportNextDraw = false;
        mWindowSession.finishDrawing(mWindow);
    }
}

这行代码意味着:

应用已经完成首次绘制,通知 WMS。

条件二:保留条件变为 false

若 SplashScreenView 仍在,但 setKeepOnScreenCondition {} 返回 false,则系统通过 SplashScreenExitAnimation 移除它。

五、系统如何移除 SplashScreen?

SplashScreenExitAnimation
 ↳ playAnimation()
 ↳ mSplashScreenViewProvider.remove() // 从窗口 detach 掉 view

最终移除 SplashScreenView,暴露真实界面。

六、关键源码位置(AOSP)

类名 作用 路径
SplashScreenController.java 管理 splash 的创建/移除 frameworks/base/services/core/java/com/android/server/wm/
SplashScreenView.java 实际显示的 splash View frameworks/base/core/java/com/android/internal/policy/
ViewRootImpl.java 应用绘制流程,触发 finishDrawing() frameworks/base/core/java/android/view/
installSplashScreen() Jetpack 封装 API androidx.core:splashscreen
mReportNextDraw 强制报告一次绘制 ViewRootImpl 中字段

总结

阶段 行为
系统启动应用 自动插入 SplashScreenView
App installSplashScreen() 设置过渡动画和延迟条件
App 首帧绘制完成 调用 finishDrawing 通知系统
延迟条件为 false 系统播放动画并移除 splash
主内容露出 启动流程结束,用户可交互

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器