哥哥教你Android冷启动优化秘籍宝典 !
阅片无数,如果我问你冷启动优化你有哪些高招?你会怎么讲?
一,改主题背景?
把启动页主题背景设置为闪屏页图片,给用户一秒响应的感觉,但是并不会真正减少用户启动时间,仅仅属于视觉优化。
二,改主页布局?
1)通过减少冗余或者嵌套布局来降低视图层次结构
2)用 ViewStub 替代在启动过程中不需要显示的 UI 控件
三,异步加载第三方?
因为在主线程上进行资源初始化会降低启动速度,所以可以将不必要的资源初始化延迟,达到优化的效果。但是这里要注意懒加载集中化的问题,别用户启动时间快了,但是无法在界面上操作就尴尬了。
这个回答对吗?对!没人说不对!但在我看来,这只是开发的基础,并不可以算是优化!是你必须要做的!
接下来哥哥就教你到底该怎么办?
办法一:
1,清空手机后台
2,在命令行执行(需要配置ANDROID_HOME 环境变量)
python $ANDROID_HOME/platform-tools/systrace/systrace.py gfx view wm am pm ss dalvik app sched -b 90960 -a 你的包名 -o test.log.html
3,运行你的APP,操作到你想测试性能的地方,然后再命令执行行窗口中按Enter键停止收集。
4,用chome(只支持此浏览器)打开生成的test.log.html结果文件,打开效果如下图:

目前需要关心地方就是我们的应用进程相关的,也就是红框圈起来的地方。图中的 F 代表绘制帧,黄色/红色表示该帧绘制超时,绿色代表绘制正常,也就是在16.6ms内绘制完一帧。
关于 systrace 如何使用可以参考这篇文章 性能工具Systrace
放大可以看到在启动过程中,控件渲染耗时情况如下:

1,所以可以很容易发现哪些启动过程中没有用到的 UI 控件也被渲染了,这时就可以用 ViewStub 去替代。
2,但是现在可以看到的都是系统调用的耗时情况,因为谷歌预先在代码里关键的地方加上了监控,如果想要看到自己方法的耗时,那需要手动在方法入口加上Trace.beginSection("TAG"),在方法结束的地方加上Trace.endSection()。 这样就可以在生成的结果中看到我们自定义的 tag。
3, 如果很多地方你都想加上监控,手动加是肯定不合适的,这里推荐函数插桩方式自动加上监控代码,参考 systrace+函数插桩,这种方式不仅可以帮助监控启动过程中性能问题,再做卡顿优化的时候也可以用这种方式。
办法二:
redex 是 Facebook 开源的一款字节码优化工具,目前只支持 mac 和 linux。我们用的是里面的 interdex 功能来重排列我们 dex 中的 class 文件,那么为什么重排列 class 文件可以优化启动速度?
简单的说,通过文件重排列的目的,就是将启动阶段需要用到的文件在 APK 文件中排布在一起,尽可能的利用 Linux 文件系统的 pagecache 机制,用最少的磁盘 IO 次数,读取尽可能多的启动阶段需要的文件,减少 IO 开销,从而达到提升启动性能的目的。作者:老王头碎碎念链接:https://www.jianshu.com/p/453d21f8c399来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
具体可以参考支付宝的这篇 《通过安装包重排布优化 Android 端启动性能》
三步走战略:
1)安装配置 redex
2)获得启动过程中 class 文件的加载顺序
3)根据这个顺序重排列 dex 中的 class 文件
1)下载 redex,配置环境(Mac OS)git clone https://github.com/facebook/redex.git

2)编译安装 redex

编译时间较久,不想干等着,可以加上 say 指令,编译完成后语音通知
autoreconf -ivf && ./configure && make && say '编译完成'
3)配置优化项
因为 redex 默认不开启 interdex,所以我们要在配置文件中加上相应的配置,在 redex 文档中有说明

所以我们打开配置文件

按照下图配置

4)获得启动 class 加载顺序列表
这里按照 redex 提供的工具获取,但是需要手机有 root 权限,首先清空后台进程,然后打开你的应用,获取你的应用的 pid

5)通过 redex 处理

6)重新签名
这时候生成的 output.apk 是不能直接安装的,需要重新签名,我测试用的是 debug 包,所以重新签了debug 的签名
jarsigner-keystore~/.android/debug.keystore-storepass android-keypass android output.apk androiddebugkey
到这就可以重新安装测试了,按照 facebook 的说法和一些大厂的实践,启动速度大概可以提高 10%~20%,在低端机型上效果应该更明显。
关于 redex 的使用和相关配置文档,都可以在 redex/docs/ 目录下查看。
启动耗时测量:
为了正确诊断冷启动的性能,需要冷启动的时间指标,下面有两种简单的方式:
adb命令 : adb shell am start -S -W 包名/启动类的全名
adb shell am start -S -W com.android.helloword/com.android.helloword.MainActivity

ThisTime: 最后一个 Activity 的启动耗时(例如从 LaunchActivity - >MainActivity「adb命令输入的Activity」 , 只统计 MainActivity 的启动耗时)
TotalTime: 启动一连串的 Activity 总耗时.(有几个Activity 就统计几个)
WaitTime: 应用进程的创建过程 + TotalTime
这里我们关注 TotalTime 就可以。
谷歌在 Android4.4(API 19)上也提供了测量方法,在 logcat 中过滤 Displayed 字段,、

输出的值表示在启动过程和完成在屏幕上绘制相应 Activity 之间经过的时间,其实和上面的方式得到的结果是一样的。
关于 Android App 的冷启动过程和一些概念可以参考谷歌官方文档 「App startup time 」
还有一些优化方法没有实践,有兴趣的可以自行了解:
1)启动过程中的 GC 优化,尽量减少 GC 次数,避免大量或者频繁创建对象,如必须,可尝试放到 Native 实现
2)线程优化,尽可能减少 cpu 调度,具体就是控制线程数量和调度
3)在类加载的过程中通过 Hook 去掉类验证的过程,可以在 systrace 生成的文件中看到 verifyClass 过程,因为需要校验方法的每一个指令,所以是一个比较耗时的操作