Redex安卓Apk优化技术研究

jinbaobei 8年前
   <h2>Redex介绍</h2>    <p>ReDex 是 非死book 开源的工具,通过对字节码进行优化,以减小 Android Apk 大小,同时提高 App 启动速度。</p>    <p>本次研究完成了Redex在Ubuntu linux上的安装和配置,进行了Redex优化测试, 实验了Redex优化的主要流程, 包括Inderdex。</p>    <h2>Redex优化的基础知识</h2>    <p>可以先看看这几篇文章:</p>    <ul>     <li><a href="http://mp.weixin.qq.com/s?__biz=MzAwMTYwNzE2Mg==&mid=2651036594&idx=1&sn=b276c0f76cea713e5d568ab51e3f7f13&scene=0#wechat_redirect" rel="nofollow,noindex">基于 非死book Redex 实现 Android APK 的压缩和优化</a></li>     <li><a href="/misc/goto?guid=4959741696287164365" rel="nofollow,noindex">非死book App 优化工具 ReDex 优化的 6 点及未优化的一大方面</a></li>     <li><a href="/misc/goto?guid=4959741696374573219" rel="nofollow,noindex">Optimizing Android bytecode with ReDex</a></li>    </ul>    <h2>Ubuntu上安装Redex</h2>    <p>Redex目前支持Ubuntu Linux和Mac系统, 安装时需要编译源码,Ubuntu下面需要有sudo权限才能安装。</p>    <p>安装过程参考官方文档。</p>    <h3>Ubuntu 14.04 LTS (64-bit)</h3>    <pre>  <code class="language-java">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</code></pre>    <h3>Download, Build and Install</h3>    <p>Get ReDex from GitHub:</p>    <pre>  <code class="language-java">git clone https://github.com/非死book/redex.git  cd redex</code></pre>    <p>Now, build ReDex using autoconf and make.</p>    <pre>  <code class="language-java">autoreconf -ivf && ./configure && make  sudo make install</code></pre>    <p>然后就可以在命令行下运行Redex了</p>    <h2>Redex Indexdex介绍</h2>    <p>Interdex优化比较复杂,默认配置是不开启的,具体看 <a href="/misc/goto?guid=4959741696458422296" rel="nofollow,noindex">Interdex文档</a> 。</p>    <p>Interdex Pass 可以优化dex中class的顺序,以及class在不同的dex中的分布(如果是app使用了multidex)</p>    <p>按照class在实际运行中调用的顺序在dex中进行重新排序,可以带来几个好处:</p>    <ul>     <li>更少的IO</li>     <li>更少的内存占用</li>     <li>更少page cache污染</li>    </ul>    <p>Redex默认的配置文件是不包含Inderdex这一步的。增加Inderdex后的配置文件如下:</p>    <pre>  <code class="language-java">{     "redex" : {       "passes" : [         "ReBindRefsPass",         "BridgePass",         "SynthPass",         "FinalInlinePass",         "DelSuperPass",         "SingleImplPass",         "SimpleInlinePass",         "StaticReloPass",         "RemoveEmptyClassesPass",         "ShortenSrcStringsPass",         "InterDexPass"       ],      "coldstart_classes":"app_list_of_classes.txt" //class调用顺序列表     }   }</code></pre>    <h2>生成输入数据</h2>    <p>如何得到实际运行中class的调用顺序?</p>    <h3>首先需要收集app的运行数据</h3>    <p>按照典型使用场景操作app,获取heap dump文件, 使用redex提供的脚本 redex/tools/hprof/dump_classes_from_hprof.py 分析dump文件,得到class列表。</p>    <p>这里有个坑,首先是dump_classes_from_hprof.py在python2运行都有错误, Python2需要安装 <a href="/misc/goto?guid=4959741696541671064" rel="nofollow,noindex">enum34</a> 后才能正常运行, 不兼容python3</p>    <p>在ubuntu上安装enum34后,用python2.7运行,可以得到class列表</p>    <h3>具体操作过程如下</h3>    <p>// get the process if of your app</p>    <pre>  <code class="language-java">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)</code></pre>    <p>// dump the heap of your app. You WILL NEED ROOT for this step</p>    <pre>  <code class="language-java">adb root   adb shell am dumpheap YOUR_PID /data/local/tmp/SOMEDUMP.hprof</code></pre>    <p>// copy the heap to your host computer</p>    <pre>  <code class="language-java">adb pull /data/local/tmp/SOMEDUMP.hprof YOUR_DIR_HERE/.</code></pre>    <p>// pass the heap dump to the python script for parsing and printing out the class list</p>    <p>// Note that the script needs python 2</p>    <pre>  <code class="language-java">YOUR_PYTHON_2_PATH redex/tools/hprof/dump_classes_from_hprof.py --hprof YOUR_DIR_HERE/SOMEDUMP.hprof > list_of_classes.txt</code></pre>    <h3>测量优化效果</h3>    <p>主要是看app内存占用和 .dex mmap</p>    <pre>  <code class="language-java">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</code></pre>    <h2>App冷启动时间测试</h2>    <p>我们常说的App冷启动,是指启动时你的应用程序的进程是没有创建的. 这也是大部分应用的使用场景.用户在桌面上点击你应用的 icon 之后,首先要创建进程,然后才启动 MainActivity.</p>    <p>这时候 adb shell am start -W packagename/MainActivity 返回的结果,就是标准的应用程序的启动时间(注意 Android 5.0 之前的手机是没有 WaitTime 这个值的)</p>    <p>如果只关心某个应用自身启动耗时,参考TotalTime;如果关心系统启动应用耗时,参考WaitTime;如果关心应用有界面Activity启动耗时,参考ThisTime。</p>    <p>我编写了一个python脚本,可以自动进行多次冷启动,并画出启动时间统计图,计算平均启动时间。用这个脚本可以很方便的测量任意app的启动时间。</p>    <p>打开app,马上运行 adb shell dumpsys activity top ,可以看到app的包名和启动Activity, 测试脚本需要输入包名和启动activity的完整类名。</p>    <h3>Redex优化效果分析</h3>    <p>使用以前开发的App做测试,体积20M,使用了multidex。由于app有启动页,本身启动速度已经很快,1s多一点,因此优化效果不够明显。</p>    <pre>  <code class="language-java">优化前数据  ['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]</code></pre>    <h2>结论</h2>    <p>Redex可以在Proguard优化后再在dex层面进行优化,Redex需要配置Proguard配置文件来保护一些不应该被优化的类(如JNI调用、反射调用的类等)。</p>    <p>根据实际测试结果看Redex优化后可以提升冷启动速度10%左右,apk体积减少100k左右,低于非死book给出的数据(25%)。原因可能是我测试的Apk比较简单,本身启动速度已经比较快,后面应该找启动速度慢的App进行优化测试。</p>    <p>普通App建议在做了Proguard优化后,再根据冷启动测试数据决定是否做Redex优化。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/57d0d527345e</p>    <p> </p>