大家都遇到过自己 app 启动时间有些长,屏幕白屏的问题吧。一个正常的商业 app 在启动时时会加载一大票组件的初始化的,功能越多需要加载的组件越多,那么 app 启动的时间就越长,app 白屏或黑屏的时间也越长,怎么办呀,今天就来优化一下,谈谈思路,不涉及过多代码
下面是几个 app 启动优化可供参考的案例:
- 优化APP的启动速度
- Android之路 - 冷启动解决方案:实现秒开
-
App启动优化方案
有介绍统计 app 启动时长
app 启动方式
1、冷启动
App没有启动过或App进程被killed, 系统中不存在该App进程。需要创建App进程, 加载相关资源, 启动Main Thread, 初始化首屏Activity等。在这个过程中, 屏幕会显示一个空白的窗口(颜色基于主题), 直至首屏Activity完全启动。
2、热启动
热启动意味着你的App进程只是处于后台, 系统只是将其从后台带到前台, 展示给用户。在这个过程中, 屏幕会显示一个空白的窗口(颜色基于主题), 直至activity渲染完毕。
3、温启动
介于冷启动和热启动之间, 一般来说在以下两种情况下发生:
用户back退出了App, 然后又启动. App进程可能还在运行, 但是activity需要重建
用户退出App后, 系统可能由于内存原因将App杀死, 进程和activity都需要重启
启动优化主要是针对冷启动. 热启动和温启动都相对较快。
关于 StartingWindow 的解释
StartingWindow 我也是才刚刚看到大家说的, 之前一直在此处有疑虑额,但是一直没搞明白,其实就是在 app 在冷启动时先显示一个空白的 window 给大家看,这个 window 叫做 StartingWindow
不错的解释:
application以及activity初始化是一个相当复杂的过程,而白屏或者黑屏恰恰就是这个过程的产物:在应用程序刚启动到应用程序初始化完全之间有一段时间空隙,这时候系统创建了一个空白窗口,叫StartingWindow。这个窗口让人感觉activity已经启动了只是没填充好数据。它是个临时窗口,对应的WindowType是TYPE_APPLICATION_STARTING。这个空白窗口是为了告诉用户,系统已经接受到操作,正在响应。应用程序初始化完成加载完第一帧之后,这个窗口就会被移除。
Window的顶层是DecorView,所以StartingWindow显示一个空DecorView,但是会给这个DecorView应用 launch activity指定的Theme,如果 launch activity没有指定Theme就用application的。activity启动时,windowBackground比setContentView()要先加载,这一段时间如果Theme是Light,屏幕就是白色的;如果是Dark,就会显示黑屏。上述就是黑白屏的原因了
1. 一个正常的 app 启动我们都这么干吧
友盟统计,推送,地图,IM,自家的各种组件,这些很耗时的,尤其是第三方组件
魅族工程师统计过,若是 app 启动时间超过 400 毫秒,那么用户在桌面点击你的应用图标后,将感觉到明显的卡顿
2. app 启动的过程
app 的过程是怎么样的,这 400 毫秒都包含哪些生命周期函数呢~,这里不做深入展开,仅仅看看 app 启动涉及到的生命周期函数及其顺序
点击桌面图标后,安卓系统会 fork 创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类。当这些操作走完之后,你才能看到 SplashActivity 的第一眼。
Application ——>attachBaseContext()——>onCreate()——>Activity——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上
那么在这期间,我们若是在 Application 的 onCreate 做了太多的 SDK的初始化的话,这段时间您的屏幕都是你手机默认主题的颜色(白色/黑色),这就是为什么我们做的APP会出现白屏
3. 那怎么优化呢
优化思路主要有2个:
- 把 Application 的 onCreate 中的初始化操作尽量都放在非 UI 线程,这样不会卡 UI ,大众做法是启动一个 IntentService
- 并不是所有的初始化操作都可以放到 非 UI 线程的,并且系统本身也是很耗时的,若是不想让用户看白屏,那么可以给 app 设置一个背景,先让用户看启动图,产生一个 app 已经启动的假象
关于分割各个组件初始化方法方案看这个,有的必须在 application,其次的放 splashActivity,最后没什么影响的异步线程
4. 如何给 app 设置背景
这里思路也有3个:
- 给 application 的 theme 设置 windowBackground
- 给 SplashActivity 的 theme 设置 windowBackground
- 给 SplashActivity 加透明背景,这样在 app 启动时会透明,就跟没启动一样,一直到后面我们改的 activity 的 theme 加载出来
这里推荐第二种方法,因为 application 的 theme 设置是全局所有 Activity 都有效的,若是 给 application 的 theme 设置 windowBackground 后,所有的 Activity windowBackground 都会设置成这样,这会造成过度绘制的问题,另外这种效果也不是我们想要的
那么给 SplashActivity 的 theme 设置 windowBackground 就成了我们的选择,为啥呢,因为 app 启动时 ,为了不让我们看大黑屏会默认启动一个空内容的 window 先显示着,这个 window 的背景就是 application theme 的 windowBackground ,若是没有的话就用 SplashActivity theme 的 windowBackground
这个体验才是最好的,经测试 4.X 版本也没问题,这里我们在 application 的 onCreate 里延迟3秒,并且给 SplashActivity theme 设置 windowBackground,然后我们看看效果如何
application 设置:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Thread.sleep(3000)
}
}
theme 设置:
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowBackground">@drawable/ic_dog</item>
</style>
<activity
android:name=".MainActivity" android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
效果:
录的不是很好,大家见谅,狗狗还是很可爱的~,最后 view 显示的那一下,gif 没提现出来,大家脑补一下吧~
app 启动优化的思路基本上就是这样了,大家翻再多的资料也不会出这个圈了
然后有个流氓的,launch activity 透明背景,让用户以后还没启动呢,这样太流氓了,我不喜欢,据说微信即使这么干的,这样其实体验也不是非常好
对于首个加载的Activity,可以使用透明背景:可以减少application加载主题的消耗
<style name= "EmptyTheme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
然后,在onCreate第一行执行:setTheme(R.style.AppTheme);
5. 很有意思的拼启动图片
我们直接使用大图作为启动画面的话是会话不少内存的,要是图片简单的话可以使用 SVG 矢量图,或是不给启动页设置主图背景,而是添加一个 imageview 进来,完事手动回收资源,再 GC,这样可以尽可能节省内存资源
要知道 drawable 里面的静态 png 图片除非我们拿到其 bitmap 对象手动标记回收,否则系统会一直保持在内存中,对于频繁使用的小图来说这是一种优化,但是对于仅仅使用一次的大图来说这就不合了
但是呢今天看到了一个很有意思的做大,使用 layer-list + 小图 拼一个大图出来,这样也可以实现节约内存的目的
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowBackground">@drawable/logo_splash</item>
</style>
<!--@drawable/logo_splash文件-->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 如果item下的 bitmap 没有设置 gravity ,再API 23 以下,会出错! -->
<item
android:drawable="@color/splashBg"
android:gravity="fill" />
<item android:gravity="top">
<bitmap
android:gravity="top"
android:src="@drawable/splash_top" />
</item>
<item
android:bottom="72dp"
android:gravity="bottom|center_horizontal">
<bitmap
android:gravity="bottom"
android:src="@drawable/splash_bottom" />
</item>
</layer-list>
这里还有更多玩法哦,甚至还有启动页动画实现的,值得看