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 |
主内容露出 | 启动流程结束,用户可交互 |