Redex安卓Apk優化技術研究

jinbaobei 7年前發布 | 7K 次閱讀 ReDex Android開發 移動開發

Redex介紹

ReDex 是 非死book 開源的工具,通過對字節碼進行優化,以減小 Android Apk 大小,同時提高 App 啟動速度。

本次研究完成了Redex在Ubuntu linux上的安裝和配置,進行了Redex優化測試, 實驗了Redex優化的主要流程, 包括Inderdex。

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/非死book/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</code></pre>

App冷啟動時間測試

我們常說的App冷啟動,是指啟動時你的應用程序的進程是沒有創建的. 這也是大部分應用的使用場景.用戶在桌面上點擊你應用的 icon 之后,首先要創建進程,然后才啟動 MainActivity.

這時候 adb shell am start -W packagename/MainActivity 返回的結果,就是標準的應用程序的啟動時間(注意 Android 5.0 之前的手機是沒有 WaitTime 這個值的)

如果只關心某個應用自身啟動耗時,參考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]</code></pre>

結論

Redex可以在Proguard優化后再在dex層面進行優化,Redex需要配置Proguard配置文件來保護一些不應該被優化的類(如JNI調用、反射調用的類等)。

根據實際測試結果看Redex優化后可以提升冷啟動速度10%左右,apk體積減少100k左右,低于非死book給出的數據(25%)。原因可能是我測試的Apk比較簡單,本身啟動速度已經比較快,后面應該找啟動速度慢的App進行優化測試。

普通App建議在做了Proguard優化后,再根據冷啟動測試數據決定是否做Redex優化。

 

來自:http://www.jianshu.com/p/57d0d527345e

 

 本文由用戶 jinbaobei 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!