需要分Dex的理由想必大家都知道了.正是在ART以前的Android系統中,Dex文件對於方法索引是用一個short類型的數據來存放的.而short的最大值是65535,因此當項目足夠大包含方法數目足夠多超過了65535(包括引用的外部Lib里面的所有方法),當運行App,就會得到如下的錯誤提示.
Unable to execute dex: method ID not in [0, 0xffff]: 65536 Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
這個致命嚴重的Bug出現后,Android官方就寫了一篇著名的Blog(這篇文章我讀了五六遍,甚至連作者的Google+的照片我都看了兩三次,我還是沒有完全搞明白怎么Walkaround這個嚴重的問題).
再后來,慢慢發展,有很多能人異士發揮自己的創造力,寫了開源的Lib放在GitHub上分享給其他Android開發者們使用.HelloMultiDex, Secondary-Dex-Gradle等等.
但是解鈴還須系鈴人,這種系統性的嚴重致命Bug,當然還是Android官方給出解決方案最讓人放心.終於,我們還是等到了你,MultiDex Offical Solution.
我推薦官方文檔和這篇GitHub的文章一起看會有更好的了解,少走彎路.
下面說一說使用的步驟:
1. 修改Gradle,導入'com.android.support:multidex:1.0.0',打開multiDexEnabled;
android { compileSdkVersion 21 buildToolsVersion "21.1.0" defaultConfig { ... minSdkVersion 14 targetSdkVersion 21 ... // Enabling multidex support.
multiDexEnabled true } ... } dependencies { compile 'com.android.support:multidex:1.0.0' }
2.修改Application.兩種方法:
1) 直接把Application替換成MultiDexApplication
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.multidex.myapplication">
<application ... android:name="android.support.multidex.MultiDexApplication"> ... </application>
</manifest>
2) 在原來的Application中修改調用MultiDex.install(this);
public class HelloMultiDexApplication extends Application { @Override public void onCreate() { super.onCreate(); } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } }
到這里其實MultiDex的配置已經完成了.
但是,實際上下面介紹的3個問題也非常值得我們關注.
1. 一些在二級Dex加載之前,可能會被調用到的類(比如靜態變量的類),需要放在主Dex中.否則會ClassNotFoundError.
通過修改Gradle,可以顯式的把一些類放在Main Dex中.
afterEvaluate { tasks.matching { it.name.startsWith('dex') }.each { dx ->
if (dx.additionalParameters == null) { dx.additionalParameters = [] } dx.additionalParameters += '--multi-dex' dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString() } }
上面是修改后的Gradle,其中<filename>是一個文本文件的文件名,存放在和這個Gradle腳本同一級的文件目錄下.
而這個文本文件的內容如下.實際就是把需要放在Main Dex的類羅列出來.
android/support/multidex/BuildConfig/class android/support/multidex/MultiDex$V14/class android/support/multidex/MultiDex$V19/class android/support/multidex/MultiDex$V4/class android/support/multidex/MultiDex/class android/support/multidex/MultiDexApplication/class android/support/multidex/MultiDexExtractor$1/class android/support/multidex/MultiDexExtractor/class android/support/multidex/ZipUtil$CentralDirectory/class android/support/multidex/ZipUtil/class
2. 如果用使用其他Lib,要保證這些Lib沒有被preDex,否則可能會拋出下面的異常
UNEXPECTED TOP-LEVEL EXCEPTION: com.android.dex.DexException: Library dex files are not supported in multi-dex mode at com.android.dx.command.dexer.Main.runMultiDex(Main.java:337) at com.android.dx.command.dexer.Main.run(Main.java:243) at com.android.dx.command.dexer.Main.main(Main.java:214) at com.android.dx.command.Main.main(Main.java:106)
遇到這個異常,需要在Gradle中修改,讓它不要對Lib做preDexing
android { // ...
dexOptions { preDexLibraries = false } }
3. 如果每次都打開MultiDex編譯版本的話,會比平常用更多的時間(這個也容易理解,畢竟做了不少事情)
Android的官方文檔也給了我們一個小小的建議,利用Gradle建立兩個Flavor.一個minSdkVersion設置成21,這是用了ART支持的Dex格式,避免了MultiDex的開銷.而另外一個Flavor就是原本支持的最小sdkVersion.平時開發時候調試程序,就用前者的Flavor,發布版本打包就用后者的Flavor.
android { productFlavors { // Define separate dev and prod product flavors.
dev { // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin // to pre-dex each module and produce an APK that can be tested on // Android Lollipop without time consuming dex merging processes.
minSdkVersion 21 } prod { // The actual minSdkVersion for the application.
minSdkVersion 14 } } ... buildTypes { release { runProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile 'com.android.support:multidex:1.0.0' }
有了Android官方的支持后,MultiDex比最原始的解決方案簡單多了.媽媽再也不用擔心我們要分Dex啦!