9

android app启动页(闪屏页)白屏快速优化方案实践

 3 years ago
source link: http://www.androidchina.net/11831.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

无意间发现,自己的一个线上项目虽然很轻量级,但是在低端机型上依然存在启动页白屏现象,于是就快速优化了一番,在此分享一下优化方案。

2.存在问题

引用一张探索 Android 启动优化方法中的app冷启动流程示意图:

ILQ5nrpng

由启动图可知:打开app时,系统要加载一个空白window,创建进程,初始化app(启动应用),最后才加载启动页布局。如果在创建进程和初始化app过程中耗时较久,那么在启动页布局显示之前,用户便能肉眼感知到空白window的存在,也就是我们所说的白屏(或者黑屏,由你设置的theme决定)。

本文要做的,一步一步解决显示白屏的问题。

3.特别说明

本文就不聊zygote创建进程运行app的那一堆原理来班门弄斧了,只谈一谈解决问题最简单的方法,对原理有兴趣的可以自行翻阅【参考文献】中的相关文档。

4.解决思路

通过给activity指定带有window背景的theme来避免白屏(设置window背景)

优点 缺点 设置window背景 能够快速解决显示白屏问题 可能会引起背景图拉伸问题

5.快速解决方案

创建一个style,清单文件里单独给启动页的theme设置为该style,代码如下:

<style name="AppTheme.Splash" parent="Theme.AppCompat.DayNight.NoActionBar">
    <!--将顶部状态栏设置为透明,并将界面内容布局上边界上提至状态栏顶部-->
    <item name="android:windowTranslucentStatus">true</item>
    <!--如果有底部虚拟导航栏,则将底部虚拟导航栏设置为透明,并将界面内容布局下边界下沉至虚拟导航栏底部-->
    <item name="android:windowTranslucentNavigation">true</item>
    <!--给window窗口设置背景图-->
    <item name="android:windowBackground">@drawable/bg_splash_snow</item>
</style>

@drawable/bg_splash_snow改为你自己的背景图

启动页activity 的 onCreate 方法回调中,将window的背景图置空,代码如下:

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        window.setBackgroundDrawable(null)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)
    }
}

看下效果:

启动页示例2.gif

完事儿~~~下篇见

8c0de91c98bf76faca5055cabcb56a3c03a98e398fdef5f7a.jpg

这就没了?????

aae06983740640ab814d991af8898f9c67612a9391d499944.jpg

对,没错,代码就是这么简单。

不过对style里那三个属性不熟悉的朋友,可能心存疑问:这到底是个什么鬼。。。

那咱接下来就细细的捋一遍吧。

6.事前准备

创建一个启动页界面,布局里设置背景为一张图片,并放一个textview:

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)
    }
}

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_splash_snow"
    tools:context=".activity.SplashActivity">

    <androidx.appcompat.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:text="@string/app_name"
        android:textColor="@color/white"
        android:textSize="22sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

styles文件里新建一个 style ,parent 设置为 Theme.AppCompat.DayNight.NoActionBar 来取消标题栏:

<style name="AppTheme.Splash" parent="Theme.AppCompat.DayNight.NoActionBar">
</style>

AndroidManifest.xml里将该style设置给SplashActivity,方便我们后续对比效果:

<activity android:name=".activity.SplashActivity"
    android:theme="@style/AppTheme.Splash">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

华为手机上运行效果如下:

5ad43f16d970f580f925f31f65fbd2c.jpg

咦?这顶部状态栏跟底部导航栏也忒丑了点。。。。。。

D415A583C0FF67A7D583065D4701F7BE.jpg

那。。干掉吧。。。

1407C31B0C9096C6B3967DE5631B0B63.gif

在style里设置一下:

<style name="AppTheme.Splash" parent="Theme.AppCompat.DayNight.NoActionBar">
    <!--将顶部状态栏设置为透明,并将界面内容布局上边界上提至状态栏顶部-->
    <item name="android:windowTranslucentStatus">true</item>
    <!--如果有底部虚拟导航栏,则将底部虚拟导航栏设置为透明,并将界面内容布局下边界下沉至虚拟导航栏底部-->
    <item name="android:windowTranslucentNavigation">true</item>
</style>

运行效果如下:

启动页示例.gif

哦豁 ~~ 处理好顶部状态栏跟底部导航栏后舒服多了,但是,这个白屏问题???

恩,明显的不像话。

7.通过【设置window背景】解决白屏问题

这个贼简单,只需要通过style的android:windowBackground属性给window设置一个背景,最终设置如下:

<style name="AppTheme.Splash" parent="Theme.AppCompat.DayNight.NoActionBar">
    <!--将顶部状态栏设置为透明,并将界面内容布局上边界上提至状态栏顶部-->
    <item name="android:windowTranslucentStatus">true</item>
    <!--如果有底部虚拟导航栏,则将底部虚拟导航栏设置为透明,并将界面内容布局下边界下沉至虚拟导航栏底部-->
    <item name="android:windowTranslucentNavigation">true</item>
    <!--给window窗口设置背景图-->
    <item name="android:windowBackground">@drawable/bg_splash_snow</item>
</style>

@drawable/bg_splash_snow 是我的一张背景图:

bg_splash_snow.png

运行效果如下:

启动页示例2.gif

完事儿,收工~

“等等~”,有朋友(无中生友?)怕是要问了:那你这一个界面设置了两个背景图,岂不是有些冗余?

哎呀,那来吧,去掉一张吧,把布局文件的图干掉(tools:background表示只渲染预览布局,不参与实际运行):

image-20210610170155306.png

结果如下:

启动页示例2.gif

可以看到,运行结果跟之前跟一模一样。

眼尖的朋友又要问了:“连时间都一样???”

哈哈哈,我就是偷的上一张图。

47915A4C88384FBDB3496D2D7C544FA5.gif

当然了,这里显示的背景图就是window上的背景图,实际上这样是有问题的,比如我启动页要放个viewpager啥的,需要处理图片的一些展示逻辑可咋整,window背景又不能搞成viewpager?

那咱们换一个思路,window的背景图在用完后给他置空,继续显示启动页的布局视图行不行呢?来实践一下看看:

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // 置空window背景图
        window.setBackgroundDrawable(null)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)
    }
}

运行效果如下:

启动页示例3.gif

这什么鬼,黑屏了?哦,忘了给背景图加回去了哈哈哈,再来再来:

image-20210610172320282.png

再运行一下看看效果:

启动页示例4.gif

这下OK了,通过将window背景图设置成一个临时图片,既解决了重复引用图片资源的问题,又可以正常执行启动页自己的逻辑了。

【特别注意】

由于window背景使用图片时无法像imageView一样设置缩放,所以会强制将图片拉伸为屏幕宽高,从而导致图片变形。要避免这种情况出现,可以尝试使用点九图【9-Patch图】或者自定义的drawable图来处理,这里就需要仁者见仁智者见智了。

解决app启动页白屏的方案不止一种,我们这里采用了最直观明了的做法,即使用图片替换白屏的方式,但是要明白,这种做法仅仅是替换了白屏,并没有消除白屏展示占用的时间。由app冷启动流程可知,空白window(即白屏)时间主要由创建进程和初始化app(即启动应用)来决定,虽然我们无法干预创建进程的时间,但可以从初始化app来下手,比如优化自定义的application,从而缩短白屏显示时间,加快应用启动速度。

探索 Android 启动优化方法


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK