性能优化一直都是一个 Android 开发者逃不过的话题,启动优化则更是重中之重。启动速度能够直接影响一个 App 的留存率和转化率,没有人会但愿本身点击以后还要等一会才打开。html
可是当我一番调研后发现,网上大部分启动优化相关的文章,套路都差很少,我称之为老三样。python
什么是老三样?linux
这么作的目的主要是为了消除启动时的黑白屏,给用户一种秒响应的感受,可是并不会真正减小用户启动时间,仅属于视觉优化。android
1)经过减小冗余或者嵌套布局来下降视图层次结构 2)用 ViewStub 替代在启动过程当中不须要显示的 UI 控件git
由于在主线程上进行资源初始化会下降启动速度,因此能够将没必要要的资源初始化延迟,达到优化的效果。可是这里要注意懒加载集中化的问题,别用户启动时间快了,可是没法在界面上操做就尴尬了。github
老三样并不说是无论用或者过期了,只是这三种优化方式都是很是基础的方式,当你的启动优化遇到了瓶颈,是不可以再经过这三种方式突破的。chrome
并且只会基础的优化方式,并不会在履历中展示出优点。shell
因此今天说说在老三样的基础优化之上还有哪些可行的方案。json
具体步骤xcode
1)清空手机后台
2)在命令行执行
python $ANDROID_HOME/platform-tools/systrace/systrace.py gfx view wm am pm ss dalvik app sched -b 90960 -a 你的包名 -o test.log.html
复制代码
这一步须要你系统环境配置了 ANDROID_HOME 环境变量。
3)运行你的App,正常操做到你想测性能的地方,而后再命令行窗口中按 Enter 键中止收集
4)用 chrome(只支持此浏览器)打开生成的 test.log.html 结果文件,打开效果以下图:
目前须要关心地方就是咱们的应用进程相关的,也就是红框圈起来的地方。
图中的 F 表明绘制帧,黄色/红色表示该帧绘制超时,绿色表明绘制正常,也就是在16.6ms内绘制完一帧。
关于 systrace 如何使用能够参考这篇文章 性能工具Systrace
放大能够看到在启动过程当中,控件渲染耗时状况
因此能够很容易发现哪些启动过程当中没有用到的 UI 控件也被渲染了,这时就能够用 ViewStub 去替代。
可是如今能够看到的都是系统调用的耗时状况,由于谷歌预先在代码里关键的地方加上了监控,若是想要看到本身方法的耗时,
那须要手动在方法入口加上 Trace.beginSection("TAG")
在方法结束的地方加上Trace.endSection()
这样就能够在生成的结果中看到咱们自定义的 tag。
若是不少地方你都想加上监控,手动加是确定不合适的,这里推荐函数插桩方式自动加上监控代码,参考 systrace+函数插桩
这种方式不只能够帮助监控启动过程当中性能问题,再作卡顿优化的时候也能够用这种方式。
定位到了耗时方法,再作一些针对性的优化就相对容易了。
redex 是 Facebook 开源的一款字节码优化工具,目前只支持 mac 和 linux。
咱们用的是里面的 interdex 功能来重排列咱们 dex 中的 class 文件,那么为何重排列 class 文件能够优化启动速度?
简单的说,经过文件重排列的目的,就是将启动阶段须要用到的文件在 APK 文件中排布在一块儿,尽量的利用 Linux 文件系统的 pagecache 机制,用最少的磁盘 IO 次数,读取尽量多的启动阶段须要的文件,减小 IO 开销,从而达到提高启动性能的目的。
具体能够参考支付宝的这篇 《经过安装包重排布优化 Android 端启动性能》
因此咱们着手要作的就三件事:
1)安装配置 redex 2)得到启动过程当中 class 文件的加载顺序 3)根据这个顺序重排列 dex 中的 class 文件
具体步骤
1)下载 redex,配置环境(Mac OS)
git clone https://github.com/facebook/redex.git
xcode-select --install
brew install autoconf automake libtool python3
brew install boost jsoncpp
复制代码
2)编译安装 redex
cd redex
autoreconf -ivf && ./configure && make
sudo make install
复制代码
编译时间较久,不想干等着,能够加上 say 指令,编译完成后语音通知
autoreconf -ivf && ./configure && make && say '编译完成'
3)配置优化项
由于 redex 默认不开启 interdex,因此咱们要在配置文件中加上相应的配置,在 redex 文档中有说明
因此咱们打开配置文件
cd redex/config/
vi default.config
复制代码
按照下图配置
4)得到启动 class 加载顺序列表
这里按照 redex 提供的工具获取,可是须要手机有 root 权限
首先清空后台进程,而后打开你的应用
获取你的应用的 pid
adb shell ps | grep 你的应用包名
复制代码
收集堆内存,须要 root 权限
adb root
adb shell am dumpheap YOUR_PID /data/local/tmp/SOMEDUMP.hprof
复制代码
把堆内存文件拉取到电脑的某个位置
adb pull /data/local/tmp/SOMEDUMP.hprof YOUR_DIR_HERE/.
复制代码
经过 python 脚本解析堆内存,生成类加载顺序列表
python redex/tools/hprof/dump_classes_from_hprof.py --hprof YOUR_DIR_HERE/SOMEDUMP.hprof > list_of_classes.txt
复制代码
ps: 这个脚本支持 Python 2,执行过程当中若是遇到某个库没安装之类的,直接经过 pip install 缺失的库
就能够
5)经过 redex 处理
ANDROID_SDK=你的Android sdk路径 redex input.apk -o output.apk
复制代码
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
复制代码
这里咱们关注 TotalTime 就能够。
谷歌在 Android4.4(API 19)上也提供了测量方法,在 logcat 中过滤 Displayed 字段,
输出的值表示在启动过程和完成在屏幕上绘制相应 Activity 之间通过的时间,其实和上面的方式获得的结果是同样的。
关于 Android App 的冷启动过程和一些概念能够参考谷歌官方文档 「App startup time 」
因为一些缘由,还有一些优化方法没有实践,有兴趣的能够自行了解:
1)启动过程当中的 GC 优化,尽可能减小 GC 次数,避免大量或者频繁建立对象,如必须,可尝试放到 Native 实现
2)线程优化,尽量减小 cpu 调度,具体就是控制线程数量和调度
3)在类加载的过程当中经过 Hook 去掉类验证的过程,能够在 systrace 生成的文件中看到 verifyClass 过程,由于须要校验方法的每个指令,因此是一个比较耗时的操做
以上就是我关于 Android 冷启动优化的一些总结,水平有限,不免出现不许确的地方,欢迎指正。
本文首发于公众号「后知后jue」,微信搜索关注回复「1024」,你懂的!