24、SplashScreen:SplashScreen API、启动画面定制、兼容性处理

各位同学,今天我们来聊聊 SplashScreen。说实话,这个组件在 Android 12 之前一直是个「野路子」——各家有各家的实现方式,有的用 Activity 主题,有的用专门的 SplashActivity,还有的直接在 Application 里搞个闪屏。乱象丛生,对吧?

直到 Android 12 推出了官方的 SplashScreen API,Google 终于把这件事给「收编」了。我个人觉得,这是 Jetpack 里最容易被忽视但实际价值很高的一个组件。为什么?因为它直接关系到用户对 App 的第一印象。

好,我们直接进入正题。

一、SplashScreen API 是什么?

说白了,SplashScreen API 就是 Android 系统提供的一套标准化的启动画面机制。它解决了两个核心问题:

  • 冷启动和温启动时的白屏/黑屏问题——这个我猜你肯定遇到过
  • 启动画面的统一性和可定制性——让 App 的启动体验更流畅

在 Android 12 之前,App 启动时系统会先显示一个「预览窗口」(Preview Window),这个窗口的颜色取决于你的主题。如果你没处理好,用户就会看到一闪而过的白屏或黑屏。嗯,这体验真的很糟糕。

而 SplashScreen API 接管了这个过程,它让你可以在系统层面就展示一个带图标的启动画面,而不是等到 Activity 加载完成后再显示。说白了,就是让启动过程「无缝」了。

核心要点:SplashScreen API 在 Android 12+ 上是系统级行为,不需要你手动创建 Activity 或 Fragment。你只需要配置主题和图标即可。

二、启动画面定制

好,我们来看看怎么用。先上代码,再解释。

2.1 基础配置

首先,在你的主题中设置 SplashScreen 相关的属性:

<!-- res/values/themes.xml -->
<style name="Theme.MyApp" parent="Theme.SplashScreen">
    <!-- 设置启动画面背景色 -->
    <item name="windowSplashScreenBackground">@color/white</item>
    <!-- 设置启动画面图标 -->
    <item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
    <!-- 设置图标动画时长 -->
    <item name="windowSplashScreenAnimationDuration">500</item>
    <!-- 设置启动后显示的 Activity 主题 -->
    <item name="postSplashScreenTheme">@style/Theme.MyApp.Main</item>
</style>

这里要注意一点:windowSplashScreenAnimatedIcon 支持 AnimatedVectorDrawable,也就是说你可以做一个简单的动画图标。我在项目中就做过一个 Logo 从中心放大再淡出的效果,用户反馈说「感觉 App 启动很快」。其实不是快了,是视觉上平滑了。

2.2 控制启动画面消失时机

默认情况下,启动画面会在 Activity 绘制第一帧后自动消失。但有时候我们需要等一些初始化工作完成后再关闭,比如加载配置、检查登录状态等。

这时候可以用 SplashScreensetOnExitAnimationListener

// 在 Activity 的 onCreate 中
val splashScreen = installSplashScreen()

splashScreen.setOnExitAnimationListener { splashScreenView ->
    // 自定义退出动画
    val anim = ObjectAnimator.ofFloat(
        splashScreenView.iconView,
        View.ALPHA,
        1f, 0f
    )
    anim.duration = 300
    anim.addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            splashScreenView.remove()
        }
    })
    anim.start()
}

我个人习惯用这种方式来做「条件性延迟关闭」。比如 App 启动时需要检查是否有新版本更新,如果有就弹窗提示,没有就直接进入主页。这时候你可以让启动画面多停留一会儿,等检查结果出来再决定下一步。

注意:不要滥用延迟关闭。启动画面停留超过 2 秒,用户就会觉得「卡」。我曾经在一个项目里把启动画面延迟了 3 秒等网络请求,结果被用户骂惨了。后来改成了「先显示主页骨架屏,再异步加载数据」的方案。

2.3 自定义启动画面布局

如果你觉得系统提供的图标+背景太单调,想加个品牌标语或者进度条,可以用 setOnExitAnimationListener 配合自定义 View 来实现。但说实话,我不建议这么做。因为 SplashScreen API 的设计初衷就是「轻量、快速」,加太多东西反而会拖慢启动速度。

如果你非要加,可以这样:

splashScreen.setOnExitAnimationListener { view ->
    // 在系统启动画面上叠加自定义布局
    val customView = layoutInflater.inflate(R.layout.custom_splash, null)
    view.addView(customView)
    
    // 等自定义动画结束后再移除
    // ...
}

嗯,这里要提醒一句:自定义布局会增加启动耗时,请谨慎使用。

三、兼容性处理

这是最让人头疼的部分。SplashScreen API 是 Android 12 才引入的,那低版本怎么办?别急,Jetpack 已经帮我们想好了。

3.1 使用 SplashScreen 兼容库

build.gradle 中添加依赖:

dependencies {
    implementation 'androidx.core:core-splashscreen:1.0.1'
}

这个库会向下兼容到 Android 6.0(API 23)。在低版本设备上,它会模拟出类似 Android 12 的启动画面效果。说白了,就是帮你把「白屏」给「美化」了。

3.2 主题配置的兼容写法

因为低版本没有 windowSplashScreenBackground 这些属性,所以我们需要用 values-v31 和 values 两套主题:

<!-- res/values/themes.xml (低版本) -->
<style name="Theme.MyApp" parent="Theme.AppCompat.DayNight.NoActionBar">
    <!-- 低版本不需要 SplashScreen 属性,但可以设置一个启动背景 -->
    <item name="android:windowBackground">@color/splash_background</item>
</style>

<!-- res/values-v31/themes.xml (Android 12+) -->
<style name="Theme.MyApp" parent="Theme.SplashScreen">
    <item name="windowSplashScreenBackground">@color/white</item>
    <item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
    <item name="postSplashScreenTheme">@style/Theme.MyApp.Main</item>
</style>

这样写的好处是:高版本用系统 API,低版本用兼容库模拟。用户感知不到区别。

3.3 常见兼容问题

问题 原因 解决方案
低版本启动画面闪烁 兼容库模拟时与 Activity 主题冲突 确保 android:windowBackground 与 SplashScreen 背景色一致
图标显示位置偏移 不同厂商 ROM 对启动画面有修改 使用 windowSplashScreenIconBackgroundColor 设置图标背景
启动画面无法消失 Activity 的 setContentView 调用时机不对 确保 installSplashScreen()super.onCreate() 之前调用

我曾经在小米手机上遇到过一个问题:启动画面图标偏左,看起来特别别扭。后来发现是 MIUI 对启动画面做了定制,加了一个 windowSplashScreenIconBackgroundColor 属性才解决。嗯,厂商 ROM 的坑,你永远猜不到下一个在哪。

四、知识体系总览

下面这张图总结了 SplashScreen 的核心知识点和它们之间的关系:

SplashScreen API 系统级启动画面 冷启动/温启动无缝过渡 预览窗口 → 启动画面 → Activity 定制化配置 主题属性配置图标/背景/动画 setOnExitAnimationListener 控制消失 自定义退出动画 兼容性处理 core-splashscreen 兼容库 values-v31 主题分离 厂商 ROM 适配 核心目标:消除白屏,提升启动体验

五、避坑指南

最后,我把自己踩过的坑总结一下,希望能帮你少走弯路:

  • installSplashScreen() 必须在 super.onCreate() 之前调用——这是最容易被忽略的。如果你放在后面,启动画面就不会生效。
  • 不要用 SplashScreen 做耗时操作——它只是一个「视觉过渡」,不是「加载页面」。如果你需要加载数据,请用骨架屏或 Loading 状态。
  • 图标建议用 Adaptive Icon 格式——这样在不同屏幕尺寸和厂商 ROM 上都能正确显示。
  • 测试时记得覆盖冷启动和温启动——冷启动是进程被杀后启动,温启动是进程还在但 Activity 被回收。两者的表现可能不同。

小技巧:如果你想让启动画面看起来更「快」,可以把 windowSplashScreenAnimationDuration 设置得短一些,比如 300ms。用户会觉得「嗖的一下就进去了」。

好了,关于 SplashScreen 的内容就讲到这里。记住一句话:启动画面不是功能,是体验。做得好,用户觉得流畅;做不好,用户觉得卡顿。就这么简单。


公众号:蓝海资料掘金营,微信deep3321