延遲載入 Dex 檔案

lihenair發表於2015-10-23

避免啟動時載入多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:

生成dex檔案後,將它放到assets資料夾

為了從程式碼中使用依賴,你需要將依賴標記為provided

帶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的時間,直到更多的使用者使用升級到比棒棒糖更新的版本。

相關文章