Tworzenie wielu pakietów APK

Uwaga: od sierpnia 2021 r. wszystkie nowe aplikacje muszą być publikowane jako pakiety aplikacji. Jeśli publikujesz aplikację w Google Play, utwórz i prześlij pakiet aplikacji na Androida. Gdy to zrobisz, Google Play automatycznie wygeneruje i udostępni zoptymalizowane pliki APK dla konfiguracji urządzenia każdego użytkownika, aby pobierał tylko kod i zasoby potrzebne do uruchomienia aplikacji. Publikowanie wielu plików APK jest przydatne, jeśli publikujesz w sklepie, który nie obsługuje formatu AAB. W takim przypadku musisz samodzielnie tworzyć, podpisywać i zarządzać plikami APK.

Chociaż w miarę możliwości lepiej jest utworzyć jeden plik APK, który będzie obsługiwał wszystkie urządzenia docelowe, może to spowodować, że plik APK będzie bardzo duży z powodu plików obsługujących różne gęstości ekranu lub interfejsów binarnych aplikacji (ABI). Jednym ze sposobów zmniejszenia rozmiaru pliku APK jest utworzenie kilku plików APK zawierających pliki przeznaczone do określonych gęstości ekranu lub ABI.

Gradle może tworzyć osobne APK-i zawierające tylko kod i zasoby przeznaczone dla każdej gęstości lub ABI. Na tej stronie opisaliśmy, jak skonfigurować kompilację, aby generować wiele plików APK. Jeśli chcesz utworzyć różne wersje aplikacji, które nie są oparte na gęstości ekranu lub ABI, użyj wariantów kompilacji.

Konfigurowanie kompilacji na potrzeby wielu plików APK

Aby skonfigurować kompilację na potrzeby wielu plików APK, dodaj blok splits do pliku build.gradle na poziomie modułu. W bloku splits podaj blok density, który określa, jak Gradle ma generować pliki APK dla poszczególnych gęstości, lub blok abi, który określa, jak Gradle ma generować pliki APK dla poszczególnych ABI. Możesz podać zarówno bloki gęstości, jak i bloki interfejsu ABI. System kompilacji utworzy plik APK dla każdej kombinacji gęstości i interfejsu ABI.

Konfigurowanie wielu plików APK pod kątem gęstości ekranu

Aby utworzyć osobne pliki APK dla różnych gęstości ekranu, dodaj blok density w bloku splits. W bloku density podaj listę żądanych gęstości ekranu i obsługiwanych rozmiarów ekranu. Używaj listy obsługiwanych rozmiarów ekranu tylko wtedy, gdy potrzebujesz określonych elementów <compatible-screens> w pliku manifestu każdego pliku APK.

Aby skonfigurować wiele plików APK pod kątem gęstości ekranu, użyj tych opcji Gradle DSL:

enable – skrypt Groovy, isEnable – skrypt Kotlin
Jeśli ustawisz tę wartość na true, Gradle wygeneruje kilka plików APK na podstawie zdefiniowanych przez Ciebie gęstości ekranu. Wartością domyślną jest false.
exclude
Określa rozdzieloną przecinkami listę gęstości, dla których nie chcesz, aby Gradle generował osobne pliki APK. Użyj opcji exclude, jeśli chcesz wygenerować pliki APK dla większości wartości gęstości, ale musisz wykluczyć kilka wartości gęstości, których nie obsługuje Twoja aplikacja.
reset()

Czyści domyślną listę gęstości ekranu. Używaj tylko w połączeniu z elementem include, aby określić gęstości, które chcesz dodać.

Ten fragment kodu ustawia listę gęstości na tylko ldpixxhdpi, wywołując funkcję reset() do wyczyszczenia listy, a potem używając funkcji include:

reset()                  // Clears the default list from all densities
                         // to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities to generate APKs
                         // for.
include
Określa listę rozdzielczości rozdzielonych przecinkami, dla których Gradle ma wygenerować pliki APK. Używaj tylko w połączeniu z opcją reset(), aby określić dokładną listę gęstości.
compatibleScreens

Określa listę zgodnych rozmiarów ekranu rozdzielonych przecinkami. W ten sposób w pliku manifestu każdego pakietu APK zostanie wstrzyknięty odpowiedni węzeł <compatible-screens>.

To ustawienie zapewnia wygodny sposób zarządzania gęstością pikseli i rozmiarami ekranu w tej samej sekcji build.gradle. Użycie usługi <compatible-screens> może jednak ograniczyć typy urządzeń, na których działa Twoja aplikacja. Informacje o innych sposobach obsługi różnych rozmiarów ekranu znajdziesz w omówieniu zgodności z ekranami.

Każdy plik APK, który jest oparty na gęstości ekranu, zawiera tag <compatible-screens> z określonymi ograniczeniami dotyczącymi typów ekranów, które obsługuje APK (nawet jeśli opublikujesz kilka plików APK), więc niektóre nowe urządzenia nie będą pasować do filtrów wielu plików APK. W związku z tym Gradle zawsze generuje dodatkowy uniwersalny plik APK zawierający zasoby dla wszystkich gęstości ekranu i nie zawiera tagu <compatible-screens>. Opublikuj ten uniwersalny plik APK wraz z plikami APK dla poszczególnych wartości gęstości, aby zapewnić alternatywne rozwiązanie dla urządzeń, które nie pasują do plików APK z tagiem <compatible-screens>.

W tym przykładzie generowany jest osobny plik APK dla każdej gęstości ekranu, z wyjątkiem wartości ldpi, xxhdpixxxhdpi. Aby to zrobić, użyj parametru exclude, aby usunąć te 3 gęstości z domyślnej listy wszystkich gęstości.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on screen density.
    density {

      // Configures multiple APKs based on screen density.
      enable true

      // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
      exclude "ldpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings for the manifest.
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

Kotlin

android {
    ...
    splits {

        // Configures multiple APKs based on screen density.
        density {

            // Configures multiple APKs based on screen density.
            isEnable = true

            // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
            exclude("ldpi", "xxhdpi", "xxxhdpi")

            // Specifies a list of compatible screen size settings for the manifest.
            compatibleScreens("small", "normal", "large", "xlarge")
        }
    }
}

Więcej informacji o dostosowywaniu różnych wersji aplikacji do konkretnych typów ekranów i urządzeń znajdziesz w artykule Zadeklarowanie obsługi ograniczonych ekranów.

Konfigurowanie wielu plików APK na potrzeby interfejsów ABI

Aby utworzyć osobne pliki APK dla różnych ABI, dodaj blok abi w bloku splits. W bloku abi podaj listę żądanych ABI.

Aby skonfigurować wiele plików APK na jeden ABI, użyj tych opcji Gradle DSL:

enable w przypadku Groovy lub isEnable w przypadku skryptu Kotlin
Jeśli ustawisz ten element na true, Gradle wygeneruje wiele plików APK na podstawie zdefiniowanych przez Ciebie ABI. Wartością domyślną jest false.
exclude
Określa listę ABI oddzielonych przecinkami, dla której nie chcesz, aby Gradle generował osobne pliki APK. Użyj opcji exclude, jeśli chcesz wygenerować pliki APK dla większości interfejsów ABI, ale musisz wykluczyć kilka interfejsów, których Twoja aplikacja nie obsługuje.
reset()

Czyści domyślną listę ABI. Używaj tylko w połączeniu z elementem include, aby określić ABI, które chcesz dodać.

Ten fragment kodu ustawia listę ABI na x86x86_64, wywołując funkcję reset(), aby ją wykasować, a potem używając funkcji include:

reset()                 // Clears the default list from all ABIs to no ABIs.
include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.
include
Określa listę ABI oddzieloną przecinkami, dla której Gradle ma wygenerować pliki APK. Używaj tylko w połączeniu z opcją reset(), aby określić dokładną listę interfejsów ABI.
universalApk dla Groovy lub isUniversalApk dla skryptu Kotlina.

Jeśli true, Gradle generuje uniwersalny plik APK oprócz plików APK dla poszczególnych ABI. Uniwersalny plik APK zawiera kod i zasoby dla wszystkich interfejsów ABI w jednym pliku APK. Wartością domyślną jest false.

Ta opcja jest dostępna tylko w bloku splits.abi. Podczas kompilowania wielu plików APK na podstawie gęstości ekranu Gradle zawsze generuje uniwersalny plik APK, który zawiera kod i zasoby dla wszystkich gęstości ekranu.

W tym przykładzie generowany jest osobny pakiet APK dla każdego ABI: x86x86_64. Aby to zrobić, użyj instrukcji reset(), aby zacząć od pustej listy interfejsów ABI, a następnie instrukcji include, aby utworzyć listę interfejsów ABI, z których każdy ma swój plik APK.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include "x86", "x86_64"

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      isEnable = true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include("x86", "x86_64")

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      isUniversalApk = false
    }
  }
}

Listę obsługiwanych interfejsów ABI znajdziesz w artykule Obsługiwane interfejsy ABI.

projekty bez kodu natywnego lub C++,

W przypadku projektów bez kodu natywnego lub C++ panel Warianty kompilacji ma 2 kolumny: ModułAktywna kompilacja (patrz rysunek 1).

Panel Tworzenie wariantów
Rysunek 1. Panel Generowanie wariantów zawiera 2 kolumny w przypadku projektów bez kodu natywnych/C++.

Wartość Aktywna wersja kompilacji w module określa wersję kompilacji, która jest wdrożona i widoczna w edytorze. Aby przełączać się między wariantami, kliknij komórkę Aktywny wariant kompilacji w module i wybierz odpowiedni wariant z pola listy.

Projekty z kodem natywnym lub C++

W przypadku projektów z kodem natywnym lub C++ panel Warianty kompilacji zawiera 3 kolumny: Moduł, Aktywna wersja kompilacjiAktywna ABI, jak pokazano na rysunku 2.

Rysunek 2. Panel Warianty kompilacji dodaje kolumnę Aktywny ABI do projektów z kodem natywnym lub C++.

Wartość Aktywna odmiana wersji w module określa wersję, która jest wdrażana i widoczna w edytorze. W przypadku modułów natywnych wartość Aktywny ABI określa ABI używany przez edytor, ale nie ma wpływu na to, co jest wdrażane.

Aby zmienić typ kompilacji lub ABI:

  1. Kliknij komórkę kolumny Aktywna wersja kompilacji lub Aktywna ABI.
  2. Wybierz odpowiedni wariant lub ABI z pola listy. Nowa synchronizacja zostanie uruchomiona automatycznie.

Zmiana dowolnej kolumny w aplikacji lub module biblioteki powoduje zastosowanie tej zmiany do wszystkich zależnych wierszy.

Konfigurowanie obsługi wersji

Domyślnie, gdy Gradle generuje wiele plików APK, każdy z nich ma te same informacje o wersji, które są określone w pliku build.gradle lub build.gradle.kts na poziomie modułu. Sklep Google Play nie zezwala na używanie wielu plików APK tej samej aplikacji z tymi samymi informacjami o wersji. Przed przesłaniem pliku APK do Sklepu Play musisz się upewnić, że każdy plik APK ma unikalny identyfikator versionCode.

Możesz skonfigurować plik build.gradle na poziomie modułu, aby zastąpić versionCode w przypadku każdego pliku APK. Po utworzeniu mapowania, które przypisuje unikalną wartość liczbową do każdego ABI i gęstości, dla których skonfigurujesz wiele plików APK, możesz zastąpić kod wersji wyjściowej wartością, która łączy kod wersji zdefiniowany w bloku defaultConfig lub productFlavors z wartością liczbową przypisaną do gęstości lub ABI.

W tym przykładzie APK dla ABI x86 ma wartość versionCode 2004, a ABI x86_64 – wartość versionCode 3004.

Przypisywanie kodów wersji z dużą liczbą, np. 1000, umożliwia późniejsze przypisywanie unikalnych kodów wersji, jeśli trzeba zaktualizować aplikację. Na przykład jeśli defaultConfig.versionCode w kolejnych aktualizacjach jest zastępowane przez 5, Gradle przypisuje do pliku APK x86 kod versionCode 2005, a do pliku APK x86_64 – kod 3005.

Wskazówka: jeśli wersja zawiera uniwersalny plik APK, przypisz mu versionCode mniejszy niż w przypadku innych plików APK. Sklep Google Play instaluje wersję aplikacji, która jest zgodna z urządzeniem docelowym i ma najwyższą wartość versionCode. Przypisanie niższej wartości versionCode do uniwersalnego pliku APK powoduje, że Sklep Google Play próbuje zainstalować jeden z plików APK, zanim użyje uniwersalnego pliku APK. Poniżej znajdziesz przykładowy kod, który nie zastępuje domyślnej wartości versionCode w uniwersalnym pakiecie APK.

Groovy

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3]

// For per-density APKs, create a similar map:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code doesn't override the version code for universal APKs.
    // However, because you want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the
      // version code for only the output APK, not for the variant itself. Skipping
      // this step causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3)

// For per-density APKs, create a similar map:
// val densityCodes = mapOf("mdpi" to 1, "hdpi" to 2, "xhdpi" to 3)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code doesn't override the version code for universal APKs.
            // However, because you want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

Więcej przykładów alternatywnych schematów kodów wersji znajdziesz w artykule Przypisywanie kodów wersji.

Tworzenie wielu plików APK

Po skonfigurowaniu pliku build.gradle na poziomie modułu lub pliku build.gradle.kts w celu utworzenia wielu plików APK kliknij Utwórz > Utwórz plik APK, aby utworzyć wszystkie pliki APK dla wybranego w tym momencie modułu w panelu Projekt. Gradle tworzy pliki APK dla każdej gęstości lub ABI w katalogu build/outputs/apk/ projektu.

Gradle kompiluje plik APK dla każdej gęstości lub interfejsu ABI, dla których skonfigurowano większą liczbę plików APK. Jeśli włączysz wiele plików APK zarówno dla różnych gęstości, jak i dla różnych ABI, Gradle utworzy plik APK dla każdej kombinacji gęstości i ABI.

Na przykład ten fragment kodu build.gradle umożliwia tworzenie wielu plików APK dla gęstości mdpihdpi oraz ABI x86x86_64:

Groovy

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

Kotlin

...
  splits {
    density {
      isEnable = true
      reset()
      include("mdpi", "hdpi")
    }
    abi {
      isEnable = true
      reset()
      include("x86", "x86_64")
    }
  }

Dane wyjściowe z przykładowej konfiguracji obejmują 4 pliki APK:

  • app-hdpiX86-release.apk: zawiera kod i zasoby dla gęstości hdpi i interfejsu ABI x86.
  • app-hdpiX86_64-release.apk: zawiera kod i zasoby dla gęstości hdpi i interfejsu ABI x86_64.
  • app-mdpiX86-release.apk: zawiera kod i zasoby dla gęstości mdpi i interfejsu ABI x86.
  • app-mdpiX86_64-release.apk: zawiera kod i zasoby dla gęstości mdpi i interfejsu ABI x86_64.

Podczas kompilowania wielu plików APK na podstawie gęstości ekranu Gradle zawsze generuje uniwersalny plik APK, który zawiera kod i zasoby dla wszystkich gęstości, a także pliki APK dla poszczególnych gęstości.

Podczas kompilowania wielu plików APK na podstawie ABI Gradle generuje plik APK zawierający kod i zasoby dla wszystkich ABI tylko wtedy, gdy w bloku splits.abi w pliku build.gradle (w przypadku Groovy) lub w bloku isUniversalApk = true w pliku build.gradle.kts (w przypadku skryptu Kotlin) określisz wartość universalApk true (w przypadku Groovy) lub isUniversalApk = true (w przypadku skryptu Kotlin).splits.abi

Format nazwy pliku APK

Podczas kompilowania wielu plików APK Gradle generuje nazwy plików APK za pomocą tego schematu:

modulename-screendensityABI-buildvariant.apk

Komponenty schematu:

modulename
Określa nazwę modułu, który jest kompilowany.
screendensity
Jeśli włączono wiele plików APK dla gęstości ekranu, określa gęstość ekranu pliku APK, np. mdpi.
ABI

Jeśli włączono wiele plików APK dla ABI, określa ABI pliku APK, na przykład x86.

Jeśli włączono wiele plików APK zarówno dla gęstości ekranu, jak i ABI, Gradle złącza nazwę gęstości z nazwą ABI, np.mdpiX86. Jeśli opcja universalApk jest włączona dla plików APK dla poszczególnych ABI, Gradle używa universal jako części ABI uniwersalnego pliku APK.

buildvariant
Określa wariant kompilacji, np. debug.

Na przykład podczas kompilowania pliku APK o gęstości ekranu mdpi na potrzeby wersji debugowania aplikacji myApp nazwa pliku APK to myApp-mdpi-debug.apk. Wersja wydania aplikacji myApp, która jest skonfigurowana do tworzenia wielu plików APK zarówno dla gęstości ekranu mdpi, jak i interfejsu ABI x86, ma nazwę pliku APK myApp-mdpiX86-release.apk.