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 Android App Bundle. Gdy to zrobisz, Google Play automatycznie wygeneruje i będzie udostępniać zoptymalizowane pliki APK dla każdej konfiguracji urządzenia. Dzięki temu każdy użytkownik pobierze tylko kod i zasoby potrzebne do uruchomienia Twojej aplikacji. Publikowanie wielu plików APK jest przydatne, jeśli publikujesz w sklepie, który nie obsługuje formatu aplikacji na Androida. W takim przypadku musisz samodzielnie utworzyć i podpisać każdy plik APK oraz nim zarządzać.

Lepiej utworzyć jeden plik APK, który będzie w miarę możliwości obsługiwać wszystkie urządzenia docelowe. Może to jednak spowodować utworzenie bardzo dużego pliku ze względu na pliki obsługujące wiele gęstości ekranu lub interfejsy binarne aplikacji. Jednym ze sposobów zmniejszenia rozmiaru pliku APK jest utworzenie wielu plików APK zawierających pliki o określonej gęstości ekranu lub interfejsach ABI.

Gradle może tworzyć osobne pliki APK zawierające tylko kod i zasoby właściwe dla poszczególnych gęstości lub interfejsu ABI. Na tej stronie dowiesz się, jak skonfigurować kompilację pod kątem generowania wielu plików APK. Jeśli musisz utworzyć różne wersje aplikacji, które nie opierają się na gęstości ekranu ani interfejsie ABI, użyj wariantów kompilacji.

Konfigurowanie kompilacji pod kątem 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, w jaki sposób Gradle ma generować pliki APK według gęstości, lub blok abi, który określa sposób generowania przez Gradle plików APK dla poszczególnych interfejsów ABI. Możesz podać zarówno bloki gęstości, jak i bloków interfejsu ABI, a system kompilacji utworzy pakiet 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 wewnątrz bloku splits. W bloku density podaj listę żądanych gęstości ekranu i zgodnych rozmiarów ekranu. Z listy zgodnych rozmiarów ekranu używaj tylko wtedy, gdy w pliku manifestu każdego pakietu APK potrzebujesz konkretnych elementów <compatible-screens>.

Do konfigurowania wielu plików APK pod kątem gęstości ekranu służą te opcje DSL w Gradle:

enable – Groovy, isEnable – skrypt Kotlin
Jeśli ustawisz ten element na true, Gradle wygeneruje wiele 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 Gradle nie ma generować osobnych plików APK. Użyj exclude, jeśli chcesz generować pliki APK dla większości gęstości, ale musisz wykluczyć kilka gęstości, których Twoja aplikacja nie obsługuje.
reset()

Czyści listę domyślnych 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 tylko na ldpi i xxhdpi, wywołując reset(), aby wyczyścić listę, a następnie używając parametru 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 rozdzieloną przecinkami listę gęstości, dla których Gradle ma generować pliki APK. Używaj go tylko w połączeniu z właściwością reset(), aby określić dokładną listę gęstości.
compatibleScreens

Określa rozdzieloną przecinkami listę zgodnych rozmiarów ekranu. Spowoduje to wstawienie pasującego węzła <compatible-screens> w pliku manifestu dla każdego pliku APK.

To ustawienie pozwala w wygodny sposób zarządzać gęstością i rozmiarami ekranu w tej samej sekcji build.gradle. Jednak użycie właściwości <compatible-screens> może ograniczyć typy urządzeń, na których działa Twoja aplikacja. Informacje o alternatywnych sposobach obsługi różnych rozmiarów ekranów znajdziesz w omówieniu zgodności ekranu.

Każdy plik APK oparty na gęstości ekranu zawiera tag <compatible-screens> z określonymi ograniczeniami dotyczącymi typów ekranów obsługiwanych przez ten plik – nawet jeśli publikujesz kilka plików APK. Niektóre nowe urządzenia nie pasują do wielu filtrów plików APK. Dlatego Gradle zawsze generuje dodatkowy uniwersalny plik APK, który zawiera zasoby dla wszystkich gęstości ekranu i nie zawiera tagu <compatible-screens>. Opublikuj ten uniwersalny plik APK wraz z pakietami APK według gęstości, aby udostępnić aplikację zastępczą dla urządzeń, które nie pasują do plików APK z tagiem <compatible-screens>.

Ten przykład generuje oddzielny plik APK dla każdej gęstości ekranu z wyjątkiem ldpi, xxhdpi i xxxhdpi. W tym celu korzysta się z metody exclude do usuwania tych 3 gęstości z domyślnej listy wszystkich gęstości.

Odlotowy

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

Listę nazw gęstości i nazw rozmiarów ekranu znajdziesz w artykule Obsługa różnych rozmiarów ekranu. Więcej informacji o dostosowywaniu różnych wariantów kompilacji aplikacji do określonych typów ekranów i urządzeń znajdziesz w sekcji Deklarowanie ograniczonej obsługi ekranu.

Konfigurowanie wielu plików APK dla interfejsów ABI

Aby tworzyć osobne pliki APK dla różnych interfejsów ABI, dodaj blok abi w bloku splits. W bloku abi podaj listę odpowiednich interfejsów ABI.

Do konfigurowania wielu plików APK dla każdego interfejsu ABI służą te opcje DSL w Gradle:

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 interfejsów ABI. Wartością domyślną jest false.
exclude
Określa rozdzieloną przecinkami listę interfejsów ABI, dla których Gradle nie ma generować osobnych plików APK. Użyj exclude, jeśli chcesz generować pliki APK dla większości interfejsów ABI, ale musisz wykluczyć kilka interfejsów ABI, których Twoja aplikacja nie obsługuje.
reset()

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

Ten fragment kodu ustawia listę interfejsów ABI tylko na x86 i x86_64. Aby to zrobić, wywołaj reset(), aby wyczyścić listę, a potem użyj polecenia 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 rozdzieloną przecinkami listę interfejsów ABI, dla których Gradle ma generować pliki APK. Używaj tylko w połączeniu z reset(), aby określić dokładną listę interfejsów ABI.
universalApk w przypadku Groovy lub isUniversalApk w przypadku skryptu Kotlin

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

Pamiętaj, że ta opcja jest dostępna tylko w bloku splits.abi. Gdy tworzysz wiele plików APK opartych na gęstości ekranu, Gradle zawsze generuje uniwersalny plik APK, który zawiera kod i zasoby dostosowane do każdej gęstości ekranu.

Ten przykład generuje oddzielny plik APK dla każdego interfejsu ABI: x86 i x86_64. W tym celu użyj parametru reset(), aby rozpocząć od pustej listy interfejsów ABI, a następnie include z listą interfejsów ABI, z których każdy pobierze plik APK.

Odlotowy

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 sekcji Obsługiwane interfejsy ABI.

Projekty bez kodu natywnego/C++

W przypadku projektów bez kodu natywnego/C++ panel Warianty kompilacji zawiera 2 kolumny: Moduł i Aktywny wariant kompilacji, jak widać na ilustracji 1.

Panel wariantów kompilacji
Rysunek 1. W przypadku projektów bez kodu natywnego/C++ panel Tworzenie wariantów zawiera 2 kolumny.

Wartość Aktywny wariant kompilacji modułu określa wariant kompilacji, który jest wdrożony i widoczny w edytorze. Aby przełączać się między wariantami, kliknij komórkę Aktywna wersja kompilacji obok modułu i wybierz odpowiedni wariant z pola listy.

Projekty z kodem natywnym/C++

W przypadku projektów z kodem natywnym/C++ panel Warianty kompilacji zawiera 3 kolumny: Module, Aktywny wariant kompilacji i Aktywny interfejs ABI, jak widać na ilustracji 2.

Rysunek 2. W przypadku projektów z kodem natywnym/C++ panel Warianty kompilacji dodaje kolumnę Aktywny interfejs ABI.

Wartość Aktywny wariant kompilacji modułu określa wariant kompilacji, który jest wdrożony i widoczny w edytorze. W przypadku modułów natywnych wartość Active ABI określa interfejs ABI używany przez edytor, ale nie ma wpływu na to, co jest wdrażane.

Aby zmienić typ kompilacji lub interfejs ABI:

  1. Kliknij komórkę kolumny Wersja aktywnej kompilacji lub Aktywny ABI.
  2. W polu listy wybierz odpowiedni wariant lub interfejs ABI. Automatycznie zostanie uruchomiona nowa synchronizacja.

Zmiana jednej z kolumn dla aplikacji lub modułu biblioteki spowoduje zastosowanie zmiany do wszystkich wierszy zależnych.

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 publikowanie wielu plików APK tej samej aplikacji o tych samych informacjach o wersji. Zanim prześlesz pliki do Sklepu Play, musisz się upewnić, że każdy z nich ma unikalny versionCode.

Możesz skonfigurować plik build.gradle na poziomie modułu tak, aby zastępował versionCode w przypadku każdego pliku APK. Tworząc mapowanie, które przypisuje unikalną wartość liczbową dla każdego interfejsu ABI i gęstości, dla których konfigurujesz wiele plików APK, możesz zastąpić wyjściowy kod wersji wartością łączącą kod wersji zdefiniowany w bloku defaultConfig lub productFlavors z wartością liczbową przypisaną do gęstości lub interfejsu ABI.

W poniższym przykładzie pakiet APK dla interfejsu ABI x86 uzyska versionCode z 2004 r., a interfejs ABI x86_64versionCode 3004.

Przypisanie kodów wersji w dużych przyrostach, np. 1000, umożliwia późniejsze przypisanie unikalnych kodów wersji, gdy musisz zaktualizować aplikację. Jeśli na przykład w kolejnej aktualizacji defaultConfig.versionCode powtórzy się na 5, Gradle przypisze wartość versionCode 2005 do pliku APK x86 i 3005 plikowi APK x86_64.

Wskazówka: jeśli kompilacja zawiera uniwersalny plik APK, przypisz mu versionCode mniejszą wartość niż w przypadku innych plików APK. Sklep Google Play instaluje wersję Twojej aplikacji, która jest zarówno zgodna z urządzeniem docelowym, jak i ma najwyższą wartość versionCode, dlatego przypisanie do uniwersalnego pliku APK niższej wartości versionCode sprawi, że Sklep Google Play spróbuje zainstalować jeden z Twoich plików APK, zanim wróci do uniwersalnego pliku APK. Poniższy przykładowy kod pozwala to uniknąć zastępowania domyślnej wartości versionCode uniwersalnego pliku APK.

Odlotowy

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 kodu wersji znajdziesz w artykule o przypisywaniu kodów wersji.

Tworzenie wielu pakietów APK

Gdy skonfigurujesz plik build.gradle lub build.gradle.kts na poziomie modułu pod kątem tworzenia wielu pakietów APK, kliknij Utwórz > Utwórz plik APK, aby w panelu Projekt utworzyć wszystkie pliki APK dla wybranego modułu. Gradle tworzy pliki APK dla każdej gęstości lub interfejsu ABI w katalogu build/outputs/apk/ projektu.

Gradle tworzy pakiet APK dla każdej gęstości lub interfejsu ABI, dla którego konfigurujesz wiele plików APK. Jeśli włączysz wiele plików APK zarówno dla gęstości, jak i interfejsów ABI, Gradle utworzy plik APK dla każdej kombinacji gęstości i interfejsu ABI.

Na przykład ten fragment kodu build.gradle umożliwia utworzenie wielu plików APK o gęstościach mdpi i hdpi, a także interfejsy ABI x86 i x86_64:

Odlotowy

...
  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ą te 4 pliki APK:

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

Gdy tworzysz wiele plików APK opartych na gęstości ekranu, Gradle zawsze generuje uniwersalny plik APK, który zawiera kod i zasoby na potrzeby wszystkich gęstości, a nie tylko pakiety APK odpowiadające konkretnej gęstości.

Gdy tworzysz wiele plików APK opartych na interfejsie ABI, Gradle generuje plik APK zawierający kod i zasoby dla wszystkich interfejsów ABI tylko wtedy, gdy określisz universalApk true w bloku splits.abi w pliku build.gradle (dla Groovy) lub isUniversalApk = true w bloku splits.abi w pliku build.gradle.kts (dla skryptu Kotlin).

Format nazwy pliku APK

Gdy tworzysz wiele plików APK, Gradle generuje ich nazwy zgodnie z tym schematem:

modulename-screendensityABI-buildvariant.apk

Komponenty schematu to:

modulename
Określa nazwę tworzonego modułu.
screendensity
Jeśli włączonych jest wiele plików APK przeznaczonych do gęstości ekranu, określ gęstość ekranu dla tego pliku, np. mdpi.
ABI

Jeśli włączonych jest wiele plików APK dla interfejsu ABI, określ interfejs ABI pliku APK, np. x86.

Jeśli włączonych jest wiele plików APK zarówno pod kątem gęstości ekranu, jak i interfejsu ABI, Gradle łączy nazwę gęstości z nazwą interfejsu ABI, na przykład mdpiX86. Jeśli universalApk jest włączone dla plików APK dla poszczególnych interfejsów ABI, Gradle używa elementu universal jako części ABI uniwersalnej nazwy pliku APK.

buildvariant
Wskazuje tworzony wariant kompilacji, np. debug.

Jeśli na przykład tworzysz plik APK służący do zagęszczenia ekranu w wersji mdpi na potrzeby debugowania wersji myApp, nazwa pliku APK to myApp-mdpi-debug.apk. Wersja aplikacji myApp skonfigurowana pod kątem tworzenia wielu plików APK dla gęstości ekranu mdpi i interfejsu ABI x86 ma nazwę pliku APK o nazwie myApp-mdpiX86-release.apk.