Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 分析Dalvik字節碼進行減包優化

分析Dalvik字節碼進行減包優化

編輯:關於Android編程

Android結合版最近幾個版本在包大小配額上超標了,先後采用了包括圖片壓縮,功能H5,無用代碼移除等手段減包,還是有著很大的減包壓力。組內希望我能從代碼的角度減少一些包大小,感覺有點壓力山大。經過一段時間對手q安裝包反編譯後的Dalvik字節碼的分析,發現通過調整Java代碼可以減少編譯後的Dalvik字節碼,從而減少包大小。在這方面我做了許多的嘗試,有成功有失敗,拿出來給大家分享分享,多拍磚多交流。

優化思路

通過dexdump反編譯apk中的dex,得到對應Dalvik字節碼,找到尋找冗余的字節碼,嘗試去除或替換冗余的字節碼

目前主要是替換或去除原有的java代碼,減少對應的Dalvik指令,從而減少安裝包大小。

現在主要是從Dalvik字節碼分析來調整Java代碼,之後希望能夠通過ASM等框架直接調整字節碼減少現在的包大小。

優化效果

去除初始化賦值方案————減少整個手q的發布包大小80k左右。

插樁函數優化———減少整個手q的發布包大小2k左右。

其它嘗試方案,包括字符串拼接、移除interface很多空方法等,因為效果比較小、難以統一修改等問題,只是列舉下分析結果,大家如果項目中出現的量比較多也是可以嘗試去優化的。

優化方案如下:

1、去除初始化賦值冗余

1.1、問題分析:

靜態變量為類的所有對象共享,在類加載的准備階段就會初始設置為系統零值(如下圖),比如String被設置初始值為null,而在類中存在

\

這樣的賦值行為會在之後的()類構造器方法中執行,重復設置StringA為null,增加了對應的()方法的Dalvik指令,沒有必要,可以干掉。

成員變量在對象創建內存分配完成後,對應的內存空間會被初始設置為系統零值(和靜態變量一樣),比如int類型被設置為0,而在類中存在

publicintB=0;

這樣的賦值行為會在之後的()對象構造方法中執行,重復設置intB為0,增加了對應的方法中的Dalvik指令,沒有必要,可以干掉。

\ 對於初始化賦值為系統分配默認零值的靜態變量和成員變量,去掉初始化賦值,直接使用系統賦的系統零值,可以減少中的Dalvik指令,從而減少包大小,而且可以提高類加載和對象創建的效率。

\

1.2、優化要點

注意對於staticfinal的變量必須賦初值;

interface的變量都是staticfinal類型的;

注意只有賦值為系統賦予的零值的靜態變量和成員變量才能按照這種方式優化,其它比如局部變量的改動會導致編譯不通過等問題。

1.3、冗余示例:

優化前:

\ 對應字節碼: \ 優化後:

\

對應字節碼:

\ 減少了兩行Dalvik指令的執行,最後分析結果平均優化一處可以減少安裝包8個字節左右。

1.4、優化結果:

目前在手Q6.3.0分支上利用自行寫的過濾腳本(可以私下找我要對應的優化腳本用於對應的工程)可以看到優化的效果,如果對整個手q執行這個方案,預計能夠優化80k左右,修改了4677個文件,修改了17164處冗余。

2、調整插樁對應的代碼

Qzone補丁包引入了插樁這一步,需要在所有qzone類的構造函數中加入對mqq.app.MobileQQ類的引用。
優化的方案是將插樁插入到對象構造函數中的語句由

\ 改為 \ 以Qzone某個類的為例,由原本的字節碼 \ 變成了 \ 這裡替換一處代碼,將System.out.print改成getName,可以減少對象構造函數的一行Dalvik指令,替換了1314處初始化函數中插入的代碼,最終將對應的qzone_plugin.apk減少了2459字節,整個手q減少2457字節左右。一行代碼,2k收益,其實還是很劃算的。

3、字符串拼接

下面是我針對String拼接的特殊情況“變量+”””和“””+變量”的不同形式舉例分析Dalvik字節碼。

\ \ 字節碼

\

從示例中可以看出各類字符串拼接方式的優劣,如果用String.valueOf()絕對是最優方案。只是通過對“變量+”””和“””+變量”的形式在手q整個項目調整以後大概能夠優化6k左右,如果只是優化qzone部分,效果比較微小,腳本方面不太好過濾對應情況,暫時沒有加入,只是做了下試驗。
PS:其實“String+”一般來說比StringBuffer的拼接更費字節碼,這個部分可以自行驗證,前提是a+b+…的形式中首位a這個為變量,而不是常量,如果a是常量,則實際上和StringBuffer等同,這也是個優化點,具體可以參考文章從字節碼視角看java字符串的拼接。

4、調整interface到class,減少實現接口造成的空方法

很多代碼中實現接口時有很多的空方法,並沒有作用但還是會占用字節碼,希望能夠通過調整對應的interface為class,去除冗余的空方法,減少字節碼,從而減少包大小。
示例如下:

\ 改成 \
該方案的缺點在於修改必須手動,難度大,qzone中場景不足以引起量變,而且因為Qzone中中還加入了插樁函數的負擔,所以整體優化效果不佳,優化完qzone才2k不到的大小縮減,優化難度高收益小,棄坑。

這些減包思路希望能夠給一起在減包路上踩坑的朋友們一些幫助吧。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved