避免啟動時載入多dex
如果你是一個Android開發者,你可能知道臭名昭著的dex方法個數限制的存在。如果你不知道我在說什麼,請看看網上其他的博文。 Sebastiano Gottardo 的《[DEX] 天空是極限? 不, 65K方法個數限制才是》或者 Matthias Käppler 的《“恭喜,你有許多程式碼了!” 彌補Android的方法個數限制》都是很好的博文。
理解你的處境
首先要了解你的程式碼,什麼增加了方法個數?
使用mihaip 的 dex-method-count 是一個檢查庫增加了多少方法的簡單手段。警告:這個庫會計算每個包。與有其他依賴或者不同包的庫一起使用時要小心。
其次要問問自己:你維護的程式碼有多少?
首先我們注意到依賴關係遍佈了所有的方法。支援庫和Google Play服務的方法個數都是巨大的。經過調研,我們發現我們的方法個數是30k,剩下都是第三方庫。
超過 65k 是否有意義?
去年Google IO 大會後,我有機會與在SoundCloud工作的 Fernando Cejas 討論這個課題。他們的情況是,有一個本地元件,該元件有JNI介面,增加了很多方法。沒有辦法,他們的主用例有大量的方法,不得不使用MultiDex機制。
我們的情況不同,我們的應用不大,只是充斥了第三方庫。
MultiDex有多糟?
Android版本低於棒棒糖(Lollipop)的使用者,MultiDex會增加啟動時間。我們決定用產品做一個小的AB測試,在應用啟動時人為增加延遲。
在測試了不同值之後,我們得出了結論。如大家所料,延長啟動對互動有顯著的負面影響。我不能分享這些數字,但是請相信我。在晚上構建時,測試你的啟動效能,並確保啟動良好。
延遲載入Dex檔案
如果你檢查MultiDex庫原始碼,它只修改了類載入器,以便在應用啟動時載入額外的dex檔案。問題是,為什麼要在啟動時載入?如果在啟動後載入dex檔案會怎麼樣?
瀏覽你應用中的依賴關係,是否這些依賴是最常見用例的一部分?在我們的應用中,答案是no。給你舉些例子,比如支付型別的開發庫:PayPal、Google Wallet、Bitcoin等等。你的使用者會所有都用到嗎?表單助手:Date pickers、 credit card scanners。只有在使用者註冊時才會使用一次。
在啟動時載入所有庫有意義嗎?
讓我們來看看程式碼
最近,我有空寫了一個例子應用。程式碼在這裡。
該應用延遲載入Picasso庫。啟動後,當使用者希望顯示一個圖片時,應用首先載入Picasso庫。我們從不延遲載入Picasso庫,因為他是我們最常用用例中的一部分,但是因為Picasso是開源的,很容易寫出一個例子。
這是怎麼工作的?
首先把要延遲載入的jar檔案轉換為dex檔案。可以使用dx工具。在我的電腦裡,它在/adt-bundle-mac-x86_64/sdk/build-tools/22.0.1/dx:
1 |
dx --dex --output=/path/to/the/library.dex /path/to/the/library.jar |
生成dex檔案後,將它放到assets資料夾
為了從程式碼中使用依賴,你需要將依賴標記為provided。
1 2 3 |
dependencies { provided 'com.squareup.picasso:picasso:2.5.2' } |
帶provided的依賴庫會在最終生成的dex檔案中剔除。你可以測試下方法個數,帶有compile標記的(1024個方法)和帶provided標記的(214個方法)。
使用provided的依賴有點棘手。你需要保證知道模組載入完,不會使用依賴。程式碼裡,我將所有Picasso呼叫封裝到一個單獨類。老實說,我沒有花費很多心思去找到一種更優雅的方式。根據使用庫的方式,在模組載入時,你可能想增加一個activity進行過渡。
程式碼的效果是顯而易見的。當你希望載入predexed檔案,需要把它從assets資料夾移動到應用指定的快取目錄。一旦移動完成,它呼叫一個MultiDex庫中installSecondaryDexes()方法的修改版。我把MultiDex庫原始碼增加到了工程裡。你可以做一個diff,我只把方法的訪問許可權改成了public。只要predexed庫載入完畢,你就可以使用Picasso庫了。
我不記得做了其他事情。程式碼遠遠不夠完善,請注意程式碼周圍不同的TODO事項。我增加了一些註釋來解釋我走的那些捷徑。
需要調查的事情:修復、測試等等
低版本測試
我在少量裝置上進行了測試。很高興知道能否在全部API級別上工作。
aar 的解決方案
aar可能含有資源,所以上面的方法不適用。
測試增加多個依賴
我從沒有試著載入多個。AFAIK、MultiDex支援多個依賴,所以它應該可以工作。
在產品中測試
我們還沒有在產品中測試過:(
混淆
就像bubbleguuum在這篇博文中提到的:
你不能混淆任何從附加dex中的類或介面繼承來的類和介面(在你的例子中,繼承了Picasso中的類和介面)
結論
使用MultiDex不是一個輕鬆的決定。延遲載入Dex只是一種 hack,以延長方法個數少於65k的時間,直到更多的使用者使用升級到比棒棒糖更新的版本。