Android 冷启动黑/白屏 or“两个启动屏幕(SplashActivity)?”or“多了一个含有app icon的启动页面”

嘿嘿,你好啊

说起来,这是一个咱们Android开发中,非常经典的一个问题,通常就是叫"冷启动白屏/黑屏问题"(Cold Start White/Black Screen)。然后看到的第一个"新加的页面",其实并不是咱们开发者主动增加的activity,而是Android系统给自动生成的"预览窗口"(Starting Window)。

1.为啥会出现这个页面

当用户点击app图片来启动App时,我们都知道Android系统需要做很多准备工作,如加载app进程、加载DEX文件、初始化application等等,这个过程是需要一定的加载时间的,通常就是几十毫秒到几秒。

然后呢,Android系统为了不让用户觉得点击图标后手机"死机"了或者没反应,系统就会立即弹出一个预览窗口,用来先及时响应一下用户的指令

2.效果图

原始效果

方案2效果:

方案3效果:

3.为啥标题这么长

哈哈哈哈,搞这么长个题目,实在也是属于无奈之举。因为国内厂商系统碎片化严重,各种厂商的各种定制系统,例如:小米的MIUI、HyperOS,oppo的ColorOS,华为的HarmonyOS等等。导致对Android系统来说是同一个问题的东西,在不同的厂商手机上,可能会有不一样的表现。

如果不做任何适配,比方说(目前发现问题的),

  • OPPO:可能是他们为了提升用户体验,然后系统自动检测咱们项目中的APP图标,然后给放到预览窗口的中间,模仿一个启动页,所以就有了最后一个"or"
  • 其他:其他的手机就是根据默认的主题背景色,然后会有一个白屏或者黑屏的显示

4.最佳解决方案:无缝过渡(Themed Launch Screen)

首先哈,建议不要试图去"消除"这个窗口(就比如说,你如果把这个预览窗口设为透明,那样确实不会有白屏黑屏啥的了,但是用户点击icon后,会感觉手机卡顿了一下才出现了你的SplashActivity,对于体验来说,甚至不如有个白屏了)。

经多对市场上的一些热门app的体验调研,我发现现在市面上大概有三种情况,

  1. 第一种就是先不管,点一下有个白屏,全部交给系统
  2. 第二种是设计一个背景色,然后加一个自己的logo,相当于简单自定义一下这个页面,更价贴合app主题
  3. 第三种则是将startingwindow直接替换成一整张图片

第一种肯定不用管,首先我们先说一下第二种,实现完毕之后就是:

点击icon ->瞬间展示贴合主题的页面->然后代码逻辑开始运行了,体验很丝滑美妙

ffs

  1. 第一步:添加依赖(build.gradle)
    在 app/build.gradle 文件中添加库的依赖:
xml 复制代码
dependencies {
    implementation "androidx.core:core-splashscreen:1.0.1"
}
  1. 第二步:配置主题 (themes.xml)
    咱们需要自定义一个专门的启动主题。这个主题用来自定义启动页长什么样
    打开 res/values/themes.xml (或 styles.xml):
xml 复制代码
    <style name="Theme.App.Starting" parent="Theme.SplashScreen">
<!--        背景色-->
        <item name="windowSplashScreenBackground">@color/white</item>
<!-- 背景中间的的图片-->
        <item name="windowSplashScreenAnimatedIcon">@drawable/startcenter</item>
<!--启动页结束后」,App 主界面要切换到的目标主题-->
        <item name="postSplashScreenTheme">@style/Theme.YourApp.Normal</item>
    </style>

    <style name="Theme.YourApp.Normal" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="colorPrimary">@color/purple_500</item>
    </style>
  1. 第三步:修改 AndroidManifest.xml
    将项目中的入口activity,我的demo中是SplashActivity的主题,替换成咱们上面上自定义的主题
xml 复制代码
 <activity
            android:name=".ui.startingwindow.SplashActivity"
            android:exported="true"
            android:theme="@style/Theme.App.Starting">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
 </activity>
  1. 第四步:运行
    嘿嘿嘿,逗你玩儿,这个时候你会惊喜的发现,诶?startwindow设置明白了,怎么报错呢?欲知为何报错,请看下章第五步
xml 复制代码
  java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.blogdemoapplication/com.example.blogdemoapplication.ui.startingwindow.SplashActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
                                                                                                    	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4410)
                                                                                                    	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4631)
                                                                                                    	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:112)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:186)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:112)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:84)
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2873)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:107)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:249)
                                                                                                    	at android.os.Looper.loop(Looper.java:337)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:9580)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
                                                                                                    Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
                                                                                                    	at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:902)
                                                                                                    	at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:865)
                                                                                                    	at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:748)
                                                                                                    	at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:193)
                                                                                                    	at com.example.blogdemoapplication.ui.startingwindow.SplashActivity.onCreate(SplashActivity.kt:21)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:9221)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:9177)
                                                                                                    	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1538)
                                                                                                    	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4392)
                                                                                                    	... 13 more
  1. 在代码中初始化
    这是最重要的一步。你必须在 super.onCreate() 之前调用 installSplashScreen()。

kotlin:

xml 复制代码
 override fun onCreate(savedInstanceState: Bundle?) {
        val splashScreen = installSplashScreen()

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
       
        // ... 你的初始化代码
    }

下面是咱们用java的同学看的:

xml 复制代码
protected void onCreate(Bundle savedInstanceState) {
        SplashScreen splashScreen = SplashScreen.installSplashScreen(this);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // ...你的初始化代码
    }

问题是解决了,那么有些喜欢观察的兄弟们说了, 我咋看有的app直接就是一个中间图片,然后底部还有个他们的Slogan啊,这博主藏着掖着的,不好好写是吧。别着急,兄弟们,没上来就说,其实是因为,底部添加图片的话,会在Android12上下有一个兼容性限制,比方说:

  • 新手机(Android 12+):显示中间Logo+底部图片
  • 旧手机(Android 11-):只显示中间Logo,底部图片会被自动忽略

如果能接受这种差异的话(当然凡是底部有图片的,也都是这么做的),请看下面详情解析:

  1. 首先你需要准备一张图片(建议是透明背景的PNG或SVG),我是demo只是为了展示功能。因为这个新的配置是Android12+的,所以你需要在values的同级目录新建一个values-v31,然后再创建一个themes.xml(styles.xml),原来themes.xml中的不变:
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="Theme.App.Starting" parent="Theme.SplashScreen">
        <!--        背景色-->
        <item name="windowSplashScreenBackground">@color/white</item>
        <!-- 背景中间的的图片-->
        <item name="windowSplashScreenAnimatedIcon">@drawable/startcenter</item>
        <!-- 背景底部的的图片-->
        <item name="android:windowSplashScreenBrandingImage">@drawable/startingbom</item>
        <!--启动页结束后」,App 主界面要切换到的目标主题-->
        <item name="postSplashScreenTheme">@style/Theme.YourApp.Normal</item>
    </style>
</resources>
复制代码
注意:
 对图片的一些要求:如果底部这张图太大,系统就可能不显示或者裁剪的及其难看,查了一些资料,看到推荐尺寸是:高度不要超过80dp,宽度不超过200dp,长宽比建议是2.5:1,内功的话,不要有太多透明边距

方案2终于是告一断落,接下来我们说一下方案3,看过官方文档并且细心的小伙伴应该能发现,理论上来说,绝对的替换成一张图片,Google是不支持的,因为官方最新标准,新 Splash API (core-splashscreen) 强制规定背景只能是颜色,不能是图片。并且中间必须是 Icon。所以理论上来说,方案3是不可行的。

但是虽然Google强推新标准,但是国内厂商(小米,OPPO,华为等)的ROM对"传统方式"的兼容性很好,为了视觉效果,国内很多app就依旧使用的传统方案。即:android:windowBackground 来设置背景图片,接下来说说完整步骤:

  1. 搞一张背景图
    起个名字,比如说splashbg.png,放到drawable目录下面
  2. 定义传统 Splash 主题
    在 themes.xml 中定义一个主题,然后将上面提到的 windowBackground 设为图片,并将状态栏设为透明以实现沉浸式效果
xml 复制代码
    <style name="Theme.App.FullSplash" parent="Theme.MaterialComponents.Light.NoActionBar">
<!--        背景图-->
        <item name="android:windowBackground">@drawable/splashbg</item>

        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:windowTranslucentStatus">true</item> </style>
  1. 应用到 Activity
xml 复制代码
<activity
            android:name=".ui.startingwindow.SplashActivity"
            android:exported="true"
            android:theme="@style/Theme.App.FullSplash">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

这就是背景是一整张图片的实现方案了,上面也说了,国内厂商兼容的挺好,但是就意味着,在原生 Android 12+如Pixel 手机上就会失效,然后变成纯色+logo的效果,看能不能接受了。

我是入梦,谢谢你的观看我的博客,觉着好用的话,麻烦帮忙点个赞呗,谢谢啦~ 如果有什么错误,请随时联系我噢~QQ:897589417

相关推荐
阿巴斯甜15 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker15 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952716 小时前
Andorid Google 登录接入文档
android
黄林晴17 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android