다중 APK 빌드

가능하면 모든 대상 기기를 지원하는 단일 APK를 빌드하는 것이 좋지만, 여러 Application Binary Interface(ABI)를 지원하는 파일로 인해 APK가 매우 커질 수 있습니다. APK 크기를 줄이는 한 가지 방법은 특정 ABI의 파일이 포함된 다중 APK를 만드는 것입니다.

Gradle은 각 ABI에만 해당하는 코드와 리소스만 포함하는 별도의 APK를 만들 수 있습니다. 이 페이지에서는 여러 APK를 생성하도록 빌드를 구성하는 방법을 설명합니다. ABI를 기반으로 하지 않는 다양한 버전의 앱을 만들어야 하는 경우 대신 빌드 변형을 사용하세요.

다중 APK를 위한 빌드 구성

다중 APK의 빌드를 구성하려면 모듈 수준 build.gradle 파일에 splits 블록을 추가합니다. splits 블록 내에서 Gradle이 ABI별 APK를 생성하는 방법을 지정하는 abi 블록을 제공합니다.

ABI를 위한 다중 APK 구성

여러 ABI의 APK를 별도로 만들려면 splits 블록 내에 abi 블록을 추가합니다. abi 블록에서 선택한 ABI 목록을 제공합니다.

다음의 Gradle DSL 옵션은 ABI별로 다중 APK를 구성하는 데 사용됩니다.

enable(Groovy의 경우) 또는 isEnable(Kotlin 스크립트의 경우)
이 요소를 true로 설정하면 Gradle은 정의된 ABI를 기반으로 여러 APK를 생성합니다. 기본값은 false입니다.
exclude
Gradle에서 별도의 APK를 생성하지 않도록 하려는 ABI의 쉼표로 구분된 목록을 지정합니다. 대부분의 ABI에 대해 APK를 생성하되 앱에서 지원하지 않는 ABI를 몇 개 제외해야 하는 경우 exclude를 사용합니다.
reset()

기본 ABI 목록을 삭제합니다. 추가하려는 ABI를 지정하기 위해 include 요소와 결합된 경우에만 사용합니다.

다음 스니펫은 reset()를 호출하여 목록을 지운 다음 include를 사용하여 ABI 목록을 x86 및 x86_64로만 설정합니다.

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

Gradle이 APK를 생성할 ABI의 쉼표로 구분된 목록을 지정합니다. 정확한 ABI 목록을 지정하려면 반드시 reset()과 함께 사용합니다.

universalApk(Groovy의 경우) 또는 isUniversalApk(Kotlin 스크립트의 경우)

true인 경우 Gradle은 ABI별 APK 외에도 범용 APK를 생성합니다. 범용 APK에는 단일 APK의 모든 ABI에 관한 코드와 리소스가 포함되어 있습니다. 기본값은 false입니다.

다음 예에서는 각 ABI(x86 및 x86_64)에 별도의 APK를 생성합니다. 이렇게 하려면 reset()를 사용하여 빈 ABI 목록으로 시작한 다음 include를 사용하여 각각 APK를 가져오는 ABI 목록을 사용합니다.

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

지원되는 ABI 목록은 지원되는 ABI를 참고하세요.

네이티브/C++ 코드가 없는 프로젝트

네이티브/C++ 코드가 없는 프로젝트의 경우 Build Variants 패널에는 그림 1과 같이 Module과 Active Build Variant라는 두 개의 열이 있습니다.

네이티브/C++ 코드가 없는 프로젝트의 경우 두 개의 열이 있는 빌드 변형 패널
그림 1. 네이티브/C++ 코드가 없는 프로젝트의 경우 두 개의 열이 있는 빌드 변형 패널

모듈의 Active Build Variant 값은 배포되고 편집기에 표시되는 빌드 변형을 결정합니다. 변형 간에 전환하려면 모듈의 Active Build Variant 셀을 클릭하고 목록 필드에서 원하는 변형을 선택합니다.

네이티브/C++ 코드가 있는 프로젝트

네이티브/C++ 코드가 있는 프로젝트의 경우 Build Variants 패널에는 그림 2와 같이 Module, Active Build Variant, Active ABI라는 세 개의 열이 있습니다.

네이티브/C++ 코드가 있는 프로젝트의 경우 Active ABI 열이 추가된 빌드 변형 패널
그림 2. 네이티브/C++ 코드가 있는 프로젝트의 경우 Active ABI 열이 추가된 빌드 변형 패널

모듈의 Active Build Variant 값은 배포되고 편집기에 표시되는 빌드 변형을 결정합니다. 네이티브 모듈의 경우 Active ABI 값이 편집기에서 사용하는 ABI를 결정하지만 배포되는 항목에는 영향을 미치지 않습니다.

빌드 유형 또는 ABI를 변경하려면 다음 단계를 따르세요.

  1. Active Build Variant 또는 Active ABI 열의 셀을 클릭합니다.
  2. 목록 필드에서 선택한 변형 또는 ABI를 선택합니다. 새 동기화가 자동으로 실행됩니다.

앱 또는 라이브러리 모듈의 열을 변경하면 모든 종속 행에 변경사항이 적용됩니다.

버전 관리 구성

기본적으로 Gradle이 여러 APK를 생성할 때 각 APK는 모듈 수준 build.gradle 또는 build.gradle.kts 파일에 지정된 것과 동일한 버전 정보를 갖습니다. Google Play 스토어에서는 동일한 버전 정보를 가진 동일한 앱에 여러 APK를 허용하지 않으므로 각 APK에 고유한 versionCode가 있는지 확인한 후 Play 스토어에 업로드해야 합니다.

모듈 수준의 build.gradle 파일을 구성하여 각 APK의 versionCode를 재정의할 수 있습니다. 여러 APK를 구성하는 각 ABI에 고유한 숫자 값을 할당하는 매핑을 만들면 defaultConfig 또는 productFlavors 블록 내에 정의된 버전 코드와 ABI에 할당된 숫자 값을 결합하는 값으로 출력 버전 코드를 재정의할 수 있습니다.

다음 예에서 x86 ABI의 APK는 versionCode 2004를 가져오고 x86_64 ABI는 versionCode 3004를 가져옵니다.

버전 코드를 1,000과 같이 큰 단위로 할당하면 나중에 앱을 업데이트해야 할 때 고유한 버전 코드를 할당할 수 있습니다. 예를 들어 후속 업데이트에서 defaultConfig.versionCode가 5로 반복되면 Gradle은 x86 APK에 2005의 versionCode를 할당하고 x86_64 APK에 3005를 할당합니다.

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]

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)

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를 빌드한 후 Build > Build APK를 클릭하여 Project 창에서 현재 선택된 모듈의 모든 APK를 빌드합니다. Gradle은 프로젝트의 build/outputs/apk/ 디렉터리에 각 ABI의 APK를 만듭니다.

Gradle은 다중 APK를 구성하는 각 ABI의 APK를 빌드합니다.

예를 들어 다음 build.gradle 스니펫은 x86 및 x86_64 ABI에 대한 다중 APK 빌드를 사용 설정합니다.

Groovy

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

Kotlin

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

구성 예의 출력에는 다음 네 개의 APK가 포함됩니다.

  • app-X86-release.apk: x86 ABI의 코드와 리소스를 포함합니다.
  • app-X86_64-release.apk: x86_64 ABI의 코드와 리소스를 포함합니다.

ABI를 기반으로 여러 APK를 빌드할 때 build.gradle 파일(Groovy의 경우)의 splits.abi 블록에 universalApk true를 지정하거나 build.gradle.kts 파일(Kotlin 스크립트의 경우)의 splits.abi 블록에 isUniversalApk = true를 지정하는 경우에만 Gradle은 모든 ABI의 코드와 리소스를 포함하는 APK를 생성합니다.

APK 파일 이름 형식

다중 APK를 빌드할 때 Gradle은 다음 스키마를 사용하여 APK 파일 이름을 생성합니다.

modulename-ABI-buildvariant.apk

체계 구성요소는 다음과 같습니다.

modulename
빌드 중인 모듈 이름을 지정합니다.
ABI
ABI용 다중 APK가 사용 설정된 경우 APK의 ABI를 지정합니다(예: x86).
buildvariant
빌드 중인 빌드 변형을 지정합니다(예: debug).