Android Gradle 提供了大量的 DSL 給我們,以方便我們根據自己的需求定義相應的配置。
在這里記錄一些常用的配置,以方便使用的時候查詢。老話說 好記性不如爛筆頭。
關於 Android 項目的配置幾乎全部在 android{} 里了,我這里記錄的也全是 android 擴展里的配置。
最上面的兩個配置
//編譯的SDK 版本
compileSdkVersion 29
//構建工具版本
buildToolsVersion "29.0.2"
defaultConfig 默認配置
defaultConfig{} 是一個配置塊,負責定義所有的默認配置。
如果一個變體沒有定義自己的配置,就會默認使用 defaultConfig{} 里的配置。
例如 應用 ID ,版本號,版本名等。
//默認配置
defaultConfig {
//應用程序ID,創建時的包名,可以更改。
applicationId "com.skymxc.example"
//最小支持的SDK 版本
minSdkVersion 19
//目標 SDK 版本
targetSdkVersion 29
//應用版本代碼,一般用於控制APP的升級。
versionCode 1
//應用版本名稱,用戶可以看到。
versionName "1.0"
//配置單元測試使用的 runner
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
如果是 library 項目,是沒有 applicatoinId
的,但是有一個 consumerProguardFiles
配置
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//配置庫自己的 混淆規則,打包時會被打包進 AAR 包里,在 使用這個庫的項目里會自動應用庫里的混淆規則。
consumerProguardFiles 'consumer-rules.pro'
}
構建類型
構建類型是 Gradle 在構建和打包 APK 時使用的某些配置,通常是為開發生命周期的不同階段做配置。
例如 debug 構建類型 是在開發調試階段使用的,Gradle 會使用 調試密鑰庫為其簽名;
release 構建類型是正式發布打包階段使用的,會縮減 APK,對 APK 進行混淆處理並使用特定的加密密鑰庫。
Android studio 在創建項目時就會自動創建兩個構建類型:debug 和 release 。
雖然沒有把 debug 顯示在配置腳本上,但會有 debuggable true
配置它。
//構建類型
buildTypes {
//發布類型
release {
//是否啟用混淆
minifyEnabled false
//proguard 規則文件;
//getDefaultProguardFile 是 Android 擴展的一個方法,可以獲取你的 Android SDK 目錄下默認的 proguard 配置文件。
//在 android-sdk/tools/proguard/目錄下,文件名就是傳入的 proguard-android-optimize.txt
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
如果你有其他別的配置要在 debug 構建類型上配置,直接定義即可。
例如 我想讓 調試版和發布版本都能在一個手機上安裝,就可以為 debug 構建類型 增加一個配置,更改它的應用ID
//構建類型
buildTypes {
//正式發布階段使用
release {
//是否啟用混淆
minifyEnabled false
//proguard 規則文件;
//getDefaultProguardFile 是 Android 擴展的一個方法,可以獲取你的 Android SDK 目錄下默認的 proguard 配置文件。
//在 android-sdk/tools/proguard/目錄下,文件名就是傳入的 proguard-android-optimize.txt
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
//調試階段使用
debug {
applicationIdSuffix ".debug"
}
}
如果你有其他需求是這兩個類型不能滿足的,也可以添加一個構建類型,自定義其中的配置。
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
debuggable true
}
staging {
initWith debug
manifestPlaceholders = [hostName:"internal.example.com"]
applicationIdSuffix ".debugStaging"
}
}
關於構建類型可以使用的配置,我把常用的在這里列一下,方便以后查閱
- applicationIdSuffix 基於默認 applicationId 追加后綴。(注意也要符合 applicationId 的定義規則) 接受 String 類型的值。
- debuggable 是否生成一個可調式的 APK。接受 Boolean 類型的值。
- jniDebuggable 是否生成一個可供調試 Jni(C/C++) 代碼的 APK。接受 Boolean 類型的值。
- minifyEnabled 是否進行代碼縮減、啟用 Proguard 混淆。接受 Boolean 類型的值。
- proguardFiles 配置 Proguard 使用的規則文件,可以接受多個文件。
- shrinkResources 是否清理未使用的資源,默認是 false 。
- signingConfig 配置使用的簽名
更多更詳細的屬性和方法查看文檔吧,傳送門
產品變種
產品變種可以理解為應用的不同版本。例如,農村版和非農版。
可以自定義變種以使用不同的代碼和資源,同時共享和重復利用所有應用版本共用的部分。
產品變種是配置出來的,默認是沒有的,有需要則需要自己配置。
產品變種的配置和構建類型的差不多。
- 首先要添加一個產品維度,可以理解為一個分組,所有的變種都必須指定一個維度
//變種維度
flavorDimensions "domicile"
- 添加變種
//變種維度
flavorDimensions "domicile"
//產品變種
productFlavors{
//農村 變種
countryside {
//指定所屬維度
dimension "domicile"
versionNameSuffix '-countryside'
}
//城市 變種
city {
dimension "domicile"
versionNameSuffix '-city'
}
}
defaultConfig{} 中的配置在變種里都可以用,他們兩個都是屬於 ProductFlavor 類
比如,想給不同變種設置不同的版本就可以像上面那樣設置。
如果沒有在變種中配置任何自定義屬性,將使用 defaultConfig
中的配置。
在添加產品變種並同步后, Gradle 就會生成很多任務,基本上都是基於 構建類型+產品變種的方式生成的。
比如 assembleCity、assembleRelease 、assembleCityRelease 等。
assemble 開頭的負責生成 APK,比如 assembleCity 運行之后就會生成 City 變種的 release 和 debug 包;
assembleRelease 運行之后就會生成所有變種的的 release 包;
而 assembleCityRelease 運行后只會生成 city 的 release 包。
除了 assemble 系列,還有 compile系列,install系列等等,可以通過命令 gradle tasks
查看。
除了生成任務之外,每個產品變種和構建類型還可以有自己的 源集、依賴。
這就意味着可以為每個變種和構建類型定義他們自己的資源、代碼以及依賴的第三方庫。
關於怎么為構建類型,產品變種定義自己的資源、代碼和依賴下面和變體一起看。
產品變種也是多渠道構建的基礎,通常多渠道構建就是配置多個變種。
構建變體
構建變體是 Gradle 使用一組特定規則將構建類型和產品變種中配置的設置、代碼和資源組合在一起的結果。
它是構建類型和產品變種的交叉產物,也是 Gradle 用來構建應用的配置。
例如,“city” 產品變種可以定義不同的功能和設備要求(如自定義源代碼、資源和最低 API 級別),而 “debug” 構建類型則會應用不同的構建和打包設置(如調試選項和簽名密鑰)。
生成的構建變體是應用的“cityDebug”版本,它由“city”產品變種、“debug”構建類型和 main/ 源代碼集中包含的配置和資源組合而成。
你無法直接創建和更改變體,只能通過更改構建類型和產品變種的方式創建和配置變體。
在腳本中添加產品變種后,點擊 “Sync Now” 。
同步完成后,Gradle 會根據構建類型和產品變種自動創建構建變體,並按照 <product-flavor><Build-Type>
為其命名。
例如,如果創建了“countryside”和“city”產品變種,並保留了默認的“debug”和“release”構建類型,則 Gradle 會創建以下構建變體:
- cityDebug
- cityRelease
- countrysideDebug
- countrysideRelease
變體同步出來之后,你可以任意選擇要構建運行的變體,只需要在 Android studio 的 Build Variant 窗口選擇即可:
源代碼集
想知道怎么為構建變體配置特定資源,就需要先了解源代碼集的概念。
源代碼集俗稱源集,是 Android studio 按照邏輯關系將每個模塊的源代碼和資源進行的分組。
模塊的 main 源集包含所有構建變體共用的代碼和資源。
其他的源集都是可選的,在添加構建類型,產品變種或者配置構建變體后,Android studio 並不會自動的創建對應的源集。
我們可根據實際需求自行創建:
-
src/main/
此源代碼集包含所有構建變體共用的代碼和資源。 -
src/buildType/
創建此源代碼集可加入特定構建類型專用的代碼和資源。 -
src/productFlavor/
創建此源代碼集可加入特定產品變種專用的代碼和資源。
注意:如果配置構建以組合多個產品變種,則可以為變種維度之間的每個產品變種組合創建源代碼集目錄:src/productFlavor1ProductFlavor2/ -
src/productFlavorBuildType/
創建此源代碼集可加入特定構建變體專用的代碼和資源。
例如,如果要生成 “cityDebug” 版本,Android Gradle 需要合並來自以下源集的代碼、資源和配置:
- src/cityDebug (構建變體源集)
- src/debug (構建類型源集)
- src/city (產品變種源集)
- src/main (主源集)
如果不同源集包含同一資源的不同版本,Gradle 將按照以下優先順序決定使用哪一個(左側源集替換右側源集的文件和設置)
構建變體 > 構建類型 > 產品變種 > 主源代碼集 > 庫依賴項
這樣一來,Gradle 在你構建某個變體時就會專門使用對應的源集,同時重復利用與應用的其他版本共用的 Activity、應用邏輯和資源。
在合並多個清單時,Gradle 會使用相同的優先順序,這樣每個構建變體都能在最終清單中定義不同的組件或權限。
前面說到為構建類型,產品變種和構建變體創建自己專用的代碼和資源就是通過創建各自的源集實現的。
例如,要為 debug 構建類型創建源集,只需要在 src目錄下創建一個 debug 目錄就好了。
其目錄結構也和 main 源集一樣就好,java 代碼 放在 java 目錄里,資源文件文件放在 res 目錄里。
為產品變種和構建變體創建源集都是一樣的,根據名稱創建在 src 下創建對應的目錄即可。
目錄里對資源和代碼的組織和 main 源集一樣即可。
Android Gradle 提供了一個任務 :sourceSets .
這個任務的輸出展示了如何組織每個構建類型、產品變種和構建變體的文件。
當然這些都不是說定死了, 只不過約定俗成的都這么用,如果你偏偏不想這么用,非想特立獨行的改變源集的組織方式也是可以的。自行查閱吧,關鍵詞: “更改默認源代碼集配置”
單獨配置資源和代碼整完了,接着看怎么單獨配置依賴,使用第三方庫。
在關鍵字 implementation
前邊加上源集的名稱就可以了,我們創建一個應用模塊時就能看到這樣的例子
// main 源集的依賴
implementation 'androidx.constraintlayout:constraintlayout:1.1.3’
//test 源集的依賴
testImplementation 'junit:junit:4.12’
//androidTest 源集的依賴
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
為 debug 構建類型添加依賴
debugImplementation project(":library")
為 city 構建變種添加依賴
cityImplementation project(":library")
為 cityDebug 構建變體添加依賴
cityDebugImplementation project(":library")
配置簽名信息
APP 只有在簽名之后才能被發布,安裝,使用,簽名是保護 APP 的一種方式,標記該 APP 的唯一性。
如果簽名被惡意篡改,簽名就不一樣了,就無法安裝升級了,一定程度上也保護了我們的 APP。
要對 APP 簽名 就需要一個簽名證書文件,這個文件怎么創建下面會有。
一般情況下我們有兩種構建類型,debug 和 release ,對應的是開發生命周期中的兩個階段,通常也是只給發布類型的 APP 配置簽名信息。
開發調試時,Android studio 已經為我們提供了一個默認的 debug 簽名證書,可以直接使用,無需單獨配置,一般位於 $HOME/.android/debug.keystore
;密碼為 android 。
Android Gradle 提供了 signingConfigs{}
以便我們配置簽名信息,只需要將簽名信息添加在這個代碼塊里就可以了。
//簽名密鑰庫配置
signingConfigs {
//配置一個簽名信息,名稱為 release
release {
storeFile file("myreleasekey.keystore")
storePassword "password"
keyAlias "MyReleaseKey"
keyPassword "password"
}
}
signingConfigs{} 代碼塊里配置實際是屬於 SigningConfig 類型的,一個SigningConfig 就是一個簽名配置,可以添加多個簽名信息。
其中 release 就是簽名配置的名字,代碼塊里就是對當前簽名的配置。
其中常用的配置屬性如下
- storeFile 簽名證書文件
- storePassword 簽名證書文件密碼
- keyAlias 密鑰別名
- keyPassword 密鑰密碼
更詳細的配置看這里 SigningConfig
一個簽名證書里可以有多個 密鑰,每個密鑰 都有自己的密碼。
Android studio 目前是提供了圖形窗口供我們使用的,在窗口里填好信息就自動配置腳本。
配置完成 簽名后,就可以直接在構建類型或者產品變種的配置中引用了
//城市 變種
city {
dimension "domicile"
versionNameSuffix '-city'
signingConfig signingConfigs.city
}
創建簽名證書
1.在上方工具條的 build 菜單里選擇 Generate Signed Builde /APK
-
選擇 APK,點擊 next
-
選擇 Create New… 進入創建窗口
- 然后就是創建簽名證書文件,選擇存儲路徑,設置密碼,創建密鑰,設置密鑰密碼,有效期默認就好,其他的信息視自己情況配置。填寫完成后,點擊 OK 就可以了。
5.在創建完簽名證書后,會自動賦值到之前的窗口,到這里簽名證書文件就創建完成了,如果不繼續生成 APK 就可以關閉了。
壓縮 APK
在配置了 minifyEnabled true
之后系統默認就會啟用 R8 代碼壓縮,優化,混淆功能。
混淆規則是配置在 proguard-rules.pro 文件里,可通過 proguardFiles 配置更改。
//發布類型
release {
//是否啟用混淆
minifyEnabled false
//proguard 規則文件;
//getDefaultProguardFile 是 Android 擴展的一個方法,可以獲取你的 Android SDK 目錄下默認的 proguard 配置文件。
//在 android-sdk/tools/proguard/目錄下,文件名就是傳入的 proguard-android-optimize.txt
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
文件里默認是沒有任何規則的,需要自行配置。
Android Gradle 在 3.4.0 以及之后的版本已經默認不使用 Proguard 了,改成 R8 編譯器了。
目前 R8 編譯器是 支持所有現有的 Proguard 規則文件,所以即使現在不使用 Proguard 了,仍然不需要做什么改變。
關於 Proguard 的規則我在另一篇文章里詳細的解釋了一下 Proguard 常用規則
R8 編譯器幾乎和 Proguard 編譯過程差不多,在編譯時會處理以下任務
- 代碼縮減(即搖樹優化):從代碼中檢測沒有使用到的類、字段、方法和屬性然后進行安全的移除。這里如果你用到了反射並沒有直接引用就需要配置保留入口點了。
- 資源縮減:移除不使用的資源,包括依賴的庫中的資源。如果你有動態引用的資源就需要配置哪些資源不能被清除
- 混淆處理:縮短或者重命名類和成員的名稱,從而減小 DEX 文件的大小。
- 優化:檢查並重寫代碼,進一步縮小 DEX 的大小。例如,如果 R8 檢測到 if/else 語句中的 else {} 分之沒有采用過,就會把 else {} 移除
在設置了 minifyEnabled true
之后,R8 會從多個地方讀取 Proguard 規則文件,這里把 R8 使用的文件來源列一下,了解一下編譯器都是從哪里讀取配置文件。
來源 | 位置 | 說明 |
---|---|---|
Android studio | <module-dir>/proguard-rules.pro | 在使用 IDE 創建新模塊時,IDE 會在該模塊的根目錄創建 proguard-rules.pro 文件默認是沒有任何規則的,需要自行配置。 |
Android Gradle 插件 | 由 Android Gradle 插件在編譯時生成 | Android Gradle 插件會生成 proguard-android-optimize.txt(其中包含了對大多數 Android 項目都有用的規則),並啟用 @Keep* 注釋。默認情況下,使用 Android Studio 創建新模塊時,模塊級 build.gradle 文件會配置此文件。 |
庫依賴項 | AAR 庫:<library-dir>/proguard.txt JAR 庫:<library-dir>/META-INF/proguard/ |
如果某個 AAR 庫是使用它自己的 ProGuard 規則文件發布的,並且將該 AAR 庫作為編譯時依賴項納入到項目中,那么 R8 在編譯項目時會自動應用其規則。(library 項目里的 consumerProguardFiles 配置) 如果 AAR 庫需要某些保留規則才能正常運行,那么使用該庫隨附的規則文件將非常有用。 也就是說,庫開發者已經為你配置好了規則。不過,請注意,由於 ProGuard 規則是累加的,因此 AAR 庫依賴項包含的某些規則無法移除,並且可能會影響對應用其他部分的編譯。 例如,如果某個庫包含停用代碼優化功能的規則,該規則會針對整個項目停用優化功能。 |
Android 資源打包工具 2 (AAPT2) | 使用 minifyEnabled true 構建項目后:<module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt | AAPT2 會根據對應用清單中的類、布局及其他應用資源的引用,生成保留規則。 例如,AAPT2 會為您在應用清單中注冊為入口點的每個 Activity 添加一個保留規則。 |
自定義配置文件 | 默認情況下,當使用 Android Studio 創建新模塊時,IDE 會創建 <module-dir>/proguard-rules.pro,以便添加自己的規則。 | 我們可以添加自定義的配置,R8編譯器在編譯時會應用這些配置 |
在配置了 minifyEnabled true
之后, R8 會將來自上述所有可用來源的配置文件組合到一起。
如果需要查看 R8 在構建項目時應用的所有規則的完整報告,可以將以下代碼加入到模塊的 proguard-rules.pro 文件中
路徑和名字可以隨意更改
-printconfiguration ~/tmp/full-r8-config.txt
自動清理未使用的資源
在配置 minifyEnabled true
后 R8 編譯器會將代碼進行優化縮減混淆,那么資源呢,如何優化呢?
答案是通過配置 shrinkResources 屬性
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
通過 shrinkResources true
配置 資源縮減,可將沒有使用到的資源自動移除。
配置資源縮減的前提是配置了代碼縮減,資源縮減只有在與代碼縮減配合才能發揮作用。
在代碼縮減器移除掉所有不使用代碼后,資源縮減器便可以確定哪些資源沒有被引用,從而安全的移除。
通常我們在實際開發中會有一些資源是未被直接引用的,存在動態引用或者反射引用的可能,這時就需要配置一些規則,告訴資源縮減器規則內的資源不能被移除。
這就和配置 Proguard 規則保留入口點的道理一樣。
配置要保留的資源
如果需要自定義保留或者舍棄特定資源,需要在項目中創建一個包含 <resources>
標記的 XML 文件。
可以在 tools:keep
屬性中指定要保留的資源,在 tools:discard
屬性中指定要舍棄的資源。
這兩個屬性都接受以逗號分隔的資源名稱列表,也可以使用 * 作為通配符。
例如
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:keep="@mipmap/more,@mipmap/saw"
tools:discard="@mipmap/*_2"
tools:shrinkMode="strict">
<!--保留 more saw文件-->
<!-- 舍棄 share_2 文件-->
<!-- 嚴格模式-->
</resources>
通常將文件保存在 /res/raw/keeps.xml
。Android Gradle 不會將其打包進 APK,除非你在代碼中使用了 R.raw.keeps
上面的三個屬性都是可選項,其中 shrinkMode
是配置自動清理資源的模式,有兩個可選項
- strict 嚴格模式
- safe 安全模式,默認值
默認值的情況下,對於代碼中動態引用資源,資源縮減會自行模糊判斷一些資源,當作已使用資源。
例如,下面這個代碼,就會把 img_ 為前綴的資源都算已引用資源。
String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());
如果設置了 strict 模式,就必須手動保留了,例如這樣
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/img_*"
tools:shrinkMode="strict">
</resources>
配置生成的 APK 名稱
// 配置 APK 名稱
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "Example_${variant.flavorName}_${variant.versionCode}_${variant.versionName}.apk"
}
}
動態配置 AndroidManifest 文件
動態配置 AndroidManifest 文件就是在構建過程中,動態修改 AndroidManifest 文件中的一些內容。
其本質是將構建變量注入到清單文件中。
在清單文件中是可以使用 占位符 的,格式是 ${variable} 。
例如常用的渠道編號
<!-- 渠道商編號 -->
<meta-data
android:name="BaiduMobAd_CHANNEL"
android:value="${CHANNEL_NAME}" />
定義好占位符之后在 build.gradle 腳本里就可以通過 ProductFlavor 的 manifestPlaceholders 屬性進行配置,它接受 Map 類型的參數。
//默認配置,它是一個 ProductFlavor
defaultConfig {
······
manifestPlaceholders = [
CHANNEL_NAME:"default"
]
}
實際開發中通常是在產品變種配置渠道,方便日后運營統計查看
//變種維度
flavorDimensions "channel"
//產品變種
productFlavors{
// 為了實踐動態配置 AndroidManifest 文件
huawei {
dimension "channel"
manifestPlaceholders = [
"CHANNEL_NAME":"華為"
]
}
mi {
dimension "channel"
manifestPlaceholders = [
CHANNEL_NAME:"小米"
]
}
}
默認情況下是有一個 ${applicationId}
占位符的,它會提供程序的應用ID。
例如
<intent-filter ... >
<action android:name="${applicationId}.TRANSMOGRIFY" />
...
</intent-filter>
自定義 BuildConfig
對於 BuildConfig
這個類,應該都很熟悉,這是一個由 構建工具生成的類。
其中有一個 DEBUG 字段是我們常用的,用於標記是否在開發調試階段,通常可以用作是否開啟日志的開關,剩下的幾個字段也都很熟悉,都是 build.gradle 中的配置。
除了構建工具自動生成的字段,也可以通過 ProductFlavor.buildConfigField(String type, String name, String value)
方法自定義 BuildConfig 的字段。
- 第一個參數是 要生成的字段的類型
- 第二個參數是 要生成的字段的名字
- 第三個參數是 要生成的字段的常量值,如果是 String 類型類型的要帶上引號。
例如,在 腳本里定義一個字段后
defaultConfig {
buildConfigField "String" "WEBURL" "\"https://github.com/skymxc\""
}
在 Sync now
之后就會在 BuildConfig 類中看到對應的字段了
這個配置在構建類型和產品變種里都是可以配置的,利用這個特性,就可以針對不同的構建類型,不同的產品變種配置不同的字段。
例如,分別配置開發和生產環境
defaultConfig {
buildConfigField "String" "WEBURL" "\"https://github.com/skymxc\""
}
//構建類型
buildTypes {
//發布類型
release {
·······
buildConfigField "String","WEBURL","\"https://www.cnblogs.com/skymxc/\""
}
}
也可以針對不同變種配置
//產品變種
productFlavors{
//農村 變種
countryside {
dimension "domicile"
buildConfigField "int","type","2"
}
//城市 變種
city {
dimension "domicile"
buildConfigField "int","type","1"
}
}
Java 編譯選項
Android Gradle 提供了一個 compileOptions{}
對 Java 編譯進行配置
//Java 編譯選項配置
compileOptions {
// Java 源代碼的編碼
encoding = 'utf-8'
//Java 源代碼編譯級別
sourceCompatibility JavaVersion.VERSION_1_8
//生成的 Java 字節碼版本
targetCompatibility JavaVersion.VERSION_1_8
}
常用的配置有三個
- encoding Java源代碼的編碼
- sourceCompatibility Java 源代碼編譯級別
- targetCompatibility 生成的 Java 字節碼版本
關於 64 K 限制
在打包 APK 文件時,Java 源代碼被打包成了一個 DEX 文件,這個文件就是優化過的、Dalvik 虛擬機可執行的文件。
Dalvik 虛擬機在執行 DEX 文件的時候,它使用了 short 這個類型來索引 DEX 文件中的方法,這就意味着單個 DEX 文件最多只能是 65535 個,當方法數超過這個數量就會出現編譯錯誤。
因為 1024 * 64 = 65535 也稱 64K 限制。
解決辦法就是生成多個 DEX 文件,讓單個 DEX 文件內的方法數不超過 64K .
在 Android 5.0 及之后的版本使用 ART 了運行時,本身支持從 APK 中加載多個 DEX 文件。
ART 在應用安裝時執行預編譯,掃描 class[N].dex
文件,並將他們合成一個 .oat 文件,以供 Android 設備執行。
因此,如果你的 minSdkVersion 是 21 或者更高的版本,默認就會啟用 MultiDex ,在打包時構建工具會自動生成多個 DEX 文件。
如果 miniSdkVersion 在 20或者更低,就需要在 build.gradle 腳本添加配置 multiDexEnabled true 啟用 MultiDex,並添加 MultiDex 支持庫。
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 28
multiDexEnabled true
}
...
}
dependencies {
implementation 'com.android.support:multidex:1.0.3’
}
如果使用了 androidx ,則使用以下依賴
def multidex_version = "2.0.1"
implementation 'androidx.multidex:multidex:$multidex_version'
添加完依賴庫之后,還需要替換 Application 類
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
android:name="android.support.multidex.MultiDexApplication" >
...
</application>
</manifest>
如果你有自己的 Application 類,只需繼承 MultiDexApplication 即可
public class MyApplication extends MultiDexApplication { ... }
如果你不能繼承此類,也可在自己的 Application 類的 attachBaseContext() 方法里調用 MultiDex.install(this) 以啟用 MultiDex
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
注意:在 MultiDex.install() 完成之前,不要通過反射或 JNI 執行 MultiDex.install() 或其他任何代碼。MultiDex 跟蹤功能不會追蹤這些調用,從而導致出現 ClassNotFoundException,或因 DEX 文件之間的類分區錯誤而導致驗證錯誤。
關於更多 64K 限制的文檔可以看這里:啟用 MultiDex
學習資料
- 《Android Gradle 權威指南》
- Android Studio 用戶指南
- Android Plugin DSL Reference
End
最后把 Android Gradle 的 DSL 查詢地址留一下Android Plugin DSL Reference 方便大家隨用隨查。
我把這些配置和注釋都放在了 GitHub 上的一個倉庫里,需要時查閱即可 AndroidGradleExample