和大家分享下大型APP處理Multidex的方案。
65K問題的來源:
.Java文件編譯生成字節碼文件,然后打包生成.dex時會按照類,方法等進行分類,使用IndexMap結構處理。

所有的方法都存放在short[] 里, 而short的大小正是65K。
method的數量遠多於class的數量,method的數量會先到65K,class、field同樣存在65k的大小限制。
統計時是把系統的,第三方的加上自己的,方法數量的合,都放到這個數組里進行計算, frameworkMethodids + libraryMdthodids + mycodemethodids。
目前存在的解決方案:
坊間存在一種插件的解決方案:
把部分app的功能分離出來做成獨立的工程,並把這些功能打包生成apk,在把apk改成.png類型作為資源文件放到asset中,在中工程用到時加載這些“資源”並調用之中的方式,實現了減小主工程方法數、動態加載指定的類完成需要的功能。抽離出來功能作為插件減小了主工程方法數、也帶來了新工程的維護成本、和插件工程不能混淆的安全成本、而且跨2個功能的代碼維護會變得復雜、調試成本提高。
頂尖的Google提供了另外的一種Multidex方案,簡單高效:
1. 編譯配置文件build.gradle的 android{}集合中添加 multDexEnable true
2. 編譯配置文件build.gradle的dependencies{}集合中添加依賴庫 developCompile android.support:multidex:1.0
3. App啟動時,oncreate中添加MultiDex.install(context);
如果APP運行在ART虛擬機的設備上,首次啟動app加載時間,目前小於100ms,不存在加載慢的問題。ART的優化堪稱完美
如果運行在老舊的dalvik虛擬機上,手機app加載時間大約5-20s,會導致ANR。
解決ANR的方法:
總體思路是:把這個MultiDex.install(this)放到異步線程中加載,同時又要保證在用戶使用功能之前,異步線程已經把非主dex加載完成。
從編譯到運行起來,發生什么了呢?
1. 編譯生成allclasses.jar 生成classes.dex, classes2.dex, .... 多個。
安裝階段:把.dex 優化成classes.odex, 注意,只優化第一個dex文件。
首次運行:
1. dexpath BaseClassLoader.pathlist 類, 把所有的dex都加載。解決了為什么多個dex里的方法也能被正確的index到,不會導致no such method 的crash
2. odex, 這個過程是非常耗時的,如果第二個dex是5-7M的話大約要加載4s, google這個默認方式時間太長,會導致ANR。
這就需要完成2個事情, 1. 把啟動需要加載的類放到主dex中, 2. 顯示loading界面,保證用戶使用之前把dex加載完成。
主dex中的類:
把這個MultiDex.install(this)放到異步線程中加載,主dex中存放application啟動的1直接引用類,所有可能的2入口類,加上3第三方的調用。掃面這些類的直接引用類。統統放到主dex中。
首先在編譯打包階段,在build task中添加一層task, 添加Blacklist.xml自定義的黑名單, 這個名單中的方法對應的類需要添加到主dex中,保證主dex能夠加載APP啟動時需要的必要的類。
balcklist中包含了需要放到主dex中的類的path,addtask的這個task,把blacklist中的類放到了dextask中生成想要的dex。
如何確定哪些類需要放到blcaklist中:
粒度到方法層面,從主activity加載開始,加載了class B的public x 和public y, 則把B的x 和 y方法中,僅x, y方法中import到的類進行分析, 進行回溯添加。
那如何能讓工作更高效,能讓查找更有保證呢? 請看下篇,基於字節碼文件分析.class文件。
異步加載界面的現實時機:
splash界面,主界面,在收到事件之前進行監聽顯示load界面。直到異步加載完成所有dex。

其他問題:
主dex不夠65k方法,主dex會有多少方法? 生成dex時會一直塞直到塞滿65K。
JNI層的回調類,和用到反射的類是需要放到主dex中的,這些BI,庫加載都是需要運行就work的。
