序言
我目前任职于一家专做服装批发的互联网公司,做的APP叫做一手APP,用户量在千万级,日活在20万左右,月流水上亿。但目前App的启动耗时较久,启动过渡动画等体验较差。所以,专门对启动优化方面做了调研,并输出调研方案和解决方案。优化前后也有对比图输出,详见第2大点第3小点,优化效果立秆见影。但这只是启动优化的第一阶段,以及整个性能优化系列的某一个知识面,后续还会继续深入做优化。
一、 启动优化的作用。
启动时间过久,会影响新老用户体验。
- 新用户。降低新用户的留存率和应用评分。
- 老用户。对老用户的使用造成烦扰,用户使用频率越高,影响程度越重。
二、商城App与主流/竞品App的启动对比。
- 测试机型:荣耀v20,无后台应用。(不同机型与使用状况,会影响启动时间)
- 测试方式:Logcat过滤Displayed值,多次打开取平均值
- 启动耗时对比:
- 测试总结
- 一手App在启动时间上,与竞品批批网不相上下。但比竞品蘑菇街的启动时间大了1倍有余。比主流App,微信、饿了么等,也多了不止1秒。
- 批批网和蘑菇街,双击返回键,退出应用程序时,没有直接杀死应用。而一手App,直接杀应用。
- 批批网,再次打开时,优先展示了闪屏页,用时在150ms左右。
- 蘑菇街,再次打开时,因为之前只是退入后台,用时0ms。
- 一手App,等同于冷启动的时间。
三、 一手启动现状分析。
- 冷启动。
- Release模式下,启动时间,+1s686ms ~ +2s127ms。
- Debug 模式下,启动时间, 在4s500ms左右。其中,application初始化各工具、SDK占用时长约为1s500ms。闪屏页,占用时长约为1s。这两部分,在当前模式下,理论最大化可降低时长为2.5s。粗略猜测,按折半算,在Release模式下,可减少时长约为1250ms。则冷启动时长,可降低到1s以下。
-
基于第1条,进行实测。Release下注释掉application初始化、闪屏页的onCreate()部分:
启动时间,大幅降低:+1s800ms ----> +398ms -
双击退出优化:参考蘑菇街、批批网的处理方式,可极大的提高APP在短时启动的响应速度。
四、启动优化的目标。
因为启动优化工作,不易处理,但易于影响到现有功能。因此,在目标制定上,应采取先稳后快的思路。优先保稳,尽量做到在优化后,不影响到现有功能。所以,预计按如下目标推进。
- Lancher 点击到 Splash打开 做到秒开。
- splash到 主页打开可操作 能继续减少(待定,看业务)
五、从Launcher到App启动(简述)。
了解应用是如何启动,将有助于我们对应用启动的优化工作。我们的安卓手机桌面,叫Launcher,它实际上也是一个应用。当我们点击桌面上的应用icon时, Launcher会开始执行应用启动,流程简述如下:
- Launcher在Launcher进程负责将启动应用的请求转发给AMS。
- AMS在SystemSever进程负责安全检查、启动应用的参数封装、通知Zygote进程创建应用进程、转发启动应用请求(并携带启动应用所需的参数)给应用进程。
- ActivityThread在应用进程,用AMS传过来的参数,启动根Activity,此时应用已被启动,对用户可见。
六、App的启动状态。
- 冷启动。
- 系统进程。
- 加载并启动应用。
- 立即启动一个空白window。
- 创建应用进程。
- 应用进程。
- 创建应用对象。
- 创建应用的UI(主)线程。
- 创建main activity。
- 加载main activity的view。(onCreate)
- 测试布局。(onMessure/onLayout)
- 执行main activity的view的绘制。(onDraw)
- 冷启动,测验方法。(-S表示如果APP在运行,则杀死APP,再启动)
- 采用上文说的,过滤logcat。
- 测验SplashActivity的冷启动耗时,可以在AS的Terminal执行以下命令:
adbshell am start –S –W com.yishouapp.fumi/.activity.SplashActivity
这里是以商城app为例,并且已将SplashActivity在AndroidManifest.xml里面
配置成启动页,才可以生效。 - 在Ternimal窗口会显示启动状态、耗时等信息。
-
热启动。
将应用进入后台,数据还保存在内存中。此时,再启动应用。应用会从后台被拉到前台,省去了冷启动时的一系列操作,启动时间大幅减少。(PS:可用内存过低时,部分数据可能会被回收,热启动时,会被重新创建,但启动时间仍然明显低于冷启动) -
测验SplashActivity的热启动耗时,需要2步。(结果查看,同冷启动)
- 先打开商城app,停留在SplashActivity页面,并将APP退入后台。
- 在AS的Terminal执行以下命令:
adb shell am start –W com.yishouapp.fumi/.activity.SplashActivity
这里是以商城app为例,并且安装了商城app,并且将SplashActivity在AndroidManifest.xml里面配置成启
动页,才可以生效。
-
温启动。
温启动,是介于冷启动与热启动之间。比冷启动快,但比热启动慢。温启动的场景,举个例子,当你双击返回退出应用后,又马上启动它。这时候,应用进程还在,但其它数据都被销毁了。这时候,应用必须重新创建所有activity的资源,相当于重新走一遍onCreate。亦或是系统杀死了你的应用进程,但应用的数据保存在instanceStateBundle对象里面,且未被销毁。在重新创建应用时,可以利用这个对象,快速的恢复界面。但复现温启动的场景比较困难,暂时没有发现可复现步骤。温启动的2种场景简述。
1)应用进程没了,但数据还在。
2)数据没了,应用进程还在。
七、优化方案。
- 延迟第三方SDK的初始化。(初始化是否一定要在UI线程?)
- 延迟非必要资源的加载,包括网络请求。
- 启动页的UI绘制优化、线程调度优化、GC优化。
- 提高应用的保活度,避免冷启动。
- 重点优化Application和第一个Activity,两个类的onCreate方法。
- 子线程式初始化SDK,这种方式,有两个注意点,一个是要确保SDK是可以在子线程初始化。另一个是要确保在使用SDK的功能前,SDK已经完成初始化,因为子线程式的初始化,是一个异步操作。
八、优化工具。
- Redex. (存在风险)
九、调试辅助。
为了真实的模拟启动时间,建议在release模式下进行,但release模式下,打包速度会极慢。建议,在gradle里面,加入关闭代码检查task,二次编译时,可以极大的减少编译时间。我测试出来的效果(编译时长):4m500s —> 2m。
tasks.whenTaskAdded { task ->
if (task.name == "lintVitalPpRelease") {
task.enabled = false
}
}