Włącz multidex w przypadku aplikacji z ponad 64 tys. metod

Jeśli Twoja aplikacja ma interfejs minSdk API o wartości 20 lub starszej, a aplikacja oraz do ponad 65 536 metod,występuje ten błąd kompilacji, wskazuje, że aplikacja osiągnęła limit architektury kompilacji Androida:

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

Starsze wersje systemu kompilacji zgłaszają inny błąd, co wskazuje na ten sam problem:

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

W przypadku tych warunków błędu wyświetlana jest wspólna liczba: 65536. Ten numer to łączna liczba plików referencyjnych, które można przesłać wywoływana przez kod w pojedynczym pliku bajtowym Dalvik Executable (DEX). Z tego artykułu dowiesz się, jak obejść to ograniczenie przez włącz konfigurację aplikacji o nazwie multidex, która umożliwia w celu tworzenia i odczytywania wielu plików DEX.

Informacje o limicie 64 tys. plików referencyjnych

Pliki aplikacji na Androida (APK) zawierają wykonywalne pliki z kodem bajtowym w formacie z Dalvik Pliki wykonywalne (DEX) zawierające skompilowany kod służący do uruchamiania aplikacji. Specyfikacja pliku wykonywalnego Dalvik ogranicza łączną liczbę metod, które są mogą być odwoływane w pojedynczym pliku DEX do wersji 65 536,w tym na urządzeniach z Androidem metod platformy, metod biblioteki i metod w Twoim kodzie.

W w kontekście informatyki, termin kilo lub K oznacza 1024 (lub 2^10). Ponieważ 65 536 to 64 x 1024, limit ten jest nazywany _64 tys. plików referencyjnych.

Obsługa Multidex w wersjach starszych niż Android 5.0.

Wersje platformy starsze niż Android 5.0 (poziom interfejsu API 21) używają interfejsu Dalvik. w środowisku wykonawczym do wykonywania kodu aplikacji. Domyślnie Dalvik ogranicza aplikacje do jednej classes.dex plik z kodem bajtowym na pakiet APK. Aby obejść ten problem, dodaj bibliotekę Multidex do build.gradle na poziomie modułu lub build.gradle.kts plik:

Odlotowe

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")
}

Ta biblioteka staje się częścią głównego pliku DEX aplikacji, zarządza dostępem do dodatkowych plików DEX i zawartego w nich kodu. Aby wyświetlić bieżące wersje tej biblioteki, zobacz w wersji multidex.

Więcej informacji znajdziesz w sekcji dotyczącej skonfigurować aplikację pod kątem obsługi środowiska Multidex.

Obsługa Multidex na Androidzie 5.0 i nowszych

Android 5.0 (poziom interfejsu API 21) i nowsze używają środowiska wykonawczego ART, które natywnie obsługuje wczytywanie wielu plików DEX z plików APK. Sztuka przeprowadza wstępną kompilację w momencie instalowania aplikacji, skanując classesN.dex pliki i skompilowanie ich w jeden Plik OAT dla przez urządzenie z Androidem. Dlatego, jeśli minSdkVersion ma wartość 21 lub nowszą, format Multidex jest włączony domyślnie i nie potrzebujesz biblioteki Multidex.

Więcej informacji o Androidzie 5.0 przeczytaj artykuły Środowisko wykonawcze Android (ART) i Dalvik.

Uwaga: jeśli używasz Androida Studio, kompilacja jest zoptymalizowana pod kątem urządzeń docelowych, na których wdrażasz wdrożenie. Obejmuje to włączenie Multidex, gdy uruchomione są urządzenia docelowe Androida 5.0 lub nowszego, Ponieważ optymalizacja jest stosowana tylko podczas wdrażania aplikacji za pomocą w Android Studio, konieczne może być skonfigurowanie kompilacji wersji. dla multidex, aby uniknąć limitu 64 KB.

Unikaj limitu 64 tys.

Zanim skonfigurujesz aplikację, aby umożliwić korzystanie z odwołań do metod co najmniej 64 tys., wykonaj odpowiednie czynności , aby zmniejszyć łączną liczbę odwołań wywoływanych przez kod aplikacji, w tym metod zdefiniowanych przez do kodu aplikacji lub dołączonych bibliotek.

Poniższe strategie pomogą Ci uniknąć osiągnięcia limitu plików referencyjnych DEX:

Sprawdzanie zależności bezpośrednich i przechodnich aplikacji
Zastanów się, czy wartość dowolnej dużej zależności biblioteki uwzględnionej w aplikacji przewyższa ilość kodu dodawania do aplikacji. Częstym, ale problematycznym wzorcem jest uwzględnianie bardzo dużej biblioteki. bo kilka metod użytkowych było przydatnych. Zmniejszenie zależności kodu aplikacji często pomaga uniknąć limitu plików DEX.
Usuń nieużywany kod za pomocą R8
Włącz zmniejszanie kodu, by uruchomić R8 dla kompilacji wersji. Włącz zmniejszanie, aby zapewnić nie są wysyłane nieużywany kod wraz z plikami APK. Jeśli kompresowanie kodu jest skonfigurowane prawidłowo, może też usunąć z zależności nieużywany kod i zasoby.

Korzystając z tych technik, możesz zmniejszyć ogólny rozmiar pliku APK i uniknąć konieczności korzystania z multidex w aplikacji.

Konfigurowanie aplikacji pod kątem obsługi środowiska Multidex

Uwaga: jeśli minSdkVersion ma wartość 21 lub większą, tryb multidex jest domyślnie włączony i nie potrzebujesz biblioteki Multidex.

Jeśli minSdkVersion ma wartość 20 lub niższą, musi użyć funkcji multidex Library i te zmiany w projekcie aplikacji:

  1. Zmodyfikuj plik build.gradle na poziomie modułu na włącz multidex i dodaj bibliotekę multidex jako zależność, jak w poniższym przykładzie:

    Odlotowe

    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. W zależności od tego, czy zastępujesz Application , wykonaj jedną z tych czynności:
      .
    • Jeśli nie zastąpisz parametru Application , zmodyfikuj plik manifestu, aby ustawić android:name w <application> w następujący sposób:

      <?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>
      
    • Jeśli zastąpisz parametr Application klasy, zmień ją na rozszerzenie MultiDexApplication w ten sposób:

      Kotlin

      class MyApplication : MultiDexApplication() {...}
      

      Java

      public class MyApplication extends MultiDexApplication { ... }
      
    • Jeśli zastąpisz Application ale nie można zmienić klasy bazowej, zamiast tego zastąp metodę attachBaseContext() i wywołaj MultiDex.install(this), aby włączyć 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);
        }
      }
      

      Uwaga: nie uruchamiaj MultiDex.install() lub dowolny inny kod poprzez odbicie lub JNI przed ukończeniem MultiDex.install(). Śledzenie Multidex będzie nie realizuj tych wywołań, powodując ClassNotFoundException lub zweryfikując błędy z powodu nieprawidłowej partycji klasy między plikami DEX.

Teraz podczas tworzenia aplikacji narzędzia Android Build tworzą podstawowy plik DEX (classes.dex) i obsługujące pliki DEX (classes2.dex, classes3.dex itd.) w razie potrzeby. Następnie system kompilacji spakuje wszystkie pliki DEX do Twojego pliku APK.

W czasie działania zamiast wyszukiwania tylko w głównej aplikacji classes.dex, interfejsy API Multidex używają specjalnego programu ładującego klas do przeszukiwania wszystkich dostępne pliki DEX.

Ograniczenia biblioteki Multidex

Biblioteka Multidex ma pewne znane ograniczenia. Włączając tę bibliotekę do konfiguracji kompilacji aplikacji, weź pod uwagę te kwestie:

  • Instalacja plików DEX podczas uruchamiania na partycji danych urządzenia jest skomplikowana i może spowodować błąd ANR (Application Not Responding), jeśli dodatkowe pliki DEX będą duże. Do uniknąć tego problemu, włącz zmniejszanie kodu, by zminimalizować pliki DEX i usunąć nieużywane fragmenty kodu.
  • Jeśli działa na urządzeniach z Androidem w wersji starszej niż 5.0 (poziom interfejsu API 21) za pomocą Multidex nie wystarczy, aby obejść limit przydziału liniowego (numer 37008143). Ten limit został zwiększony o Androida 4.0 (poziom interfejsu API 14), ale to nie rozwiązało całkowicie problemu.

    W wersjach starszych niż 4.0 limit przydziału linearnego może zostać osiągnięty przed osiągnęliśmy limit indeksu DEX. Jeśli więc kierujesz reklamy na poziomy interfejsu API niższe niż 14, dokładnie przetestuj ją na tych wersjach platformy, ponieważ aplikacja może mają problemy podczas uruchamiania lub gdy są wczytywane określone grupy klas.

    Zmniejszanie kodu może zmniejszyć wyeliminować te problemy.

Deklarowanie klas wymaganych w głównym pliku DEX

Podczas tworzenia każdego pliku DEX dla aplikacji Multidex narzędzia do kompilacji złożonego procesu decyzyjnego w celu określenia, które klasy są potrzebne w głównej bibliotece DEX; , aby aplikacja mogła się uruchomić. Jakiekolwiek wymagane zajęcia nie jest dostarczany w głównym pliku DEX, a w rezultacie aplikacja ulega awarii. z błędem java.lang.NoClassDefFoundError.

Narzędzia do kompilacji rozpoznają ścieżki kodu, do których dostęp uzyskuje się bezpośrednio z aplikacji w kodzie. Ten problem może jednak występują, gdy ścieżki kodu są mniej widoczne. Na przykład gdy używana biblioteka złożonych zależności. Jeśli na przykład kod używa introspekcji lub wywołania metod Java z kodu natywnego, klasy te mogą nie zostać rozpoznane jako wymagane w podstawowym pliku DEX.

Jeśli otrzymasz java.lang.NoClassDefFoundError, musi ręcznie określić dodatkowe klasy wymagane w podstawowym pliku DEX , deklarując je we właściwości multiDexKeepProguard w swoim typie kompilacji. Jeśli klasa pasuje do plik multiDexKeepProguard, a następnie te zajęcia zostanie dodany do głównego pliku DEX.

właściwość multiDexKeepProguard

Plik multiDexKeepProguard używa tego samego formatu co ProGuard i obsługuje i całej gramatyki ProGuard. Więcej informacji o tym, jak dostosować informacje przechowywane w aplikacji, znajdziesz w artykule Wybierz kod, który chcesz zachować.

Plik określony w polu multiDexKeepProguard powinien zawierać -keep w dowolnej prawidłowej składni ProGuard. Przykład: -keep com.example.MyClass.class Możesz utworzyć plik o nazwie multidex-config.pro wygląda tak:

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

Jeśli chcesz określić wszystkie klasy w pakiecie, plik wygląda tak:

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

Następnie możesz zadeklarować ten plik dla danego typu kompilacji w ten sposób:

Odlotowe

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

Kotlin

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

Optymalizuj multidex w konstrukcjach deweloperskich

Konfiguracja multidex wymaga znacznie zwiększonego przetwarzania kompilacji ponieważ system kompilacji musi podejmować złożone decyzje dotyczące klas, muszą być uwzględnione w głównym pliku DEX i w których klasach można uwzględnić dodatkowych plików DEX. Oznacza to, że kompilacje przyrostowe korzystające z multidex zwykle są może potrwać dłużej i może spowolnić proces programowania.

Aby ograniczyć wydłużenie czasu przyrostowego kompilacji, użyj funkcji predexing, aby ponownie wykorzystywać dane wyjściowe w formacie multidex między kompilacjami. Wyodrębnianie treści przed deksowaniem korzysta z formatu ART dostępnego tylko na Androidzie 5.0 (poziom interfejsu API 21) i wyższy. Jeśli używasz Androida Studio, IDE automatycznie korzysta z predexingu podczas wdrażania aplikacji na urządzeniu z Androidem 5.0 (poziom interfejsu API 21) lub nowszym. Jeśli jednak uruchamiasz kompilacje Gradle z poziomu wiersza poleceń, musisz ustawić atrybut minSdkVersion do wersji 21 lub wyższej, by włączyć wstępne wczytywanie.

Aby zachować ustawienia kompilacja produkcyjna, możesz utworzyć 2 wersje swojej aplikacji używając różnych smaków produktów – jedna wersja, z rodzajem programistycznym i jedną wersją – różne wartości w kolumnach minSdkVersion, Jak widać:

Odlotowe

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")
}

Aby poznać więcej strategii, które pomagają zwiększyć szybkość kompilacji, z poziomu Android Studio lub polecenia przeczytaj artykuł Optymalizacja szybkości kompilacji. Więcej informacji o używaniu wariantów kompilacji znajdziesz tutaj: Skonfiguruj warianty kompilacji.

Wskazówka: jeśli masz różne warianty kompilacji dla różnych Multidex, dla każdego z nich możesz dostarczyć inny plik manifestu. wariantu, więc tylko plik dla interfejsu API poziomu 20 i niższych zmienia wartość Nazwa tagu <application>. Możesz też utwórz inną podklasę Application dla każdego wariantu, aby tylko podklasa dla interfejsu API poziomu 20 i niższych rozszerza klasę MultiDexApplication lub Wywołuje połączenie MultiDex.install(this).

Testowanie aplikacji multidex

Gdy piszesz testy instrumentacji dla aplikacji multidex, nie musisz niczego konfigurować jeśli używasz MonitoringInstrumentation lub . AndroidJUnitRunner z narzędzi pomiarowych. Jeśli używasz innego Instrumentation musisz zastąpić jej metodę onCreate() tym kodem:

Kotlin

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

Java

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