一、前言
APP优化是我们进阶高级开发工程师的必经之路,而APP启动速度的优化,也是我们开启APP优化的第一步。用户在使用我们的软件时,交互最多最频繁的也就是APP的启动页面,如果启动页面加载过慢,很可能造成用户对我们APP的印象过差,进而消耗了用户的耐心,更严重可能导致用户的卸载行为。这也是微信始终坚持使用“一个小人望着地球”
作为启动页面的背景,并且坚持不添加启动广告的的原因。
二、APP的三种启动方式
来看一下Google官方文档《Launch-Time Performance》对应用启动优化的概述;
应用的启动可以分为冷启动,热启动和温启动,而启动最慢、耗时最长的就是冷启动。
冷启动(cold start)
当应用启动时,后台没有该应用的进程(常见如:进程被杀、首次启动等),这时系统会重新创建一个新的进程分配给该应用。
热启动(hot start)
这种启动会从已有的进程中来启动应用,通俗来讲就是已经启用的应用,通过back键或者home键回到系统主界面,再次通过最近任务重新打开Activity的过程。开销比冷启动更小。
暖启动(warm start)
暖启动产生的场景很多。常见如:1。用户使用返回键退出应用,然后马上又重新启动应用。2.应用被内存清除,再次打开应用,会通过OnCreate()中保存的实例状态恢复。
冷启动是从头开始启动APP,而其他两种启动方式是从后台活动返回到前台的一个过程。热启动和暖启动没有明显的区分界限,我们姑且把热启动和暖启动统称为热启动。开发中我们更多的关注冷启动优化,本文也是从Android的实例从发,分析冷启动的启动过程,并给出冷启动的优化方案。
三、APP的冷启动过程
冷启动开始时,系统会依次执行三个任务去启动APP:
- 加载和启动应用程序
- APP启动后,立即创建一个空白的启动Window
- 创建APP的进程
在这三个任务执行后,系统创建了应用进程,那么应用进程接下来会执行下一步:
- 创建APP对象
- 开启一个主线程
- 创建启动页的Activity
- 加载View
- 布局view到屏幕
- 进行初始绘制显示视图
当应用进程完成初始绘制之后,系统进程用启动页的Activity来替换当前显示的空白Window,这个时刻用户就可以使用App了。
四、APP启动时间
APP的启动时间是我们可以检验优化效果的依据,启动时间是指打开应用从初始化到显示启动页Activity的这一段时间。
Google官方的解释:APP startup time
使用过logcat查看启动时间
在Android4.4(API level 19)以上的Android版本上,当启动应用时Android Studio
自动会在logcat
中输出启动时间。 这个时间从应用启动(创建进程)开始计算,到完成视图的第一次绘制(即Activity内容对用户可见)为止。
如Display显示:
I/ActivityManager: Displayed com.example.app/com.example.app.SplashActivity: +1s742ms (total +49s450ms)
reportFullyDrawn()方法
Activity 的reportFullyDrawn()
方法,
它会在Logcat
里打印从apk初始化到reportFullyDrawn()
方法被调用用了多长时间(文章的reportFullyDrawn()
在SplashActivity
中的onCreate()中执行,可以看到显示的时间和Displayed的是一摸一样的)
I/ActivityManager: Fully drawn com.example.app/com.example.app.SplashActivity: +1s742ms (total +49s450ms)
执行adb命令手动查看启动时间
adb shell am start -W [packagename]/[packagename.SplashActivity]
输出:
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.app/.SplashActivity }
Status: ok
Activity: com.example.app/.SplashActivity
ThisTime: 1368
TotalTime: 1368
WaitTime: 1432
Complete
ThisTime:最后一个启动的Activity的启动耗时;
TotalTime:自己的所有Activity的启动耗时;
只用关注TotalTime就行了
启动时间标准
官方给出,当启动时间超出以下指标时,会被认为启动时间过长,这是就需要考虑仔细优化启动时间。
- 冷启动时间超过5s
- 热启动时间超过1.5s
- 暖启动时间超过2s
说了一大堆启动的基础知识,下边开始讲解真正的优化实战
五、启动优化实战
解决应用刚启动时的白屏问题
前边讲到,应用初始化会进行一系列进程的创建,资源的初始化工作,这段时间系统会先分配一个空白的Window,这会造成用户打开应用到显示第一个可交互的Activity,会经历一段白屏的时间。
这时我们可以在给app定义一个主题去解决,在Activity显示出来之前先显示一个主题背景,去填补空白的Window阶段。
定义一个Splash主题
<!--Splash launcher-->
<style name="LauncherTheme" parent="AppTheme">
<item name="android:windowBackground">@mipmap/ic_splash_bg</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
在manifest
中引用该主题
<activity android:name=".activity.SplashActivity"
android:theme="@style/Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
最后记得在启动页显示以后恢复默认的APP主题
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTheme(R.style.AppTheme)
setContentView(R.layout.activity_splash)
}
这种方法只是视觉上给用户一种快速启动的感觉,不能减少实际的启动时间。
避免Application初始化过重
随着我们工程越做越大,第三方库和组件也逐渐被依赖到我们的项目中,避免不了会在Application
的onCreate()中执行很多第三方库的初始化工作。大量的初始化工作导致该生命周期过于沉重,可能会加长应用的启动时间,因此我们应该对这些第三方库进行分类和优化。
- 必须在onCreate()且是主进程中初始化
- 可以延迟,但是需要在Application中初始化
- 可以延迟到启动页的生命周期回调中初始化
- 延迟到用的时候再初始化
大家可以根据自身项目去整理代码,可以延迟执行的应该放在IntentService
或者Work Thread
中进行初始化。
- 例如EventBus 需要在Activiy中使用的,必须在Application中初始化
- 例如Bugly ,GrowingIO等类似库的可以放在Work Thread中初始化
- 例如地图定位、ImageLoad可以延迟到使用之前初始化
- SplashActivity中网络加载的资源,可以首次加载存放在缓存中,下次启动的时候再显示
- 注意有些第三方库必须在主线程中初始化
- 避免耗时操作,如数据库I/O操作不要放在主线程执行
- 删除无用或重复的代码
- 减少首屏Activity中的网络请求密度