为什么 Android App 启动会白一下?——一篇讲透 Android SplashScreen 启动机制演进
一、前言:一次面试,把我问懵了
前段时间面试时,面试官突然问了我一个问题:
“Android App 为什么启动时会白一下?”紧接着又问:
“为什么 Android12 后官方不推荐 SplashActivity?”一开始我以为只是:
windowBackground 没配好的问题。
但后来深入研究后才发现:
这背后其实涉及:
Android 启动机制 + Window Preview + Starting Window + Android12 SplashScreen + Framework 启动链路演进这一整套东西。
这一篇文章,我们就彻底把:Android Splash 启动机制讲透。
二、老 Android 的启动方式(Android11 及以下)
以前绝大部分 Android 项目:
都是这样写启动页的:
Launcher ↓ SplashActivity ↓ MainActivity经典代码:
Handler(Looper.getMainLooper()).postDelayed({ startActivity( Intent(this, MainActivity::class.java) ) finish() }, 2000)也就是:
启动页停留2秒 再跳首页那个年代:
SplashActivity ≈ 启动页本身三、为什么以前感觉“没有系统 Splash”?
很多老 Android 开发都会有一个感觉:
以前明明没有系统 Splash 为什么现在突然多了一层?其实:
以前并不是完全没有。
只是以前的机制是:
Window Preview
系统启动 Activity 时:
- 实际上会先创建一个:Starting Window
- 然后显示:android:windowBackground
所以以前真实流程其实是:
Launcher ↓ 创建Window ↓ 显示windowBackground ↓ SplashActivity绘制 ↓ 真正页面显示但因为:
windowBackground通常会被配置成:
- 启动背景图
- 品牌色
- Logo背景
于是:
Window Preview 和 SplashActivity 视觉一致所以肉眼感觉:
“一打开就是 Splash”四、白屏到底是怎么产生的?
现在终于进入核心问题。
很多人以为:
白屏 = 页面加载慢其实不完全对。
白屏真正发生的位置:
Activity 真正绘制之前系统流程:
点击App ↓ AMS/ATMS启动Activity ↓ 创建Window ↓ 显示windowBackground ↓ Activity开始绘制 ↓ setContentView / Compose ↓ 真正页面显示所以:
白屏本质: 是 Starting Window 阶段的背景色问题如果:
android:windowBackground没有配置。
系统默认:通常就是白色。
于是:
用户看到: 白一下五、Android12(API31)到底改变了什么?
真正的变化 发生在:
Android12(API31)Google 正式引入:
SplashScreen API从此:
系统开始强制接管启动第一页启动流程变成:
Launcher ↓ System SplashScreen ↓ 你的Activity注意:
这里已经不是以前简单的:
windowBackground Preview而是:
真正的系统 SplashScreen也就是说:
Android12+ 系统自己也有启动页了六、为什么 Android12 后容易出现“双 Splash”?
很多老项目升级后:
都会出现:
系统 Splash ↓ 自己写的 SplashActivity ↓ MainActivity于是:
- 白一下
- 双启动页
- Logo不一致
- 页面闪烁
问题全来了。
这也是为什么:
Google 官方开始不推荐:
SplashActivity + 延迟跳转这种老方案。
七、Theme.SplashScreen 是什么?
Android12 后 Google 提供了:
Theme.SplashScreen例如:
<style name="Theme.App.Starting" parent="Theme.SplashScreen"> <!-- 背景 --> <item name="windowSplashScreenBackground"> @color/splash_bg </item> <!-- 中间Logo --> <item name="windowSplashScreenAnimatedIcon"> @drawable/ic_splash_logo </item> <!-- 启动后切换主题 --> <item name="postSplashScreenTheme"> @style/Theme.App </item> </style>八、系统 Splash 能做到什么程度?
很多人以为:
系统 Splash 可以完全自定义其实并不行。
系统 Splash 本质是:
Framework 半封闭模板只能改:
| 能力 | 是否支持 |
|---|---|
| 背景色 | ✅ |
| 中间Logo | ✅ |
| 图标背景色 | ✅ |
| 退出动画 | ✅ |
| 倒计时 | ❌ |
| 跳过按钮 | ❌ |
| XML布局 | ❌ |
| 复杂背景 | ❌ |
| 多控件排版 | ❌ |
所以:
系统 Splash: 只能做“启动壳子”真正复杂的企业启动页:
还是得:
自己写 Activity九、企业项目应该怎么设计启动页?
这是现代 Android 企业项目最推荐方案。
正确方案
系统 Splash ↓ 企业 SplashActivity ↓ MainActivity系统 Splash
负责:
- 防白屏
- 冷启动过渡
- Android12 兼容
- 系统统一体验
通常只做:
纯色背景 + 品牌Logo企业 Splash
负责:
- 倒计时
- 广告
- 企业品牌
- 动画
- 登录检查
- 初始化逻辑
这才是现代 Android 项目的正确职责划分。
十、完整代码示例
10.1 Compose 项目推荐写法
Compose 时代:
推荐:
系统 Splash ↓ MainActivity ↓ Compose 企业 Splash ↓ HomeScreen依赖
implementation "androidx.core:core-splashscreen:1.0.1"themes.xml
<style name="Theme.App.Starting" parent="Theme.SplashScreen"> <item name="windowSplashScreenBackground"> @color/splash_bg </item> <item name="windowSplashScreenAnimatedIcon"> @drawable/ic_splash_logo </item> <item name="postSplashScreenTheme"> @style/Theme.App </item> </style>MainActivity
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() super.onCreate(savedInstanceState) setContent { var showSplash by remember { mutableStateOf(true) } if (showSplash) { EnterpriseSplashScreen( onFinish = { showSplash = false } ) } else { HomeScreen() } } } }10.2 传统 XML 项目推荐写法
传统项目 推荐:
系统 Splash ↓ SplashActivity ↓ MainActivitySplashActivity
class SplashActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() super.onCreate(savedInstanceState) setContentView(R.layout.activity_splash) object : CountDownTimer(2000, 1000) { override fun onTick(millisUntilFinished: Long) { } override fun onFinish() { startActivity( Intent( this@SplashActivity, MainActivity::class.java ) ) finish() } }.start() } }十一、低版本设备有没有系统 Splash?
这是很多人容易误解的地方。
Android11 及以下
实际上:
没有真正系统 SplashScreen只有:
windowBackground PreviewAndroid12+
才真正有:
Framework SplashScreen十二、core-splashscreen 到底是什么?
很多人以为:
implementation("androidx.core:core-splashscreen:1.0.1")会让所有系统都拥有:
系统 Splash其实并不是。它只是:
AndroidX 兼容层作用是:
统一 APIAndroid12+
调用:
真正系统 SplashAndroid11-
只是:
兼容模拟并不会:
凭空出现系统 Splash 页面十三、能彻底绕过系统 Splash 吗?
结论:
Android12+ App层基本绕不过因为:
系统 Splash 已经属于 Framework 启动链路如果真想彻底去掉:
只能改 AOSP / Framework已经属于:
ROM定制 Framework开发范畴。
十四、面试怎么回答“白屏怎么处理”?
真正高级的回答:
Android 冷启动时, 在 Activity 真正绘制前, 系统会先显示 Starting Window。 Android11 及以下, 主要是 windowBackground Preview; Android12+, Google 引入统一 SplashScreen 机制。 因此现代 Android 推荐: Theme.SplashScreen + installSplashScreen() + 业务 Splash 解耦。 系统 Splash 负责防白屏, 业务 Splash 负责广告、倒计时、品牌展示。这才是:
工程理解型回答而不是:
“改个 windowBackground”这么简单。
十五、总结
以前:
Splash 是业务页面现在:
Splash 已经变成系统能力这其实是:
Android Framework 启动机制的一次演进以前:
我们是在 Activity 里做启动页现在:
Android 已经把启动页变成了系统级能力