Redex安卓Apk優化技術研究
Redex介紹
ReDex 是 非死book 開源的工具,通過對字節碼進行優化,以減小 Android Apk 大小,同時提高 App 啟動速度。
本次研究完成了Redex在Ubuntu linux上的安裝和配置,進行了Redex優化測試, 實驗了Redex優化的主要流程, 包括Inderdex。
Redex優化的基礎知識
可以先看看這幾篇文章:
- 基于 非死book Redex 實現 Android APK 的壓縮和優化
- 非死book 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/非死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