Создайте несколько APK

Внимание: с августа 2021 года все новые приложения должны публиковаться в виде пакетов приложений. Если вы публикуете свое приложение в Google Play, создайте и загрузите пакет Android App Bundle . Когда вы это сделаете, Google Play автоматически создаст и предоставит оптимизированные APK-файлы для конфигурации устройства каждого пользователя, поэтому они загружают только тот код и ресурсы, которые им необходимы для запуска вашего приложения. Публикация нескольких APK-файлов полезна, если вы публикуете их в магазине, который не поддерживает формат AAB. В этом случае вы должны самостоятельно создавать, подписывать и управлять каждым APK.

Хотя лучше по возможности создать один APK для поддержки всех целевых устройств, это может привести к очень большому APK из-за файлов, поддерживающих несколько плотностей экрана или двоичных интерфейсов приложений (ABI). Один из способов уменьшить размер вашего APK — создать несколько APK , содержащих файлы для определенной плотности экрана или ABI.

Gradle может создавать отдельные APK-файлы, содержащие только код и ресурсы, специфичные для каждой плотности или ABI. На этой странице описано, как настроить сборку для создания нескольких APK. Если вам нужно создать разные версии вашего приложения, не основанные на плотности экрана или ABI, вместо этого используйте варианты сборки .

Настройте свою сборку для нескольких APK

Чтобы настроить сборку для нескольких APK, добавьте блок splits в файл build.gradle на уровне модуля. В блоке splits укажите блок density , который определяет, как Gradle должен генерировать APK для каждой плотности, или блок abi , который определяет, как Gradle должен генерировать APK для каждого ABI. Вы можете предоставить блоки плотности и ABI, и система сборки создаст APK для каждой комбинации плотности и ABI.

Настройка нескольких APK для плотности экрана

Чтобы создать отдельные APK-файлы для разной плотности экрана, добавьте блок density внутри блока splits . В блоке density укажите список желаемой плотности экрана и совместимых размеров экрана. Используйте список совместимых размеров экрана только в том случае, если вам нужны определенные элементы <compatible-screens> в манифесте каждого APK.

Следующие параметры Gradle DSL используются для настройки нескольких APK для плотности экрана:

enable для Groovy, isEnable для сценария Kotlin
Если вы установите для этого элемента значение true , Gradle создаст несколько APK-файлов на основе определенной вами плотности экрана. Значение по умолчанию false .
exclude
Указывает разделенный запятыми список плотностей, для которых Gradle не должен создавать отдельные APK. Используйте exclude , если вы хотите создавать APK-файлы для большинства плотностей, но вам необходимо исключить несколько плотностей, которые ваше приложение не поддерживает.
reset()

Очищает список плотностей экрана по умолчанию. Используйте только в сочетании с элементом include , чтобы указать плотность, которую вы хотите добавить.

Следующий фрагмент устанавливает в списке плотностей только ldpi и xxhdpi , вызывая reset() для очистки списка, а затем используя include :

reset()                  // Clears the default list from all densities
                         // to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities to generate APKs
                         // for.
include
Указывает разделенный запятыми список плотностей, для которых Gradle должен генерировать APK. Используйте только в сочетании с reset() чтобы указать точный список плотностей.
compatibleScreens

Указывает список совместимых размеров экрана, разделенный запятыми. При этом в манифест для каждого APK добавляется соответствующий узел <compatible-screens> .

Этот параметр обеспечивает удобный способ управления плотностью и размером экрана в одном разделе build.gradle . Однако использование <compatible-screens> может ограничить типы устройств, с которыми работает ваше приложение. Альтернативные способы поддержки экранов разных размеров см. в обзоре совместимости экранов .

Поскольку каждый APK, основанный на плотности экрана, включает тег <compatible-screens> > с конкретными ограничениями относительно того, какие типы экранов поддерживает APK (даже если вы публикуете несколько APK), некоторые новые устройства не соответствуют вашим множественным фильтрам APK. Таким образом, Gradle всегда генерирует дополнительный универсальный APK, который содержит ресурсы для всех плотностей экрана и не включает тег <compatible-screens> . Опубликуйте этот универсальный APK-файл вместе с APK-файлами для каждой плотности, чтобы обеспечить запасной вариант для устройств, которые не соответствуют APK-файлам с тегом <compatible-screens> >.

В следующем примере создается отдельный APK для каждой плотности экрана, кроме ldpi , xxhdpi и xxxhdpi . Это делается с помощью exclude , чтобы удалить эти три плотности из списка всех плотностей по умолчанию.

классный

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'
    }
  }
}

Котлин

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

Дополнительные сведения о настройке различных вариантов сборки вашего приложения для конкретных типов экранов и устройств см. в разделе Объявление ограниченной поддержки экранов .

Настройка нескольких APK для ABI

Чтобы создать отдельные APK-файлы для разных ABI, добавьте блок abi внутри блока splits . В блоке abi укажите список желаемых ABI.

Следующие параметры Gradle DSL используются для настройки нескольких APK для каждого ABI:

enable для Groovy или isEnable для сценария Kotlin
Если вы установите для этого элемента значение true , Gradle создаст несколько APK на основе определенных вами ABI. Значение по умолчанию false .
exclude
Указывает разделенный запятыми список ABI, для которых Gradle не должен создавать отдельные APK. Используйте exclude , если вы хотите создать APK для большинства ABI, но вам необходимо исключить несколько ABI, которые ваше приложение не поддерживает.
reset()

Очищает список ABI по умолчанию. Используйте только в сочетании с элементом include , чтобы указать ABI, которые вы хотите добавить.

Следующий фрагмент устанавливает в списке ABI только x86 и x86_64 , вызывая reset() для очистки списка, а затем используя 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
Указывает разделенный запятыми список ABI, для которых Gradle должен генерировать APK. Используйте только в сочетании с reset() чтобы указать точный список ABI.
universalApk для Groovy или isUniversalApk для сценария Kotlin

Если true , Gradle генерирует универсальный APK в дополнение к APK для каждого ABI. Универсальный APK содержит код и ресурсы для всех ABI в одном APK. Значение по умолчанию false .

Обратите внимание, что эта опция доступна только в блоке splits.abi . При создании нескольких APK на основе плотности экрана Gradle всегда создает универсальный APK, содержащий код и ресурсы для всех плотностей экрана.

В следующем примере создается отдельный APK для каждого ABI: x86 и x86_64 . Это делается с помощью reset() которая начинается с пустого списка ABI, а затем include список ABI, каждый из которых получает APK.

классный

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

Котлин

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

Список поддерживаемых ABI см. в разделе Поддерживаемые ABI .

Проекты без собственного кода/кода C++

Для проектов без собственного кода/кода C++ на панели «Варианты сборки» есть два столбца: «Модуль» и «Активный вариант сборки» , как показано на рисунке 1.

Панель «Варианты сборки»
Рис. 1. На панели «Варианты сборки» есть два столбца для проектов без собственного кода/кода C++.

Значение Active Build Variant для модуля определяет вариант сборки, который развертывается и отображается в редакторе. Чтобы переключиться между вариантами, щелкните ячейку «Активный вариант сборки» для модуля и выберите нужный вариант из поля списка.

Проекты с собственным кодом/C++

Для проектов с собственным кодом/кодом C++ на панели «Варианты сборки» есть три столбца: «Модуль» , «Активный вариант сборки » и «Активный ABI» , как показано на рисунке 2.

Рис. 2. На панели «Варианты сборки» добавлен столбец «Активный ABI» для проектов с собственным кодом/кодом C++.

Значение Active Build Variant для модуля определяет вариант сборки, который будет развернут и виден в редакторе. Для собственных модулей значение Active ABI определяет ABI, который использует редактор, но не влияет на то, что развертывается.

Чтобы изменить тип сборки или ABI:

  1. Щелкните ячейку столбца «Активный вариант сборки» или «Активный ABI» .
  2. Выберите желаемый вариант или ABI из поля списка. Новая синхронизация запускается автоматически.

При изменении любого столбца для модуля приложения или библиотеки изменения применяются ко всем зависимым строкам.

Настройка управления версиями

По умолчанию, когда Gradle генерирует несколько APK, каждый APK имеет одинаковую информацию о версии, указанную в файле build.gradle или build.gradle.kts на уровне модуля. Поскольку в магазине Google Play не допускается использование нескольких APK для одного и того же приложения с одинаковой информацией о версии, вам необходимо убедиться, что каждый APK имеет уникальный versionCode прежде чем загружать его в Play Store.

Вы можете настроить файл build.gradle на уровне модуля, чтобы переопределить versionCode для каждого APK. Создав сопоставление, которое назначает уникальное числовое значение для каждого ABI и плотности, для которых вы настраиваете несколько APK, вы можете переопределить выходной код версии значением, которое объединяет код версии, определенный в блоке defaultConfig или productFlavors , с числовым значением, присвоенным плотность или ABI.

В следующем примере APK для ABI x86 получает versionCode 2004, а ABI x86_64 получает versionCode 3004.

Назначение кодов версий с большим шагом, например 1000, позволяет вам позже назначать уникальные коды версий, если вам нужно обновить приложение. Например, если defaultConfig.versionCode выполняет итерацию до 5 в последующем обновлении, Gradle присваивает versionCode 2005 APK x86 и 3005 APK x86_64 .

Совет. Если ваша сборка включает универсальный APK, присвойте ему versionCode , который ниже, чем у любого другого APK. Поскольку Google Play Store устанавливает версию вашего приложения, совместимую с целевым устройством и имеющую самый высокий versionCode , присвоение более низкого versionCode универсальному APK гарантирует, что Google Play Store попытается установить один из ваших APK, прежде чем вернуться к универсальному. АПК. Следующий пример кода решает эту проблему, не переопределяя versionCode универсального APK по умолчанию.

классный

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

Котлин

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

Дополнительные примеры альтернативных схем кодов версий см. в разделе Назначение кодов версий .

Создайте несколько APK

После того как вы настроите файл build.gradle или build.gradle.kts на уровне модуля для создания нескольких APK, нажмите «Сборка» > «Создать APK» , чтобы создать все APK для выбранного в данный момент модуля на панели «Проект» . Gradle создает APK-файлы для каждой плотности или ABI в каталоге build/outputs/apk/ проекта.

Gradle создает APK для каждой плотности или ABI, для которых вы настраиваете несколько APK. Если вы включите несколько APK для обеих плотностей и ABI, Gradle создаст APK для каждой комбинации плотности и ABI.

Например, следующий фрагмент build.gradle позволяет создавать несколько APK для плотностей mdpi и hdpi , а также ABI x86 и x86_64 :

классный

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

Котлин

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

Выходные данные примера конфигурации включают следующие 4 APK:

  • app-hdpiX86-release.apk : содержит код и ресурсы для плотности hdpi и x86 ABI.
  • app-hdpiX86_64-release.apk : содержит код и ресурсы для плотности hdpi и x86_64 ABI.
  • app-mdpiX86-release.apk : содержит код и ресурсы для плотности mdpi и x86 ABI.
  • app-mdpiX86_64-release.apk : содержит код и ресурсы для плотности mdpi и x86_64 ABI.

При создании нескольких APK на основе плотности экрана Gradle всегда создает универсальный APK, который включает в себя код и ресурсы для всех плотностей в дополнение к APK для каждой плотности.

При создании нескольких APK на основе ABI Gradle создает APK, который включает код и ресурсы для всех ABI, только если вы укажете universalApk true в блоке splits.abi в файле build.gradle (для Groovy) или isUniversalApk = true в splits.abi в файле build.gradle.kts (для сценария Kotlin).

Формат имени APK-файла

При создании нескольких APK Gradle генерирует имена файлов APK, используя следующую схему:

modulename - screendensity ABI - buildvariant .apk

Компонентами схемы являются:

modulename
Указывает имя создаваемого модуля.
screendensity
Если включено несколько APK для плотности экрана, указывает плотность экрана для APK, например mdpi .
ABI

Если включено несколько APK для ABI, указывает ABI для APK, например x86 .

Если включены несколько APK-файлов как для плотности экрана, так и для ABI, Gradle объединяет имя плотности с именем ABI, например mdpiX86 . Если universalApk включен для APK-файлов для каждого ABI, Gradle использует universal в качестве части ABI универсального имени файла APK.

buildvariant
Указывает собираемый вариант сборки, например debug .

Например, при создании APK плотности экрана mdpi для отладочной версии myApp имя файла APK — myApp-mdpi-debug.apk . Выпускная версия myApp, настроенная для создания нескольких APK-файлов как для плотности экрана mdpi , так и для x86 ABI, имеет имя APK-файла myApp-mdpiX86-release.apk .