App架构师实践指南器之性能优化六
"天下武功,唯快不破",用户一般期许App响应和加载速度越快越好,如果启动速度慢了,给用户的第一印象就差了,可能导致用户较低的评分甚至直接卸载。
1、APP启动方式和流程
---Cold Start。冷启动,指在App启动之前,该App的进程还没有创建,例如在安装后第一次启动、设备重启或者应用被杀死等情况下发生。如图9-18所示为Android中一次App冷启动重要过程展示,涉及加载并启动App,展示空白Window(Starting Window),创建APP进程,在APP进程创建后,会创建App对象,启动MainThread,创建MainActivity、Inflating Views、Layout Scren、Initial Draw。
---Warm Start。热启动,当启动App时,后台已经有APP的进程(后台挂起),例如按Home键退出App等。
---Lukewarm start。问启动,介于冷启动和热启动之间,例如系统由于某种原因回收了你的App,用户重新启动APP等。
2.App启动流程
Android APP启动流程。一次完整的Android APP启动流程如图9-19所示,核心涉及Zygote fork、Application初始化、Activity Create等。
2.APP启动时间度量
App启动时间一般可以定义为从单击应用的启动图表/桌面Icon开始创建一个新的进程到我们看到第一帧的界面过程。
2.1 方法1:adb shell方式。命令为adb shell am start -W [pkg_name]/[activity],如下为微信第一次启动时间信息,会有3个时间信息
---This Time。一般和TotalTime相同,除非在启动时开了一个透明的Activity等,预先处理后再显示主Activity,这样TotalTime要小,其表示一连串启动Activity到最后一个Activity启动耗时。
---TotalTime。新应用启动耗时,包括新进程启动+Application初始化+Activity启动的时间,这是开发者一般要关注的真正启动耗时。
---WaitTime(Android 5.0+)。总的耗时,包括新应用启动耗时以及前一个应用Activity pause时间。
2.2 方法2:adb logcat方式(Android 4.4+)。Android4.4之后,Android在系统Log中添加了Display的Log信息,可以通过过滤ActivityManager及Display关键字,抓取logcat中的启动时间信息。命令为adb logical | grep "ActivityManager",显示如下信息。这里的时间不包括数据的加载,因为很多应用在加载时会启动懒加载模式,即数据获取后再刷新显示UI,所以,如果需要获取全部时间包括数据加载时间,需要在activity代码的onLoadFinished函数中加上reportFullyDrawn().
2.3 方法3:TraceView工具。
---直接通过DDMS的start traceview启动,弹窗选择trace模式开始记录。
---代码即成方式,在需要调试的地方加入Debug.startMethodTracing("XX"),在结束的地方加入Debug.stopMethodTracing(),运行后将生成XX.trace文件,通过DDMS打开trace文件即可分析,需添加“android.permission.WRITE_EXTERNAL_STORAGE”权限。
说明:上面所说的是普通应用启动时间度量,如果是游戏类应用,那启动时间还需要加上游戏本身的Activity启动时间,即游戏App启动时间=系统启动时间+游戏Activity启动时间。
3、APP启动速度优化
App启动速度优化,也称App快启,主要从减少耗时和优化体验两个部分进行优化。
3.1 减少耗时
---Application。减轻繁重的APP初始化,除非立即需要的,其他对象都采取延迟初始化/懒初始化,全局静态对象放到一个单例中懒初始化;在构造方法、attachBaseContext()、onCreate()中不做耗时操作;一些数据预取放在异步线程/后台任务中等。
---Activity。减轻繁重的Activity初始化。
------避免大量复杂布局,尽量减少布局的层次和嵌套布局。
------不必要在启动时展示的view可以通过viewstub实现,需要时再填充。
------避免加载或编码bitmap,那些依赖bitmap的view延迟更小。
------避免硬盘或网络操作阻塞主UI绘制。
------避免在主线程/UI线程中进行资源初始化操作,可以延迟初始化或者在自线程中操作。
---更深一点。上述建议可能对小型app问题不大,若考虑大型app业务的错综复杂,可以开发一个app初始化组件,其核心就是对所有的初始化任务进行分类分级,各个任务并行处理,同时设置预显示内容,这样各个业务初始化模块互不依赖,且不影响App快启,也不会因为新增业务初始化而造成不必要的工作量。
---优化体验。还可以通过主题化APP启动屏幕来改善启动体验,一种常用的方式是在等待的第一帧时间里,加入一些配置以增加体验(Android Material Design中建议使用一个placeholder UI),如加入Activity的windowBackground主题属性来为启动的Activity提供一个简单的drawble(例如设置App logo或者透明色等),这个背景会在显示第一帧前提前显示在界面上,具体代码如下。