關於 Android Dex 方法限制的一些總結
Android的編譯過程
在瞭解這個問題之前我們先要來看看Android 應用編譯的過程:
- IDE中的資源打包工具 (Android Asset Packaging Tool ,即圖中的aapt) 會將應用中的資源檔進行編譯,這些資源檔包括
AndroidManifest.xml檔案,為Activity定義的 XML 檔案等等。在這個編譯過程中也會產生一個 R.java 檔案,這樣你就可以在你的Java代碼中引用這些資源了。
- aidl 工具會將你項目中的所有
.aidl 介面轉換成Java介面。
- 項目中的所有的Java代碼,包括
R.java 和 .aidl 檔案,都會被Java編譯器編譯,然後輸出 .class 檔案。
- 接著 dex 工具就會把上一步驟產生的 .class 檔案轉成 Dalvik 位元組碼,也就是
.dex 檔案。同時項目中包含的所有第三方類庫和 .class 檔案也會被轉換成 .dex 檔案,這樣講方便下一步被打包成最終的 .apk 檔案。
- 所有的不能編譯的資源(比片等等)、編譯後的資源檔和 .dex 檔案會被 apkbuilder 工具打包成一個
.apk 檔案。
- 一旦
.apk 檔案被構建好之後,如果要把把它安裝到裝置上面去的話,它就必須用一個debug 或者發行key來對這個apk檔案簽名。
- 最後,如果應用程式已經被簽名成為發行模式的apk,你還需要使用 aipalign工具對
.apk 進行對齊最佳化。這樣的話可以減少應用程式在裝置上的記憶體消耗。
為什麼會有這個Dex 方法限制內部原因:我們注意到在第四步的時候,會產生一個.dex 檔案。Android 從之前的Dalvik 到現在Android 5.0 預設的ART 運行時環境都能夠執行這個.dex 檔案,它們還使用同一套指令集,即Dalvik 指令集。通過這篇關於Android 指令集格式的介紹文章中,我可以知道Dalvik 指令集是使用16位寄存器來儲存項目中所有的方法引用,包括第三方的方法:
invoke-kind {vC, vD, vE, vF, vG}, meth@BBBBB: method reference index (16 bits)
這就意味著 Android的單個.dex 檔案最能引用65536個方法,在這之後的方法就無法引用了。這就是Android Dex 方法限制異常出現的原因,同時因為ART和Dalvik使用同一套指令集,這個限制在ART 運行時環境中也會存在。
外部原因:第三方庫裡麵包含太多的方法。這裡就拿Google Play Service和Guava來舉例。很多Android開發人員都會用到Google Play Service庫和Guava庫,而你知道它們提供了多少了方法嗎?Google Play Service 5.0裡面就差不多包含了將近20k+方法,Guava提供了將近14k個方法。這個兩個庫就將近佔了方法限制數目65536的半壁江山。
那麼如何解決Android Dex 方法限制這個問題呢?老方法對於內部原因:
- 從上面的描述中我們知道,Android Dex 方法限制是出現在單個
.dex 檔案中的,那麼我們可以在一個apk中使用多個.dex 檔案嗎?可以,Android 官方部落格就給出了這個方案。(在Android5.0之前,由於大部分使用的是Dalvik 運行時環境,Dalvik 運行時環境限制一個apk只能包含一個classes.dex檔案。)對於外部原因:
- 使用配置指令碼對第三方庫中的方法進行清除;
- 使用ProGuard清除項目中無用的方法,不過效果不如上面的。
新方法(官方動作)主要思路:使用multidex support library 讓Android5.0之前的版本也能在一個apk裡麵包含多個.dex 檔案。具體使用方法請參看這篇文章。
Google不僅在工具上面做出了改進,還把自己的Google Play Service庫也做了一番改動——從Google Play Service 6.5開始開始支援更細精度的依賴管理,也就是說你只需要Google Drive的api,而不需要google game,maps或者wallet等api的支援,那你就可以只引入Google Drive的api即可。這樣可以在很大程度上減少Dex 方法限制出現的幾率