前言
起因:項(xiàng)目使用的一直是multidex:1.0.3版本就想著版本低了要不要升級(jí)一下。驚喜就這么來(lái)了。
65536
當(dāng)你的應(yīng)用及其引用的庫(kù)超過(guò) 65,536 個(gè)方法時(shí),你會(huì)遇到構(gòu)建錯(cuò)誤,表明你的應(yīng)用已達(dá)到 Android 構(gòu)建架構(gòu)的限制:
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
舊版本的構(gòu)建系統(tǒng)報(bào)告了不同的錯(cuò)誤,這表明存在相同的問(wèn)題:
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
這兩種錯(cuò)誤情況都顯示一個(gè)共同的數(shù)字:65536。這個(gè)數(shù)字表示單個(gè) Dalvik 可執(zhí)行文件 (DEX) 字節(jié)碼文件中的代碼可以調(diào)用的引用總數(shù)。本問(wèn)介紹了如何通過(guò)啟用稱(chēng)為multidex的應(yīng)用程序配置來(lái)克服此限制,該配置允許你的應(yīng)用程序構(gòu)建和讀取多個(gè) DEX 文件。
關(guān)于 64K 參考限制
Android 應(yīng)用 APK 文件包含 Dalvik 可執(zhí)行文件 DEX 文件形式的可執(zhí)行字節(jié)碼文件,其中包含用于運(yùn)行應(yīng)用的編譯代碼。Dalvik Executable 規(guī)范將單個(gè) DEX 文件中可以引用的方法總數(shù)限制為 65,536,包括 Android 框架方法、庫(kù)方法和你自己代碼中的方法。在計(jì)算機(jī)科學(xué)的上下文中,術(shù)語(yǔ)Kilo, K表示 1024(或 2^10)。由于 65,536 等于 64 X 1024,因此此限制稱(chēng)為64K 參考限制。
解決64K限制
對(duì) Android 5.0 及更高版本的 Multidex 支持
Android 5.0(API 級(jí)別 21)及更高版本使用稱(chēng)為 ART 的運(yùn)行時(shí),該運(yùn)行時(shí)本機(jī)支持從 APK 文件加載多個(gè) DEX 文件。 ART 在應(yīng)用安裝時(shí)執(zhí)行預(yù)編譯,它會(huì)掃描 classesN.dex 文件并將它們編譯成單個(gè) .oat 文件以供 Android 設(shè)備執(zhí)行。 因此,如果你的 minSdkVersion 為 21 或更高,則默認(rèn)啟用 multidex,并且你不需要 multidex 庫(kù)。
注意: 當(dāng)使用 Android Studio 運(yùn)行你的應(yīng)用程序時(shí),構(gòu)建會(huì)針對(duì)你部署到的目標(biāo)設(shè)備進(jìn)行優(yōu)化。 這包括在目標(biāo)設(shè)備運(yùn)行 Android 5.0 及更高版本時(shí)啟用 multidex。 由于此優(yōu)化僅在使用 Android Studio 部署應(yīng)用程序時(shí)應(yīng)用,因此你可能仍需要為 multidex 配置發(fā)布版本以避免 64K 限制。
看到?jīng)],這里最好的解決辦法就是設(shè)置minSdkVersion設(shè)置為 21 或更高。這樣網(wǎng)上的一些什么
- multidex你遇到的坑
- multidex與你不得不說(shuō)的秘密
- multidex原理及實(shí)現(xiàn)就和你沒(méi)得關(guān)系了,當(dāng)然你想了解也可以。
Android SDK 為 21 或更高的問(wèn)題解決了,那Android SDK 低于 21 的呢。咱接著往下看嘍。
Android 5.0 之前的 Multidex 支持
為你的應(yīng)用程序配置 multidex
如果你的 minSdkVersion 設(shè)置為 21 或更高,則默認(rèn)啟用 multidex,你不需要 multidex 庫(kù)。
但是,如果你的 minSdkVersion 設(shè)置為 20 或更低,那么你必須使用 multidex 庫(kù)并對(duì)你的應(yīng)用項(xiàng)目進(jìn)行以下修改:
1.修改模塊級(jí) build.gradle 文件以啟用 multidex 并添加 multidex 庫(kù)作為依賴(lài)項(xiàng),如下所示:
- 使用AndroidX
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 30
multiDexEnabled true
}
...
}
dependencies {
implementation "androidx.multidex:multidex:2.0.1"
}
- 不使用AndroidX(已棄用)
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 30
multiDexEnabled true
}
...
}
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
2.根據(jù)你是否覆蓋 Application 類(lèi),執(zhí)行以下操作之一:
- 如果你不覆蓋 Application 類(lèi),請(qǐng)編輯你的清單文件以在 < application > 標(biāo)記中設(shè)置 android:name,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.scc.demo">
<application
android:name="androidx.multidex.MultiDexApplication" >
...
</application>
</manifest>
- 如果你確實(shí)覆蓋了 Application 類(lèi),請(qǐng)將其更改為擴(kuò)展 MultiDexApplication(非必須),如下所示:
public class MyApplication extends MultiDexApplication { ... }
- 或者,如果你確實(shí)覆蓋了 Application 類(lèi)但無(wú)法更改基類(lèi),那么你可以覆蓋 attachBaseContext() 方法和 callMultiDex.install(this) 以啟用 multidex:
public class MyApp extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
注意: 在 MultiDex.install() 完成之前,不要通過(guò)反射或 JNI 執(zhí)行 MultiDex.install() 或任何其他代碼。 Multidex 跟蹤不會(huì)跟隨這些調(diào)用,導(dǎo)致 ClassNotFoundException 或由于 DEX 文件之間的類(lèi)分區(qū)錯(cuò)誤而導(dǎo)致驗(yàn)證錯(cuò)誤。
現(xiàn)在,當(dāng)你構(gòu)建應(yīng)用程序時(shí),Android 構(gòu)建工具會(huì)根據(jù)需要構(gòu)建一個(gè)主要的 DEX 文件 (classes.dex) 和支持的 DEX 文件(classes2.dex、classes3.dex 等)。構(gòu)建系統(tǒng)然后將所有 DEX 文件打包到你的 APK 中。
在運(yùn)行時(shí),multidex API 使用特殊的類(lèi)加載器來(lái)搜索所有可用的 DEX 文件以查找你的方法(而不是僅在主 classes.dex 文件中搜索)。
multidex 庫(kù)的限制
multidex 庫(kù)有一些已知的限制,當(dāng)你將其合并到應(yīng)用程序構(gòu)建配置中時(shí),你應(yīng)該注意并測(cè)試這些限制:
- 在啟動(dòng)期間將 DEX 文件安裝到設(shè)備的數(shù)據(jù)分區(qū)上很復(fù)雜,如果輔助 DEX 文件很大,可能會(huì)導(dǎo)致應(yīng)用程序無(wú)響應(yīng) (ANR) 錯(cuò)誤。為避免此問(wèn)題,請(qǐng)啟用代碼收縮以最小化 DEX 文件的大小并刪除未使用的代碼部分。
- 在 Android 5.0(API 級(jí)別 21)之前的版本上運(yùn)行時(shí),使用 multidex 不足以解決 linearalloc 限制(問(wèn)題 78035)。此限制在 Android 4.0(API 級(jí)別 14)中有所增加,但這并沒(méi)有完全解決。在低于 Android 4.0 的版本上,你可能會(huì)在達(dá)到 DEX 索引限制之前達(dá)到線(xiàn)性分配限制。因此,如果你的目標(biāo) API 級(jí)別低于 14,請(qǐng)?jiān)谄脚_(tái)的這些版本上進(jìn)行徹底測(cè)試*,因?yàn)槟愕膽?yīng)用程序可能在啟動(dòng)時(shí)或加載特定類(lèi)組時(shí)出現(xiàn)問(wèn)題。
代碼縮減可以減少或可能消除這些問(wèn)題。
在主 DEX 文件中聲明所需的類(lèi)
如果你收到 java.lang.NoClassDefFoundError,那么你必須通過(guò)在構(gòu)建類(lèi)型中使用 multiDexKeepFile 或 multiDexKeepProguard 屬性聲明它們,根據(jù)主 DEX 文件中的要求手動(dòng)指定這些附加類(lèi)。 如果某個(gè)類(lèi)在 multiDexKeepFile 或 multiDexKeepProguard 文件中匹配,則該類(lèi)將添加到主 DEX 文件中。
multiDexKeepFile 屬性
你在multiDexKeepFile其中指定的文件應(yīng)每行包含一個(gè)類(lèi),格式為com/example/MyClass.class. 例如,你可以創(chuàng)建一個(gè)如下所示的文件multidex-config.txt:
com/scc/MyClass.class
com/scc/MyOtherClass.class
然后,你可以為構(gòu)建類(lèi)型聲明該文件,如下所示:
android {
buildTypes {
release {
multiDexKeepFile file('multidex-config.txt')
...
}
}
}
注意: Gradle 讀取相對(duì)于build.gradle文件的路徑,因此如果multidex-config.txt與build.gradle文件位于同一目錄中,則上述示例有效。
multiDexKeepProguard 屬性
該multiDexKeepProguard文件使用與 Proguard 相同的格式,并支持整個(gè) Proguard 語(yǔ)法。
你在multiDexKeepProguard其中指定的文件應(yīng)包含 -keep 任何有效 ProGuard 語(yǔ)法中的選項(xiàng)。例如, -keep com.scc.MyClass.class。你可以創(chuàng)建一個(gè)名為的文件 multidex-config.pro,如下所示:
-keep class com.scc.MyClass
-keep class com.scc.MyClassToo
如果要指定包中的所有類(lèi),文件如下所示:
-keep class com.scc.** { *; } // com.scc 包中的所有類(lèi)
然后,你可以為構(gòu)建類(lèi)型聲明該文件,如下所示:
android {
buildTypes {
release {
multiDexKeepProguard file('multidex-config.pro')
...
}
}
}
在開(kāi)發(fā)版本中優(yōu)化 multidex
為了減少更長(zhǎng)的增量構(gòu)建時(shí)間,使用 pre-dexing在構(gòu)建之間重用 multidex 輸出。Pre-dexing 依賴(lài)于僅在 Android 5.0(API 級(jí)別 21)及更高版本上可用的 ART 格式。如果你使用的是 Android Studio 2.3 及更高版本,則在將你的應(yīng)用程序部署到運(yùn)行 Android 5.0(API 級(jí)別 21)或更高版本的設(shè)備時(shí),IDE 會(huì)自動(dòng)使用此功能。
注意: 適用于 Gradle 3.0.0及更高版本的Android 插件包括進(jìn)一步改進(jìn)以?xún)?yōu)化構(gòu)建速度,例如按類(lèi)進(jìn)行 dexing(以便僅對(duì)你修改的類(lèi)進(jìn)行重新索引)。一般來(lái)說(shuō),為了獲得最佳的開(kāi)發(fā)體驗(yàn),你應(yīng)該始終升級(jí)到 最新版本的 Android Studio和 Android 插件。
但是,如果你從命令行運(yùn)行 Gradle 構(gòu)建,則需要將 minSdkVersion 設(shè)置為 21 或更高以啟用 pre-dexing。保留生產(chǎn)版本設(shè)置的一個(gè)有用策略是使用產(chǎn)品風(fēng)格創(chuàng)建兩個(gè)版本的應(yīng)用程序 :開(kāi)發(fā)風(fēng)格和發(fā)布風(fēng)格,具有不同的值minSdkVersion,如下所示。
android {
defaultConfig {
...
multiDexEnabled true
//最低 API 級(jí)別。
minSdkVersion 15
}
productFlavors {
dev {
//(API 級(jí)別 21)或更高 .
minSdkVersion 21
}
prod {
// 如果你已經(jīng)為生產(chǎn)版本配置了 defaultConfig 塊
// 你的應(yīng)用程序,你可以將此塊留空,Gradle 會(huì)使用其中的配置
// 代替 defaultConfig 塊。 你仍然需要包括這種味道。
// 否則,所有變體都使用“dev”配置。
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
implementation "androidx.multidex:multidex:2.0.1"
}
避免 64K 限制
在配置應(yīng)用以啟用 64K 或更多方法引用之前,你應(yīng)該采取措施減少應(yīng)用代碼調(diào)用的引用總數(shù),包括應(yīng)用代碼定義的方法或包含的庫(kù)。以下策略可以幫助你避免達(dá)到 DEX 參考限制:
- 檢查你的應(yīng)用程序的直接和傳遞依賴(lài)項(xiàng) - 確保你在應(yīng)用程序中包含的任何大型庫(kù)依賴(lài)項(xiàng)的使用方式都超過(guò)添加到應(yīng)用程序的代碼量。一個(gè)常見(jiàn)的反模式是包含一個(gè)非常大的庫(kù),因?yàn)橐恍?shí)用方法是有用的。減少你的應(yīng)用程序代碼依賴(lài)性通??梢詭椭惚苊?DEX 引用限制。
- 使用 R8 刪除未使用的代碼 -啟用代碼收縮以運(yùn)行 R8 以用于你的發(fā)布版本。啟用收縮可確保你不會(huì)隨 APK 一起發(fā)送未使用的代碼。
使用這些技術(shù)可能會(huì)幫助你避免在應(yīng)用中啟用 multidex,同時(shí)還可以減少 APK 的整體大小。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家學(xué)習(xí)Android multidex有所幫助和啟發(fā)。