适配splashscreen步骤以及启动卡住不动(白屏)的坑

Android 12 启动画面

从 Android 12 开始,在所有应用的冷启动和温启动期间,系统一律会应用 Android 系统的默认启动画面。默认情况下,此系统默认启动画面由应用的启动器图标元素和主题的 windowBackground(如果是单色)构成。

也就是说,只要你的应用运行在Android 12的系统上,都会受到影响,以支付宝为例,截止到2023-01-24日,在Android 13的系统上冷启动时,可以很明显的看到会出现一个闪屏页,页面中间会显示一个启动图标。这个就是受到了系统影响,没有做适配。

请添加图片描述

Google应该是想让Android有一个统一的启动风格,但是讲道理推出的有点太晚了,国内的App启动页不仅仅是个简单的展示,有的还要显示广告,且风格也各不相同,都有自己的一套启动ui,所以,推出这个splashscreen对国内的app完全是个负担。
但是不适配的话启动的时候看起来有很怪,会出现两个闪屏页,所以,还是要硬着头皮适配一下。

SplashScreen适配

适配方法也很简单,按照官方文档的步骤来即可。
官方建议我们使用AndroidX中的SplashScreen库,该库使用 SplashScreen API,能够向后兼容,官方宣称可在所有 Android 版本上显示外观和风格一致的启动画面。

但是实际使用并非如此,例如动画在Android 12 以下的系统上不生效,如果需要动画,可自己使用其他方式实现,如果只是显示一个图片,那么就不受影响。

以下是适配步骤

将compileSdkVersion更改为31或以上

compileSdkVersion 31

添加依赖

implementation 'androidx.core:core-splashscreen:1.0.0'

准备启动页面要显示的图片或者动画

在这里插入图片描述

新增主题

    <style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
        <!--背景颜色-->
        <item name="windowSplashScreenBackground">@color/white</item>
        <!--设置动画或图片,注意动画在Android12 以下不生效 很难受...-->
        <item name="windowSplashScreenAnimatedIcon">
            @drawable/ic_splash
        </item>
        <!--动画执行时间-->
        <!--        <item name="windowSplashScreenAnimationDuration">1000</item>-->
        
        <!--设置闪屏也后启动页面的主题 说白了 就是app本来的主题-->
        <item name="postSplashScreenTheme">@style/Theme.XeonYuTheme</item>

    </style>

创建启屏页面 SplashActivity
先指定页面的主题
在这里插入图片描述

然后就是代码了,代码非常简单,如果你没有其他需求,直接在oncCreate中调用 installSplashScreen() ,然后正常跳转页面即可.

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        /*setContentView 可不写*/
//        setContentView(R.layout.activity_splash)
        /*创建与页面关联的SplashScreen实例*/
        installSplashScreen()
        /*跳转页面*/
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }
}

然后运行看看
请添加图片描述
可以看到,启屏页就ok了,非常简单。

在启动屏幕页面显示弹窗

一般情况下,我们需要在用户首次打开app时弹窗让用户同意隐私协议,以及做一些初始化操作之类的。那这种情况下,如要用户点击同意后再进行跳转。

此时,我们可以使用setKeepOnScreenCondition 配合 setOnExitAnimationListener,示例代码如下


class SplashActivity : AppCompatActivity() {

    /**
     * 控制是否保持启动页面的变量,值为false时继续往下走
     */
    private val keepOnScreenCondition = AtomicBoolean(true)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        /*setContentView 可不写*/
//        setContentView(R.layout.activity_splash)
        /*创建与页面关联的SplashScreen实例*/
        installSplashScreen().apply {
            /*保持住启动页面的显示*/
            setKeepOnScreenCondition {
                keepOnScreenCondition.get()
            }
            /*指定是否Activity要自己处理启动画面动画 实际上就是keepOnScreenCondition的值为false时这个就会回调*/
            setOnExitAnimationListener {
                /*在这里我们可以弹窗让用户同意隐私政策*/
                val alertDialog = AlertDialog.Builder(this@SplashActivity)
                    .setTitle("隐私协议")
                    .setMessage("隐私协议内容")
                    .setPositiveButton(
                        "同意"
                    ) { dialog, which ->

                        /*跳转页面*/
                        val intent = Intent(this@SplashActivity, MainActivity::class.java)
                        startActivity(intent)
                    }
                    .setNegativeButton("拒绝") { dialog, which ->
                        this@SplashActivity.finish()
                    }.create();
                alertDialog.show();
            }

        }


        MainScope().launch {
            Log.i("SplashActivity", "可以做一些初始化逻辑")
            delay(500)
            keepOnScreenCondition.compareAndSet(true, false)
        }

    }
}

再来运行看看:
请添加图片描述

部分设备debug模式下setOnExitAnimationListener不回调的问题

美滋滋的写完代码后,以为没啥问题了,结果换了个Android12的真机运行后发现一直卡在启动页,并没有弹窗或者跳转页面,试了好多次依旧如此。
加了log发现setOnExitAnimationListener中的回调没有执行。明明是按照官方文档写的,怎么会有问题呢?
仔细看了下文档说是要保证在splashscreen退出前setOnExitAnimationListener,这个我代码是肯定没问题的。

后来手动把app杀死,然后点击图标手动启动时又是正常的,给我整懵了,于是查了下资料发现其他人也遇到了这种问题。

https://issuetracker.google.com/issues/197906327

这个问题被google官方人标记为是预期的,不会解决,解释如下:
在这里插入图片描述

意思就是:
这个是有意这样的,因为IDE实际上使用Instrumentation来打开应用程序,所以系统将其解释为一个应用程序打开另一个应用程序,而不是用户单击启动器图标。
所以会出现setOnExitAnimationListener不执行的情况。

除了debug时会受到影响,通过其他方式拉起App也会收到影响比如一些rom上安装后会自动打开App,这种就会卡住,必须杀掉进程后重新冷启动才能正常进去。
因此,我们需要做的就是即使 setOnExitAnimationListener 没有被回调,也要保证后续代码得到执行。

下面抛砖引玉,大体思路就是给最终被执行的方法加个标记,在 keepOnScreenCondition.compareAndSet(true, false) 之后延迟短暂的时间再去调用一下最终要执行的方法,以确保一定会得到执行。

示例代码如下:
伪代码

import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.yzq.kotlincommon.ui.activity.MainActivity
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import java.util.concurrent.atomic.AtomicBoolean

class SplashActivity : AppCompatActivity() {

    /**
     * 控制是否保持启动页面的变量,值为false时继续往下走
     */
    private val keepOnScreenCondition = AtomicBoolean(true)

    /*最终的方法是否调用*/
    private val dialogShowInvoke = AtomicBoolean(false)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        /*setContentView 可不写*/
//        setContentView(R.layout.activity_splash)
        /*创建与页面关联的SplashScreen实例*/
        installSplashScreen().apply {
            /*保持住启动页面的显示*/
            setKeepOnScreenCondition {
                keepOnScreenCondition.get()
            }
            /*指定是否Activity要自己处理启动画面动画 实际上就是keepOnScreenCondition的值为false时这个就会回调*/
            setOnExitAnimationListener {
                /*在这里我们可以弹窗让用户同意隐私政策*/
                showDialog()
            }

        }


        MainScope().launch {
            Log.i("SplashActivity", "可以做一些初始化逻辑")
            /*解除blockui,此时正常情况下会调用setOnExitAnimationListener 已知Android12拉起App时不会调用*/
            keepOnScreenCondition.compareAndSet(true, false)
            delay(100)//这里最好延迟一下,保证setOnExitAnimationListener有机会得到执行
            if(!dialogShowInvoke.get()){
            	showDialog()//这里去掉调用一下最终要执行的方法,兜个底
            }
        }

    }

    @Synchronized
    private fun showDialog() {
        if (dialogShowInvoke.get()) {
            /*已经执行了*/
            return
        }
        dialogShowInvoke.compareAndSet(false, true)
        val alertDialog = AlertDialog.Builder(this@SplashActivity)
            .setTitle("隐私协议")
            .setMessage("隐私协议内容")
            .setPositiveButton(
                "同意"
            ) { dialog, which ->

                /*跳转页面*/
                val intent = Intent(this@SplashActivity, MainActivity::class.java)
                startActivity(intent)
            }
            .setNegativeButton("拒绝") { dialog, which ->
                this@SplashActivity.finish()
            }.create();
        alertDialog.show()
    }
}

这样我们就能够确保启动时不会卡住,但是在Android12上被其他app调起会出现设置的UI显示不正常的情况,比较影响观感,暂时还没想到好的办法,有知道怎么弄的大佬可以指点一波。


感谢阅读,如果对你有帮助请点赞支持。有任何疑问或建议,欢迎在评论区留言讨论。如需转载,请注明出处:喻志强的博客

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
Android 12 splashscreen 启动的详细流程,代码调用是一个技术问题,我可以回答。在 Android 12 中,SplashScreen 是一个新的启动屏幕 API,它可以在应用程序启动时显示一个自定义的启动屏幕。具体流程如下: 1. 在 AndroidManifest.xml 文件中,设置 SplashScreen 的主题样式。 2. 在应用程序的启动 Activity 中,使用 SplashScreen.installSplashScreen() 方法安装启动屏幕。 3. 在启动屏幕的布局文件中,定义自定义的启动屏幕布局。 4. 在应用程序启动完成后,使用 SplashScreen.hide() 方法隐藏启动屏幕。 代码调用示例: // 在 AndroidManifest.xml 文件中设置 SplashScreen 的主题样式 <activity android:name=".MainActivity" android:theme="@style/SplashScreenTheme"> // 在应用程序的启动 Activity 中,使用 SplashScreen.installSplashScreen() 方法安装启动屏幕 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SplashScreen.installSplashScreen(this); setContentView(R.layout.activity_main); } } // 在启动屏幕的布局文件中,定义自定义的启动屏幕布局 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/splash_screen_background_color"> <ImageView android:id="@+id/splash_screen_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/splash_screen_logo" android:layout_centerInParent="true" /> </RelativeLayout> // 在应用程序启动完成后,使用 SplashScreen.hide() 方法隐藏启动屏幕 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SplashScreen.installSplashScreen(this); setContentView(R.layout.activity_main); // 应用程序启动完成后,隐藏启动屏幕 SplashScreen.hide(this); } }
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喻志强(Xeon)

码字不易,鼓励随意。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值