Android 項目開發填坑記 - 使用 MultiDex 解決 64K 限制
Android 的 classLoader 在加載 APK 的時候限制了 class.dex 包含的 Java 方法數,其總數不能超過65535(64K, 不要再說成 65K 了,1K = 2^10 = 1024 , 64 * 1024 = 65535),Google 官方給出的解決方案是使用 Multidex 。
啟用 Multidex
基本要求:
- 使用 Android Studio 開發工具
- Android SDK Build Tools >= 21.1
- 更新 Android Support Repository 到最新版本
配置步驟:
- 配置 Gradle build 來開啟 multidex
- 修改 manifest 來引用 MultiDexApplication 類
修改 module 下的 build.gradle 文件,添加支持庫并開啟 multidex:
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'
}
PS: compileSdkVersion、buildToolsVersion 根據實際項目配置,但是版本不能低于上述版本。
在 AndroidManifest.xml 中給 application 節點添加對 MultiDexApplication 類的引用:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxx">
<application
...
android:name="android.support.multidex.MultiDexApplication">
...
</application>
</manifest>
PS: manifest 節點的 package 屬性值根據實際項目有所不同。
注意:如果你的 APP 使用了繼承 Application 的類,你需要重寫 attachBaseContext() 方法并調用 MultiDex.install(this) 來啟用 multidex 。
public class XXX extends Application{
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
網上搜到還有一個方法:不繼承 Application ,而是直接繼承 MultiDexApplication 即可,這樣就不需要重寫 attachBaseContext() 方法了。
參考資料: Configure Apps with Over 64K Methods
可能遇到的問題
NoClassDefFoundError
Android SDK Build Tools 21.1 或者更高版本中的 Gradle Android 插件有對 multidex 的支持。這個插件使用 Proguard 來分析你的項目并在 [buildDir]/intermediates/multi-dex/[buildType]/maindexlist.txt 文件中生成一個 app 啟動 classes 的列表。但是這個列表并不是100%準確,可能會丟失一些app啟動所需的 classes 。
如果你在本地的測試機上沒有遇到這個問題,并不代表你的 APP 沒有問題,我通過查看友盟的崩潰記錄和使用一些真機測試平臺來進行檢查,通常情況下會有所發現。
解決方法:在 module 下創建 multidex.keep 文件,并在其中羅列出那些 class,以便讓編譯器知道在 main dex 文件中要保持哪些 class。
生成 multidex.keep 文件中的內容有多種:
方法一:修改 module 下的 build.gradle 文件
apply plugin: 'com.android.application'
android {
...
}
dependencies {
...
}
android.applicationVariants.all { variant ->
task "fix${variant.name.capitalize()}MainDexClassList" << {
logger.info "Fixing main dex keep file for $variant.name"
File keepFile = new File("$buildDir/intermediates/multi-dex/$variant.buildType.name/maindexlist.txt")
keepFile.withWriterAppend { w ->
// Get a reader for the input file
w.append('\n')
new File("${projectDir}/multidex.keep").withReader { r ->
// And write data from the input into the output
w << r << '\n'
}
logger.info "Updated main dex keep file for ${keepFile.getAbsolutePath()}\n$keepFile.text"
}
}
}
tasks.whenTaskAdded { task ->
android.applicationVariants.all { variant ->
if (task.name == "create${variant.name.capitalize()}MainDexClassList") {
task.finalizedBy "fix${variant.name.capitalize()}MainDexClassList"
}
}
}
方法二:修改 module 下的 build.gradle 文件
apply plugin: 'com.android.application'
android {
...
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex' // enable multidex
// optional
dx.additionalParameters += "--main-dex-list=$projectDir/class-list.txt".toString() // enable the main-dex-list
dx.additionalParameters += '--minimal-main-dex'
}
}
}
dependencies {
...
}
使用上述任意方式配置完成后, clean 然后 rebuild 項目,完成之后在 module 下的 build/intermediates/multi-dex/xxx 里找到 maindexlist.txt 文件(如果找不到相關目錄,可能需要你同步后 rebuild 項目才能生成),復制里面的內容到 module 根目錄下 multidex.keep 文件中(沒有則先創建此文件)。
然后,比較重要的一步就是:通過友盟、測試記錄、Bug記錄等獲取到 NoClassDefFoundError 錯誤對應的類,按照 maindexlist.txt 文件的方式添加這些類到 multidex.keep 文件中就可解決了。
其他錯誤和問題
比如 首次安裝啟動時黑屏沒有響應/ANR 、 安裝時異常 等,你可以參考文末的一些文章,此外你還可以參考 Android 必知必會-Android Splash 頁秒開之細節處理 來優化啟動體驗。
參考資料和推薦閱讀:
總結
這是一篇早就準備寫的文章,但當時搜集的資料未及時保存或者丟失,就拖到了現在。因為一個比較舊的 APP 也遇到了相關的問題,所以重新搜集了下資料整理發布出來了,希望能幫到遇到相關問題的朋友們。
PS:你可以通過下面的方式和我聯系
來自:http://likfe.com/2016/08/17/android-multiDex/