Redex安卓Apk优化技术研究

Redex介绍

ReDex 是 Facebook 开源的工具,通过对字节码进行优化,以减小 Android Apk 大小,同时提高 App 启动速度。
GitHub:ReDex github,官网主页:fbredex.com

本次研究完成了Redex在Ubuntu linux上的安装和配置,以卡包App为例进行了Redex优化测试, 实验了Redex优化的主要流程, 包括Inderdex。

Redex优化的基础知识

可以先看看这几篇文章:
* 基于 Facebook Redex 实现 Android APK 的压缩和优化
* Facebook App 优化工具 ReDex 优化的 6 点及未优化的一大方面
* Optimizing Android bytecode with ReDex

Ubuntu上安装Redex

Redex目前支持Ubuntu Linux和Mac系统, 安装时需要编译源码,Ubuntu下面需要有sudo权限才能安装。
安装过程参考官方文档。

Ubuntu 14.04 LTS (64-bit)

sudo apt-get install \
    g++ \
    automake \
    autoconf \
    autoconf-archive \
    libtool \
    libboost-all-dev \
    liblz4-dev \
    liblzma-dev \
    make \
    zlib1g-dev \
    binutils-dev \
    libjemalloc-dev \
    libiberty-dev \
    libjsoncpp-dev

Download, Build and Install

Get ReDex from GitHub:

git clone https://github.com/facebook/redex.git
cd redex

Now, build ReDex using autoconf and make.

autoreconf -ivf && ./configure && make
sudo make install

然后就可以在命令行下运行Redex了

Redex Indexdex介绍

Interdex优化比较复杂,默认配置是不开启的,具体看Interdex文档
Interdex Pass 可以优化dex中class的顺序,以及class在不同的dex中的分布(如果是app使用了multidex)
按照class在实际运行中调用的顺序在dex中进行重新排序,可以带来几个好处:
- 更少的IO
- 更少的内存占用
- 更少page cache污染

Redex默认的配置文件是不包含Inderdex这一步的。增加Inderdex后的配置文件如下:

{
"redex" : {
"passes" : [
"ReBindRefsPass",
"BridgePass",
"SynthPass",
"FinalInlinePass",
"DelSuperPass",
"SingleImplPass",
"SimpleInlinePass",
"StaticReloPass",
"RemoveEmptyClassesPass",
"ShortenSrcStringsPass",
"InterDexPass"
],
"coldstart_classes":"app_list_of_classes.txt" //class调用顺序列表
}
}

生成输入数据

如何得到实际运行中class的调用顺序?

首先需要收集app的运行数据

按照典型使用场景操作app,获取heap dump文件, 使用redex提供的脚本redex/tools/hprof/dump_classes_from_hprof.py分析dump文件,得到class列表。
这里有个坑,首先是dump_classes_from_hprof.py在python2运行都有错误, Python2需要安装enum34后才能正常运行, 不兼容python3
在ubuntu上安装enum34后,用python2.7运行,可以得到class列表

具体操作过程如下

// get the process if of your app

adb shell ps | grep YOUR_APP_NAME | awk '{print $2}' > YOUR_PID ( if you don't have awk, the second value is the pid of your app)

// dump the heap of your app. You WILL NEED ROOT for this step

adb root
adb shell am dumpheap YOUR_PID /data/local/tmp/SOMEDUMP.hprof

// copy the heap to your host computer

adb pull /data/local/tmp/SOMEDUMP.hprof YOUR_DIR_HERE/.

// pass the heap dump to the python script for parsing and printing out the class list
// Note that the script needs python 2

YOUR_PYTHON_2_PATH redex/tools/hprof/dump_classes_from_hprof.py --hprof YOUR_DIR_HERE/SOMEDUMP.hprof > list_of_classes.txt

测量优化效果

主要是看app内存占用和 .dex mmap

adb shell ps | grep com.test.app | awk '{ print $2 }'
9003

[R:\AndroidM\packages\apps]$ adb shell dumpsys meminfo 9003
Applications Memory Usage (kB):
Uptime: 2329984 Realtime: 2329984

** MEMINFO in pid 9003 [com.test.app] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    10711    10044        0        0    44416    40455     3960
  Dalvik Heap     2201     2172        0        0    35719    33937     1782
 Dalvik Other     5424     4984        0        0                           
        Stack      516      516        0        0                           
       Ashmem        4        0        0        0                           
    Other dev        5        0        4        0                           
     .so mmap      967      152      148      360                           
    .apk mmap      271        0       56        0                           
    .ttf mmap        8        0        0        0                           
    .dex mmap     4531        8     4464        0                           
    .oat mmap     2274        0      776        0                           
    .art mmap     2761     1352     1020        0                           
   Other mmap       94        8        8        0                           
    GL mtrack     4196     4196        0        0                           
      Unknown      190      188        0        0                           
        TOTAL    34153    23620     6476      360    80135    74392     5742

 App Summary
                       Pss(KB)
                        ------
           Java Heap:     4544
         Native Heap:    10044
                Code:     5604
               Stack:      516
            Graphics:     4196
       Private Other:     5192
              System:     4057

               TOTAL:    34153      TOTAL SWAP (KB):      360

 Objects
               Views:       48         ViewRootImpl:        0
         AppContexts:        2           Activities:        1
              Assets:        3        AssetManagers:        2
       Local Binders:       12        Proxy Binders:       27
       Parcel memory:       13         Parcel count:       52
    Death Recipients:        0      OpenSSL Sockets:        0

 SQL
         MEMORY_USED:      663
  PAGECACHE_OVERFLOW:       88          MALLOC_SIZE:       62

 DATABASES
      pgsz     dbsz   Lookaside(b)          cache  Dbname
         4       68            512      225/36/22  /data/user/0/com.test.app/databases/MyTicket

App冷启动时间测试

我们常说的App冷启动,是指启动时你的应用程序的进程是没有创建的. 这也是大部分应用的使用场景.用户在桌面上点击你应用的 icon 之后,首先要创建进程,然后才启动 MainActivity.
这时候adb shell am start -W packagename/MainActivity 返回的结果,就是标准的应用程序的启动时间(注意 Android 5.0 之前的手机是没有 WaitTime 这个值的)
具体可以参考怎么计算apk的启动时间?
如果只关心某个应用自身启动耗时,参考TotalTime;如果关心系统启动应用耗时,参考WaitTime;如果关心应用有界面Activity启动耗时,参考ThisTime。

我编写了一个python脚本,可以自动进行多次冷启动,并画出启动时间统计图,计算平均启动时间。用这个脚本可以很方便的测量任意app的启动时间。
打开app,马上运行adb shell dumpsys activity top,可以看到app的包名和启动Activity, 测试脚本需要输入包名和启动activity的完整类名。

Redex优化效果分析

使用以前开发的App做测试,体积20M,使用了multidex。由于app有启动页,本身启动速度已经很快,1s多一点,因此优化效果不够明显。

优化前数据
['465', '1122', '1163']
[['444', '1123', '1149'], ['440', '1150', '1191'], ['410', '1450', '1520'], ['439', '1081', '1112'], ['419', '1072', '1117'], ['409', '1055', '1084'], ['423', '1101', '1135'], ['427', '1079', '1120'], ['465', '1122', '1163']]
apk launcher avarage times:[ThisTime, TotalTime, WaitTime]
[  430.66665649  1137.          1176.77783203]

redex优化后的数据,优化后TotalTime减少70ms
['453', '1129', '1159']
[['403', '1072', '1099'], ['411', '1077', '1123'], ['414', '1056', '1085'], ['383', '1031', '1077'], ['386', '1056', '1102'], ['381', '1030', '1071'], ['449', '1111', '1148'], ['390', '1095', '1127'], ['453', '1129', '1159']]
apk launcher avarage times:[ThisTime, TotalTime, WaitTime]
[  407.777771    1073.          1110.11108398]

结论

Redex可以在Proguard优化后再在dex层面进行优化,Redex需要配置Proguard配置文件来保护一些不应该被优化的类(如JNI调用、反射调用的类等)。
根据实际测试结果看Redex优化后可以提升冷启动速度10%左右,apk体积减少100k左右,低于Facebook给出的数据(25%)。原因可能是我测试的Apk比较简单,本身启动速度已经比较快,后面应该找启动速度慢的App进行优化测试。
普通App建议在做了Proguard优化后,再根据冷启动测试数据决定是否做Redex优化。

参考文章

ReDex github
Optimizing Android bytecode with ReDex
基于 Facebook Redex 实现 Android APK 的压缩和优化
Facebook App 优化工具 ReDex 优化的 6 点及未优化的一大方面

浅谈Android启动时间
怎么计算apk的启动时间?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

offbye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值