都9102年了,Android 冷启动优化还是只会老三样吗

图:Benjamin Suter

性能优化一直都是一个 Android 开发者逃不过的话题,启动优化则更是重中之重。启动速度可以直接影响一个 App 的留存率和转化率,没有人会希望自己点击之后还要等一会才打开。

但是当我一番调研后发现,网上大部分启动优化相关的文章,套路都差不多,我称之为老三样。

什么是老三样?

  1. 将启动页主题背景设置成闪屏页图片

这么做的目的主要是为了消除启动时的黑白屏,给用户一种秒响应的感觉,但是并不会真正减少用户启动时间,仅属于视觉优化。

  1. 主页面布局优化

1)通过减少冗余或者嵌套布局来降低视图层次结构
2)用 ViewStub 替代在启动过程中不需要显示的 UI 控件

  1. Application 和 主 Activity 的 onCreate 中异步初始化某些代码

因为在主线程上进行资源初始化会降低启动速度,所以可以将不必要的资源初始化延迟,达到优化的效果。但是这里要注意懒加载集中化的问题,别用户启动时间快了,但是无法在界面上操作就尴尬了。

老三样并不说是不管用或者过时了,只是这三种优化方式都是非常基础的方式,当你的启动优化遇到了瓶颈,是不能够再通过这三种方式突破的。

而且只会基础的优化方式,并不会在履历中展现出优势。

所以今天说说在老三样的基础优化之上还有哪些可行的方案。


进阶方案一 通过 systrace 查找耗时代码

具体步骤

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 重排列 class 文件

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


  • 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 过程,因为需要校验方法的每一个指令,所以是一个比较耗时的操作

以上就是我关于 Android 冷启动优化的一些总结,水平有限,难免出现不准确的地方,欢迎指正​。​

本文首发于公众号「后知后jue」,微信搜索关注回复「1024」,你懂的!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容