深度探讨 Jetpack SplashScreen 如何重塑应用启动画面

Posted TechMerger

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度探讨 Jetpack SplashScreen 如何重塑应用启动画面相关的知识,希望对你有一定的参考价值。

可能有不少小伙伴已经留意到 android 12 上推出了全新的启动画面 API SplashScreen。同时为了兼容低版本的使用,Jetpack 框架里推出了同名库。

本次针对这个库的使用和原理做个全面的介绍,将按照如下几个方面去展开:

  • 首先简单探讨一下为什么需要启动画面
  • 接着一起回顾一下之前打造启动画面的常规做法
  • 然后重点阐述一下 SplashScreen 库的目的,以及如何使用
  • 最后介绍一下 SplashScreen 库大致的实现原理

1. 为什么需要启动画面

1.1 启动画面的作用

  • 当我们打开各种桌面软件或移动端 App 的时候,首先会看到的经常是一段过渡画面。用以展示产品所属的公司、品牌的图标、公司的 Slogan 等,借以宣传公司的调性和传达一些特色

  • 同时在节日庆典、特殊活动的时候展示广告页面或活动海报

  • 在启动画面展示的同时,背后的内容得以加载

同时在一定程度上缓解用户的等待,启动画面良好设计的话,还能使得这个枯燥的过程显得有趣和充满期待!

1.2 App 启动的典型流程

来看一下 App 启动的典型流程。

点击 Launcher 上的 Logo 之后,首先用户将看到一个启动画面,如果是初次启动的话接着会展示 Guide 画面引导用户了解如何使用;如果是节日或广告需要的话展示的是广告宣传画面,最后才是展示内容的主画面。

我们本次探讨的核心就是第一个启动画面。

Jetpack SplashScreen 库正是用来打造用户看到的第一个启动画面,在讲述之前,先来回顾一下之前的常规做法。

2. 常规做法

我们知道从点击 Launcher 上的 icon 到 App 内容描画之前,有很多准备工作。在这段时间是看不到目标 App 内容的。

为了快速响应用户的点击或缓解用户的等待,在 App 描画之前系统将启动专用的 SplashScreenWindow 盖在 App 之上。该Window 的呈现源自于 App 主题方面的配置。

配置的不同进而影响到启动 Window 的表现,我们来看看各种配置的做法。

2.1 默认的启动画面背景

假使 App 的主题针对 Window 背景什么都不设置,你会发现启动的过程中总有个默认的白画面一闪而过。

 <style name="SplashThemeBase.DefaultBg"/>

2.2 不设置启动画面背景

当然可以将 windowBackground 设置为空,可是你会发现启动的过程中又变成黑色一片闪过。

<style name="SplashThemeBase.NoBg">
    <item name="android:windowBackground">@null</item>
</style>

无论是白画面还是黑画面一闪而过,都无法接受,所以还得继续优化。

2.3 直接关闭启动画面

无论白画面还是黑画面一闪而过的体验都不好,这时候可能会想到关闭默认的启动画面。通过 windowDisablePreview 属性可以彻底关闭启动画面。

<style name="SplashThemeBase.TransparentBg">
    <item name="android:windowDisablePreview">true</item>
</style>

这样一来,确实看不到启动画面的存在了,但整个过程貌似“变慢”了。实际上性能并没有**“劣化”**,只是启动中过渡的 Window 不存在了。假使 ApplicationActivity 等组件里存在耗时逻辑的话,这种劣化会更加明显。

相较于前面的黑白画面一闪而过,这种变慢的体验也好不到哪去。

2.4 设置启动画面背景

绕来绕去,最后我们发现还是得提供一个恰当的启动画面。具体在于将 UI 提供的背景色、Icon、Brand 等资源组合成一个 LayerListDrawable,然后将其设置到 windowBackground 中即可。

<!--theme-->
<style name="SplashThemeBase.WithBg">
    <item name="android:windowBackground">@drawable/ic_splash_bg</item>
</style>

<!--drawable-->
<layer-list ... >
    <item android:drawable="@drawable/ic_logo"></item>
    <item  android:drawable="@drawable/ic_brand"></item>
</layer-list>

这种做法既可以提供体验良好的过渡画面,同时可以快速响应用户的点击。可以说是最为简单也最为便捷的一种实现方式。

2.5 设置启动画面内容

Android 8 加入一个配置启动画面的属性 windowSplashscreenContent,用于配置启动画面的内容。它的优先级高于 windowBackground。两者一起设置的话呢,会覆盖在 windowBackground 上。大体的效果呢和前面的差不多,不再赘述。

<style name="SplashThemeBase.WithBg.SplashScreenContent">
    <item name="android:windowSplashscreenContent">@drawable/ic_splash_content</item>
</style>

注意:这个属性自 Android 8 加入,从 12 开始废弃

2.6 定义启动专用 Activity

有的时候觉得单单设置一张背景图太过简单,无法实现一些复杂的启动效果。又或者希望能和广告或节日的画面深度配合起来。这时候难免需要定义一个专门的启动 Activity

具体来讲需要在入口 Activity 前预设专门的 Splash Activity来显示启动画面,Splash Activity 在一定时间后自动跳转到入口 Activity

启动 Activity 提供类似启动画面的布局。

<androidx.constraintlayout.widget.ConstraintLayout ... >
    <ImageView android:src="@drawable/ic_icon" ...  />
    <ImageView android:src="@drawable/ic_brand" ...  />
</androidx.constraintlayout.widget.ConstraintLayout>

在画面展示后配合定时跳转等逻辑去打开广告或海报等画面。

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        setContentView(binding.root)
        goToMainScreenDelayed()
    }
}

因为有了自己实现的启动 Activity,所以系统提供的启动画面可以关闭。当然也可以将系统的启动画面的 windowBackground 设置为整体背景色相接近的色调。

<style name="SplashThemeBase.NoSplashWindow" >
    <item name="android:windowDisablePreview">true</item>
</style>

上面是通过 Activity 方式打造的启动画面的效果。

DEMO 的效果和前面的主题方式的效果差不多,但是这种做法将会拥有更大的定制空间。比如可以接入跨平台的 Lottie 动画库,打造更为丰富、炫酷、平台统一的启动效果!

3. Jetpack SplashScreen 的目的和使用

可以看到之前提供的主题配置方式只能展示一张背景图,这种静态效果看久了着实单调。而且这张图还需要我们自行组合 App 的 Icon、Brand 的 Logo,这就需要考虑很多 Size、位置、屏幕适配等 UI 细节,不太方便。如果需要丰富、灵活一些的效果,又需要自定义专用的 Activity 来解决,这样一来又显得繁琐。

正因为此,Jetpack 上推出了 SplashScreen 库,来高效、灵活地重塑启动画面。而 SplashScreen 库基于 Android 12 的新特性 SplashScreen API,所以我们先来了解一下这个特性。

3.1 Android 12 SplashScreen

3.1.1 特性介绍

Android 12 上 App 启动的时候将先展示一个启动画面的进场部分,该部分的呈现由 SplashScreen API 提供的更为丰富的属性来定制。之后在App 描画第一帧的时候将该画面的视图回调给我们来定制退场部分的效果。

进场和退场两部分共同组成了全新的 SplashScreen API。

需要说明的是,不同于之前的版本,12 上什么即便都不配置,启动画面会是白色背景下展示 Adaptive Icon 的一个效果。这相较于之前的纯白背景要好了不少。

3.1.2 画面构成

SplashScreen 的画面由一个 SplashScreenWindow 承载,中间是一个 AdaptiveIcon,SplashScreen API 则提供了丰富的属性来指定该的各部分构成,简洁明了。比如:

  • windowSplashScreenAnimatedIcon:指定 App Icon 的图片,如果指定的是 Animated Vector Drawable 的话,在启动画面展示的同时会执行一个 Icon 动画效果
  • windowSplashScreenIconBackgroundColor:Icon 背景色
  • Adaptive Icon Mask:由 Launcher 的配置指定,主题无法更改,说明一下
  • windowSplashScreenBackground:Window 背景色,和之前的 windowBackground 属性类似
  • windowSplashScreenBrandingImage:放置品牌 Logo

当 SplashScreenWindow 退场的时候整个画面的视图会被封装到 SplashScreenView 中供我们实现退场效果。

下面这是 Gmail 适配了 SplashScreen API 后的启动效果。简单来讲就是一个进场的 M 字母的 Path动画,退场的时候直接结束到目标内容的效果。

并没有适配 Icon 背景、Brand Logo、退场动画等特性,后面的章节将充分演示各项特性。

3.1.3 启动规则

在冷启动和暖启动的时候展示 SplashScreen,热启动的时候从不展示该画面。

  • 冷启动:进程尚不存在的情况下点击 App Icon 的启动
  • 暖启动:进程存在但 App 不在前台,并且 Activity 已经销毁了的启动
  • 热启动:进程存在但 App 不在前台,并且 Activity 尚未销毁的启动

3.2 Jetpack SplashScreen

3.2.1 目的

Android 12 的 SplashScreen API 非常好用,但是低版本无法尝鲜,所以 Jetpack 里推出了同名库。

它是为了兼容 12 之前低版本的 UI 库,最早兼容到 Android 6(API 23),6.0 及以上的设备占用率近 9 成,完全够用了。它通过整合和统一 SplashScreen API,达到一套代码实现近乎一致的 App 启动体验。

简述:

  • 兼容低版本系统的启动画面 UI 库
  • 支持到 Android 6.0(近 9 成)
  • 整合 12 的 SplashScreen API
  • 一套代码打造近乎一致的启动效果

3.2.2 提供的主题和属性

SplashScreen 库的版本到了 1.0.0-alpha01 版,在 gradle 文件里简单导入即可。

dependencies {
    implementation "androidx.core:core-splashscreen:1.0.0-alpha01"
}

它预设了主题供我们使用,必须将入口 Activity 的主题改成扩展自这个主题。

<style name="Theme.SplashScreen" parent="Theme.SplashScreenBase">
    <item name="windowSplashScreenAnimatedIcon">@android:drawable/sym_def_app_icon</item>
    ...
</style>

同时预设了些属性供我们覆写和提供自己的启动画面资源。

<attr format="reference" name="postSplashScreenTheme"/>
<attr format="reference" name="windowSplashScreenAnimatedIcon"/>
<attr format="integer" name="windowSplashScreenAnimationDuration"/>
<attr format="color" name="windowSplashScreenBackground"/>

这里需要提醒两点:

  1. SplashScreen 库针对 12 之前的低版本暂不支持设置 Icon Bg,和 Brand Icon 的属性
  2. postSplashScreenTheme 属性用来指定 Activity 最终的主题,避免使用预设的主题影响目标 Actvitiy 的主题展示

3.2.3 提供的 API

SplashScreen 库还提供了逻辑方面的 API,供我们去实现启动画面时长的控制,启动画面退场效果的实现需求。

API说明
SplashScreenJetpack版获取定制启动画面入口的类
Activity#installSplashScreen()覆写Activity,用以获取定制入口的静态成员函数
setKeepVisibleCondition指定保持启动画面展示的条件
KeepOnScreenCondition实现展示条件的接口
setOnExitAnimationListener监听启动画面的退出时机
OnExitAnimationListener启动画面退出的回调接口
SplashScreenViewProvider定制退场效果的启动画面视图

3.3 SplashScreen 库的使用

3.3.1 打造进场效果

进场效果的部分只涉及到资源方面的配置。

首先扩展自 SplashScreen 库的预设主题作成一个 Base 主题,指定诸如启动画面背景、目标 Activity 背景等方面的共同属性。

<style name="SplashScreenTheme.Base" parent="Theme.SplashScreen">
    ...
    <item name="windowSplashScreenBackground">@color/splashBackground</item>
    <item name="postSplashScreenTheme">@style/TargetScreenTheme</item>
</style>

其他的一些属性需要针对低版本和 12 作区分。比如 12上可以指定其独有的动画 Icon,Icon 背景和 Brand logo,而低版本上指定一个静态 Icon 即可。但为了效果接近,可以指定 App 的 Adaptive Icon。

<!-- version:12- -->
<style name="SplashScreenTheme" parent="SplashScreenTheme.Base">
    <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_icon_adaptive</item>
</style>

<!-- version:12+ -->
<style name="SplashScreenTheme" parent="SplashScreenTheme.Base">
    <item name="windowSplashScreenAnimatedIcon">@drawable/ic_icon_animated</item>
    <item name="android:windowSplashScreenIconBackgroundColor">@color/iconBackground</item>
    <item name="android:windowSplashScreenBrandingImage">@drawable/ic_brand</item>
</style>

我们来看一下分别运行在 Android 8 和 12 上的进场效果:

可以看到高低版本上是比较接近的进场画面,只不过 12 上多了特有的 Kotlin 的组合动画和一个 TechMerger 字样的 Brand Logo。

3.3.2 延长启动画面

随着 App 第一帧的开始描画,SplashScreenWindow 即将消失。如果背面的业务逻辑尚未准备完毕,那体验不是很好,鱼骨屏什么的就是用来优化这个问题。

当然对于启动画面来讲,现在可以通过 SplashScreen 库的 API 来灵活控制启动画面的时长,确保内容好了再退出。这个 API 就是 installSplashScreen()

通过这个静态函数可以拿到定制的入口,之后可以调用 setKeepVisibleCondition() 设置启动画面保持展示的条件,条件可以 ViewModel 的耗时加载相结合。这里提供的是一个 ViewModel 实例初始化 2s 之后再退出启动画面的一个模拟逻辑。

注意:由于 installSplashScreen 函数内部将调用 setTheme 反映实际的主题,所以需要在 setContentView 之前调用

class JetpackSplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        splashScreen = installSplashScreen()
        setContentView(binding.root)
        splashScreen.setKeepVisibleCondition {
            !viewModel.isDataReady() 
        }
    }
}

class MyViewModel(application: Application): AndroidViewModel(application) {
    companion object {
        const val WORK_DURATION = 2000L
    }
    private val initTime = SystemClock.uptimeMillis()
    fun isDataReady() = SystemClock.uptimeMillis() - initTime > WORK_DURATION
}

我们来看一下分别运行在 Android 8 和 12 上启动画面的延长效果:

可以看到高低版本上都成功实现了启动画面的延迟退出。

3.3.3 打造整体退场效果

当启动画面退出的时候如果能提供一个无缝过渡到目标内容的动画,体验会更好。我们可以利用 SplashScreen 库的 setOnExitAnimationListener 来针对进场画面的整体视图实现一个退场的动画。

比如这里我们定制一个 SplashScreen 整体的下移淡出效果。

注意:记得在动画结束的时候调用 SplashScreenViewProvider 的 remove() 及时将启动画面的视图移除,否则可能覆盖在实际画面上,遮挡内容。

class JetpackSplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        splashScreen.setOnExitAnimationListener { splashScreenViewProvider ->
            showSplashExitAnimator(splashScreenViewProvider.view) {
                splashScreenViewProvider.remove()
            }
        }

    private fun showSplashExitAnimator(splashScreenView: View, onExit: () -> Unit = {}) {
        ...
        AnimatorSet().run {
            ...
            playTogether(slideDown, alphaOut)
            doOnEnd { onExit() }
        }
    }
}

我们来看一下分别运行在 Android 8 和 12 上的退场效果:

可以看到 12 上的 Brand Logo 是一起执行的退场动画,总的来说高低版本上都实现了几乎一致的整体下移和淡出的效果。

3.3.4 打造 Icon 独有的退场效果

如果觉得整体的退场动画太过突兀或夸张,还可以针对 App Icon 作单独的退场效果。基本逻辑和定制整体的退场效果差不多。,区别在于执行动画的对象由 view 变成了 IconView

同样要注意在动画结束的时候调用 remove()

class JetpackSplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        splashScreen.setOnExitAnimationListener { splashScreenViewProvider ->
            showSplashIconExitAnimator(splashScreenViewProvider.iconView) {
                splashScreenViewProvider.remove()
            }
        }

    private fun showSplashIconExitAnimator(iconView: View, onExit: () -> Unit = {}) {
        ...
        AnimatorSet().run {
            ...
            playTogether(alphaOut, scaleOut, slideUp)
            doOnEnd { onExit() }
        }
    }
}

这里延时的是针对 Icon 的上移和淡出动画,来看一下效果:

可以看到 12 上的 Brand Logo 是不动的,整体上都是一个 Icon 上移和淡出的效果。

3.3.5 控制退场动画的时长

设备性能或状态会影响 App 开始描画的时间,为了让用户早点看到实际内容,可以灵活控制退场动画的时长。比如当描画得晚,可以考虑不展示退场动画或执行极短的固定时长;当描画得早,进场动画可能尚未结束,将剩余的时长交接给退场部分。

主要通过 SplashScreen 库返回的进场动画开始时刻(iconAnimationStartMillis)和总时长(iconAnimationDurationMillis)的 API,与退场回调的当前时刻进行计算即可。

需要注意的是,针对 12 之前的版本,SplashScreen 库的进场部分不支持 Icon 动画,所以上述的两个属性总是返回 0,需要特别处理一下。

private fun getRemainingDuration(provider: SplashScreenViewProvider): Long {
    val animationDuration = provider.iconAnimationDurationMillis
    val animationStart = provider.iconAnimationStartMillis

    return if (animationDuration == 0L || animationStart == 0L)
        defaultExitDuration
    else (animationDuration - SystemClock.uptimeMillis() + animationStart)
        .coerceAtLeast(0L)
}

3.4 Lottie 支持 SplashScreen 吗?

Lottie 是跨平台的动画库,非常好用。那么 SplashScreen 库支持吗?

并不支持,因为 SplashScreen 库只能配置 Drawable 文件,不可以替换 View。而 Lottie 的效果完全依赖于自定义的 AnimationView

但 SplashScreen 的设计者提供了一个魔改思路:

  1. 拷贝 Lottie Json 的第一帧,做成 SVG 并转换为 Animated Vetor Drawable,设置到 Splash Icon
  2. 目标布局正中放入执行 Icon 动画的 View
  3. 在 Splash 退出的时候将 LottieAnimationView 解析 Json 并执行动画
  4. 动画结束后记得将真正的视图展示

代码示例:

splashScreen.setOnExitAnimationListener { vp ->
    val lottieView = findViewById<LottieAnimationView>(R.id.animationView)
    ...
    lottieView.postDelayed({
        vp.view.alpha = 0f
        vp.iconView.alpha = 0f
        lottieView!!.playAnimation()
    }, delay)

    lottieView.addAnimatorListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator?) {
            imageView.visibility = View.VISIBLE
        }
    })
}

我们来看一下高低版本上魔改之后支持 Lottie 的 SplashScreen 效果,可以看到是几乎完全一致的非常流畅、丝滑的启动体验。

但这种做法违背了 SplashScreen 库的设计初衷,不建议使用,这里只是提供一种思路。

更详细的说明可以参考官方的 DEMO 介绍:https://github.com/vcaen/splashscreen-sample

4. SplashScreen 库的实现原理

接下来了解一下 SplashScreen 库如何兼容低版本,实现几乎一致的启动效果。

4.1 总体原理

写这个资料的时候 Android 12 的源码尚未公开,最近公开了之后看了一眼,发现 SplashScreen 的实现非常繁杂。

这里简单提一下关键地方,SplashScreenWindow 退出的时候,系统会通过 AIDL 将封装了启动画面的信息的序列化对象传递给 App 进程。App 将对象反序列化并创建退场视图即 SplashScreenView,然后添加到 DecorView 上去。之后 App 即可对这个视图作退场效果的定制。

更多全面的细节,感兴趣的朋友可自行研究。本次主要Jetpack SplashScreen 库的源码进行解读。

总体的原理分为进场和退场两个部分。

进场部分的画面针对 12 之前的版本是 windowBackground 思路,针对 12 是系统专属的 SplashScreen 系属性实现的。

退场部分,在 12 之前是自定义的 FrameLayout 添加到 Activity 的 ContentView 上,12 则是反序列化的 SplashScreenView 添加到了 DecorView 上。

4.2 进场画面的原理

进场画面的原理完全依赖于主题的配置,面向低版本的话和之前的常规做法是一样的思路,即提供一个读取我们配置的画面资源的 LayerListDrawable 放置到 windowBackground 中。

<style name="Theme.SplashScreen" parent="Theme.SplashScreenBase">
    ...
</style>

<style name="Theme.SplashScreenBase" parent="android:Theme.NoTitleBar">
    <item name="android:windowBackground">@drawable/compat_splash_screen</item>
    ...
</style>
<!--compat_splash_screen.xml-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:gravity="fill">
        <color android:color="?attr/windowSplashScreenBackground" />
    </item>
    <item
        android:drawable="?attr/windowSplashScreenAnimatedIcon"
        ... />
</layer-list>

面向 12 的话,则是由系统回调专属的属性去构建启动画面的视图。

<style name="Theme.SplashScreen" parent="android:Theme.DeviceDefault.NoActionBar">
    <item name="android:windowSplashScreenAnimatedIcon">?windowSplashScreenAnimatedIcon</item>
    <item name="android:windowSplashScreenBackground">?windowSplashScreenBackground</item>
    ...
</style>

4.3 定制入口的初始化

获取 SplashScreen 实例的 API 是 installSplashScreen()。其运行在低版本上的话,需要额外读取和缓存 Icon 和 Background 的配置,然后读取并设置目标 Activity 的 Theme。12 上的话,Window 背景,Icon 和 Branding 等属性由系统控制,只需要配置目标 Activity 的 Theme 即可。

4.4 延长启动画面

通过 setKeepVisibleCondition() 可以延长启动画面的展示,无关运行的版本,原理都是向 ContentView 的 ViewTreeObserver 注册OnPreDrawListener回调来实现。

系统在描画前先回调 onPreDraw(),获取是否放行描画的条件,此处将回调 KeepVisibleCondition 的逻辑。如果不放行,在下次屏幕刷新的时候继续回调,直到满足条件开始描画,启动 Window 消失。描画放行的时候,低版本额外需要手动调用 dispatchOnExitAnimation 来执行退出回调,12 则由系统自行执行。

需要注意:这个时候退场用的自定义视图仍然还没添加上来,只是延迟了 SplashScreenWindow 的退出而已。

4.5 退场画面的回调

setOnExitAnimationListener() 可以监听退场时机。

运行在低版本上的时候,需要手动加载启动画面的布局到 ContentView中,并将之前设置的 Window Background 和 Icon 等属性显示。然后添加 Layout change 回调,在布局完毕的时候通过 adjustInsets 特殊处理将 Icon 位置调整一下。判断启动画面保持条件是否达到,达到的话调用 onSplashScreenExit

运行在 12 上的时候,布局不需要手动准备,通过 12 专用的系统接口,将视图缓存到 Provider 里即可,后续的 Exit 也由系统执行。

4.6 adjustInsets 的特殊处理

面向低版本的退场画面在布局成功后会调用的特殊处理。

进场部分的是 Window Drawable,Icon 是居中的。但退场部分是向 ContentView中手动添加的 Framelayout 布局,Icon 在布局里是居中的,但由于 StatusBarNavigationBar 高度不一样,Icon 在整个 Window 里是偏下的。

如果不加干预的话,进场过渡到退场的时候,Icon 会发生跳跃。

源码通过 windowInsets API 获取状态栏和导航栏的高度,取差值的一半交由 IconView 去移动到 window 中间。

private class Impl23(activity: Activity) : Impl(activity) {
    override fun adjustInsets( ... ) {
        // Offset the icon if the insets have changed
        val rootWindowInsets = view.rootWindowInsets
        val ty = rootWindowInsets.systemWindowInsetTop
                - rootWindowInsets.systemWindowInsetBottom
        splashScreenViewProvider.iconView.translationY = -ty.toFloat() / 2f
    }
}

结语

到这里我们探讨了启动画面的必要性、回顾了启动画面打造的常规做法、介绍了 Android 12 上 SplashScreen API、以及详细了解了 Jetpack SplashScreen 库的目的、使用细节和实现原理

可以看到 SplashScreen 库简单又清晰,可以帮助我们灵活、高效地重塑启动画面,主要体现在这么几

以上是关于深度探讨 Jetpack SplashScreen 如何重塑应用启动画面的主要内容,如果未能解决你的问题,请参考以下文章

Android12 Jetpack SplashScreen API总结

Jetpack 新成员 SplashScreen:为全新的应用启动效果赋能!

Jetpack 新成员 SplashScreen:为全新的应用启动效果赋能!

Jetpack新成员SplashScreen:为全新的应用启动效果赋能!

社区说 | 使用 Jetpack SplashScreen 打造全新的应用启动效果

完整体验 Jetpack WorkManager 及场景探讨