為使用超過 64K 個方法的應用程式啟用 Multidex

如果應用程式的 minSdk API 為 API 20 以下,而且您的應用程式和 程式庫參照超過 65,536 個方法時,遇到下列建構錯誤 表示應用程式已達 Android 建構架構的上限:

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

舊版建構系統回報不同的錯誤,這表示 問題:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

以上兩種錯誤狀況都會顯示一組共同數字:65536。這個號碼 代表可使用的 單一 Dalvik Executable (DEX) 位元碼檔案中的程式碼叫用。 本頁說明如何解除這項限制, 啟用名為 Multidex 的應用程式設定,讓您的應用程式 建立及讀取多個 DEX 檔案

關於 64K 參考限制

Android 應用程式 (APK) 檔案中的可執行位元碼檔案格式為 的 Dalvik 執行檔 (DEX) 檔案,內含用於執行應用程式的編譯程式碼。 Dalvik 可執行檔規格會限制產生的方法總數 在單一 DEX 檔案中參照為 65,536 (包括 Android 版本) 導入自己的程式碼

在 「kilo (簡稱 K)」這個字詞表示 1024 年 ( 2^10)。由於 65,536 等於 64x1024,因此這項限制稱為 _64K 參考上限_。

Android 5.0 之前版本的 Multidex 支援

Android 5.0 (API 級別 21) 之前的平台版本使用 Dalvik 執行應用程式程式碼的執行階段根據預設,Dalvik 限制應用程式只能由單一應用程式 每個 APK 有 classes.dex 位元碼檔案。在這之前 限制,請將 Multidex 程式庫新增至模組層級的 build.gradlebuild.gradle.kts 檔案:

Groovy

dependencies {
    def multidex_version = "2.0.1"
    implementation "androidx.multidex:multidex:$multidex_version"
}

Kotlin

dependencies {
    val multidex_version = "2.0.1"
    implementation("androidx.multidex:multidex:$multidex_version")
}

這個程式庫會成為應用程式主要 DEX 檔案的一部分, 管理對其他 DEX 檔案及其所含程式碼的存取權。 如要查看這個程式庫目前的版本,請參閱 Multidex 版本

詳情請參閱「 設定用於 Multidex 的應用程式

Android 5.0 以上版本的 Multidex 支援

Android 5.0 (API 級別 21) 以上版本使用名為 ART 的執行階段, 原生支援從 APK 檔案載入多個 DEX 檔案。阿根廷時間 會在應用程式安裝時執行預先編譯,掃描 並編譯成單一 classesN.dex 檔案 OAT 檔案: 由 Android 裝置執行因此,如果您的 minSdkVersion 21 以上,系統會預設啟用 Multidex,您就不需使用 Multidex 程式庫。

詳情請參閱 Android 5.0 版 請詳閱 Android 執行階段 (ART) 與 Dalvik

注意:使用 Android Studio 執行應用程式時, 該版本會針對您部署的目標裝置進行最佳化處理。 這包括在目標裝置執行時啟用 Multidex Android 5.0 以上版本。因為只有在使用以下應用程式部署應用程式時,才會套用這項最佳化設定 Android Studio,您可能仍需設定發布子版本 以避免 64K 限制

避免 64K 限制

在設定應用程式以使用 64K 個以上的方法參照之前,請先採取下列步驟 ,減少應用程式程式碼呼叫的參照總數,包括 您的應用程式程式碼或隨附的程式庫

下列策略有助於避免達到 DEX 參照上限:

查看應用程式的直接與遞移依附元件
請思考應用程式中任何大型程式庫依附元件的價值,是否高於程式碼的數量 應用程式。其中一個常見但有問題的模式 就是納入一個超大型的程式庫 因為有些公用程式方法相當實用減少應用程式程式碼依附元件通常有助於解決問題 即可避免達到 DEX 參照上限
使用 R8 移除未使用的程式碼
啟用程式碼縮減功能以執行 R8 為發布子版本請啟用縮減功能,確保您 不會透過您的 APK 傳送未使用的代碼。如果程式碼縮減功能設定正確 也可以從依附元件中移除未使用的程式碼和資源。

一旦使用這些技術,您就能縮減 APK 的整體大小, 不必在應用程式中啟用 Multidex

設定用於 Multidex 的應用程式

注意:如果 minSdkVersion 設為 21 以上,系統會預設啟用 Multidex 而且我們不需要 Multidex 程式庫

如果 minSdkVersion 設為 20 以下, 必須使用 Multidex 程式庫,然後 以下修改您的應用程式專案:

  1. 將模組層級 build.gradle 檔案修改為 啟用 Multidex,並將 Multidex 程式庫新增為依附元件,如下所示:

    Groovy

    android {
        defaultConfig {
            ...
            minSdkVersion 15 
            targetSdkVersion 33
            multiDexEnabled true
        }
        ...
    }
    
    dependencies {
        implementation "androidx.multidex:multidex:2.0.1"
    }
    

    Kotlin

    android {
        defaultConfig {
            ...
            minSdk = 15 
            targetSdk = 33
            multiDexEnabled = true
        }
        ...
    }
    
    dependencies {
        implementation("androidx.multidex:multidex:2.0.1")
    }
    
  2. 視您是否要覆寫 Application 而定 類別時,請執行下列其中一項操作:
    • 如果您未覆寫 Application 類別,請編輯資訊清單檔案,在以下位置設定 android:name<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="androidx.multidex.MultiDexApplication" >
              ...
          </application>
      </manifest>
      
    • 如果您覆寫 Application 類別,將其變更為擴充 MultiDexApplication,如下所示:

      Kotlin

      class MyApplication : MultiDexApplication() {...}
      

      Java

      public class MyApplication extends MultiDexApplication { ... }
      
    • 如果您覆寫 Application 但無法變更基礎類別,則 而是覆寫 attachBaseContext() 方法,並呼叫 MultiDex.install(this) 以啟用 multidex:

      Kotlin

      class MyApplication : SomeOtherApplication() {
      
          override fun attachBaseContext(base: Context) {
              super.attachBaseContext(base)
              MultiDex.install(this)
          }
      }
      

      Java

      public class MyApplication extends SomeOtherApplication {
        @Override
        protected void attachBaseContext(Context base) {
           super.attachBaseContext(base);
           MultiDex.install(this);
        }
      }
      

      注意:請勿執行 MultiDex.install() 或任何其他程式碼 或 JNI MultiDex.install()完成。Multidex 追蹤記錄 未追蹤這些呼叫,導致 ClassNotFoundException 或驗證錯誤 。

現在當您建構應用程式時,Android 建構工具會建構主要 DEX 檔案 (classes.dex) 和支援的 DEX 檔案 (classes2.dexclasses3.dex 等)。 接著,建構系統會將所有 DEX 檔案封裝在您的 APK 中。

從執行階段開始,而不是只在主要 classes.dex 檔案時,Multidex API 會使用特殊的類別載入器來搜尋所有 您方法可用的 DEX 檔案。

Multidex 程式庫的限制

Multidex 程式庫有一些已知的限制。 將此程式庫加入應用程式建構設定前,請考量下列事項:

  • 在啟動期間將 DEX 檔案安裝至裝置資料分區的程序相當複雜, 如果次要 DEX 檔案過大,可能會導致應用程式無回應 (ANR) 錯誤。目的地: 如要避免這個問題,請啟用程式碼縮減功能,將發生的問題 ,並移除未使用的程式碼部分。
  • 在 Android 5.0 (API 級別 21) 之前的版本中執行時,請使用 Multidex 不足以解決 Linearalloc 限制 (問題 37008143)。此上限已在 Android 4.0 (API 級別 14),但這並未完全解決問題。

    在低於 Android 4.0 的版本中,您可能會在 達到 DEX 索引上限因此,如果指定的 API 級別低於 14,請在這些平台版本上進行完整測試,因為您的應用程式 會在啟動時或載入特定類別群組時發生問題。

    程式碼縮減 如何消除這些問題

宣告主要 DEX 檔案中所需的類別

為 Multidex 應用程式建構每個 DEX 檔案時,建構工具會執行以下作業 複雜的決策流程,判斷主要 DEX 中需要哪些類別 檔案,您的應用程式才能順利啟動。如果所需的類別 主要 DEX 檔案中並未提供啟動期間,會導致應用程式當機 錯誤訊息 java.lang.NoClassDefFoundError

針對直接從應用程式存取的程式碼,建構工具會辨識程式碼路徑 再也不是件繁重乏味的工作不過,這個問題 程式碼路徑較不可見時 (例如您使用的程式庫 複雜的依附元件舉例來說,如果程式碼使用自我檢查或叫用 因此,系統可能不會將這些類別視為 必須安裝於主要 DEX 檔案中

收到「java.lang.NoClassDefFoundError」後, 必須手動指定主要 DEX 中所需的其他類別 檔案,方法是在建構類型中使用 multiDexKeepProguard 屬性宣告這些內容。如果類別相符 multiDexKeepProguard 檔案,然後是該類別 就會新增至主要 DEX 檔案。

multiDexKeepProguard 屬性

multiDexKeepProguard 檔案採用與 ProGuard 相同的格式,並支援 完整的 ProGuard 文法如要進一步瞭解如何自訂應用程式保留的內容,請參閱 自訂要保留的程式碼

您在 multiDexKeepProguard 中指定的檔案應包含 -keep 選項。例如: -keep com.example.MyClass.class。您可以建立名稱為 multidex-config.pro 看起來會像這樣:

-keep class com.example.MyClass
-keep class com.example.MyClassToo

如要指定套件中的所有類別,則檔案如下:

-keep class com.example.** { *; } // All classes in the com.example package

接著,您可以針對建構類型宣告該檔案,如下所示:

Groovy

android {
    buildTypes {
        release {
            multiDexKeepProguard file('multidex-config.pro')
            ...
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            multiDexKeepProguard = file("multidex-config.pro")
            ...
        }
    }
}

在開發版本中最佳化 Multidex

Multidex 設定需要大幅增加建構處理能力 因為建構系統必須對哪些類別做出複雜決策 必須包含在主要 DEX 檔案中,以及可將哪些類別納入 次要 DEX 檔案。這表示通常使用 Multidex 漸進式建構作業 可能會拖慢開發程序

如要縮短較長的漸進式建構時間,請使用 DEX 前置處理,以便在不同版本之間重複使用 Multidex 輸出。 DEX 前置處理仰賴 ART 格式 (僅支援 Android 5.0) (API 級別 21) 以上版本。如果您使用的是 Android Studio,IDE 會自動採用 DEX 前置處理 以便將應用程式部署至搭載 Android 5.0 (API 級別 21) 以上版本的裝置。 不過,如果您是透過指令列執行 Gradle 建構作業,就必須設定 minSdkVersion 至 21 以上版本啟用 DEX 前置處理程序。

如要保留您的 正式版,您可以建立兩個應用程式版本 使用變種版本—一個版本 分別是開發變種版本和發布變種版本 minSdkVersion 有不同的值 如下所示:

Groovy

android {
    defaultConfig {
        ...
        multiDexEnabled true
        // The default minimum API level you want to support.
        minSdkVersion 15
    }
    productFlavors {
        // Includes settings you want to keep only while developing your app.
        dev {
            // Enables pre-dexing for command-line builds. When using
            // Android Studio 2.3 or higher, the IDE enables pre-dexing
            // when deploying your app to a device running Android 5.0
            // (API level 21) or higher, regardless of minSdkVersion.
            minSdkVersion 21
        }
        prod {
            // If you've configured the defaultConfig block for the production version of
            // your app, you can leave this block empty and Gradle uses configurations in
            // the defaultConfig block instead. You still need to include this flavor.
            // Otherwise, all variants use the "dev" flavor configurations.
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                                                 'proguard-rules.pro'
        }
    }
}
dependencies {
    implementation "androidx.multidex:multidex:2.0.1"
}

Kotlin

android {
    defaultConfig {
        ...
        multiDexEnabled = true
        // The default minimum API level you want to support.
        minSdk = 15
    }
    productFlavors {
        // Includes settings you want to keep only while developing your app.
        create("dev") {
            // Enables pre-dexing for command-line builds. When using
            // Android Studio 2.3 or higher, the IDE enables pre-dexing
            // when deploying your app to a device running Android 5.0
            // (API level 21) or higher, regardless of minSdkVersion.
            minSdk = 21
        }
        create("prod") {
            // If you've configured the defaultConfig block for the production version of
            // your app, you can leave this block empty and Gradle uses configurations in
            // the defaultConfig block instead. You still need to include this flavor.
            // Otherwise, all variants use the "dev" flavor configurations.
        }
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"),
                                                 "proguard-rules.pro")
        }
    }
}

dependencies {
    implementation("androidx.multidex:multidex:2.0.1")
}

如要進一步瞭解有助於透過 Android Studio 或指令加快建構速度的策略 請參閱「最佳化建構速度」。 如要進一步瞭解如何使用建構變數,請參閱 設定建構變數

提示:如果您為 Multidex 需求,您可以為每種 因此只有 API 級別 20 以下的檔案會變更 <application> 標記名稱。你也可以 為每個變數建立不同的 Application 子類別 只有 API 級別 20 以下的子類別擴充 MultiDexApplication 類別或 呼叫 MultiDex.install(this)

測試 Multidex 應用程式

為 Multidex 應用程式編寫檢測設備測試時,無須進行額外的設定 如果您使用 MonitoringInstrumentation AndroidJUnitRunner 這些指令。如果您使用其他 Instrumentation、 接著,您必須使用下列程式碼覆寫其 onCreate() 方法:

Kotlin

fun onCreate(arguments: Bundle) {
  MultiDex.install(targetContext)
  super.onCreate(arguments)
  ...
}

Java

public void onCreate(Bundle arguments) {
  MultiDex.install(getTargetContext());
  super.onCreate(arguments);
  ...
}