이제 두 번째 Android 11 개발자 프리뷰를 사용할 수 있습니다. 테스트해 보고 의견을 공유하세요.

다중 APK 빌드

앱을 Google Play에 게시하면 Android App Bundle을 빌드하고 업로드해야 합니다. 이렇게 하면 Google Play에서 각 사용자의 기기 설정에 최적화된 APK를 자동으로 생성하여 제공하므로 사용자는 앱을 실행하는 데 필요한 코드와 리소스만 다운로드하면 됩니다. Google Play에 게시하지 않는다면 다중 APK 게시가 유용하지만 각 APK를 직접 빌드, 서명 및 관리해야 합니다.

가능한 모든 타겟 기기를 지원하기 위해 단일 APK를 빌드해야 하지만 이 경우 다양한 화면 밀도 또는 Application Binary Interface(ABI)를 지원하는 데 필요한 파일 때문에 APK가 매우 커질 수 있습니다. APK 크기를 줄이는 한 가지 방법은 특정 화면 밀도 또는 ABI의 파일이 포함되어 있는 다중 APK를 만드는 것입니다.

Gradle은 각 밀도 또는 ABI에 관련된 코드와 리소스만 포함되어 있는 별도의 APK를 만들 수 있습니다. 이 페이지에서는 빌드를 구성하여 다중 APK를 생성하는 방법을 설명합니다. 화면 밀도나 ABI에 기반하지 않는 다른 버전의 앱을 만들어야 한다면 빌드 변형을 대신 사용하면 됩니다.

다중 APK를 위한 빌드 구성

다중 APK를 위한 빌드를 구성하려면 splits 블록을 모듈 수준 build.gradle 파일에 추가합니다. splits 블록 내에서 Gradle이 밀도별 APK를 생성해야 하는 방법을 지정하는 density 블록 또는 Gradle이 ABI별 APK를 생성해야 하는 방법을 지정하는 abi 블록을 제공합니다. 개발자가 밀도와 ABI 블록을 모두 제공하면 빌드 시스템에서 각 밀도 및 ABI 조합을 위한 APK를 만듭니다.

화면 밀도를 위한 다중 APK 구성

다양한 화면 밀도를 위한 별도의 APK를 만들려면 splits 내에 density 블록을 추가하세요. density 블록에서 원하는 화면 밀도 및 호환되는 화면 크기의 목록을 제공합니다. 호환되는 화면 크기의 목록은 각 APK의 manifest에 특정 <compatible-screens> 요소가 필요한 경우에만 사용해야 합니다.

아래 Gradle DSL 옵션은 화면 밀도를 위한 다중 APK를 구성하는 데 사용됩니다.

enable
이 요소를 true로 설정하면 Gradle은 개발자가 정의한 화면 밀도에 기반하여 다중 APK를 생성합니다. 기본값은 false입니다.
exclude
Gradle이 별도의 APK를 생성하지 않아야 하는 밀도의 쉼표로 구분된 목록을 지정합니다. 앱에서 지원하지 않는 몇몇 밀도를 제외하고 대부분의 밀도에 APK를 생성하려면 exclude를 사용하세요.
reset()
화면 밀도의 기본 목록을 삭제합니다. include 요소와 결합하여 추가하려는 밀도를 지정할 때만 사용합니다. 다음 스니펫은 reset()를 호출하여 목록을 삭제한 다음 include를 사용하여 밀도 목록을 ldpixxhdpi로 설정합니다.
    reset()  // Clears the default list from all densities to no densities.
    include "ldpi", "xxhdpi" // Specifies the two densities we want to generate APKs for.
    
include
Gradle이 APK를 생성해야 하는 밀도의 쉼표로 구분된 목록을 지정합니다. 정확한 밀도 목록을 지정하려면 reset()만 함께 사용합니다.
compatibleScreens
호환되는 화면 크기의 쉼표로 구분된 목록을 지정합니다. 이렇게 하면 각 APK의 manifest에 일치하는 <compatible-screens> 노드가 삽입됩니다. 이 설정으로 동일한 build.gradle 섹션에서 화면 밀도와 화면 크기를 모두 편리하게 관리할 수 있습니다. 그러나 <compatible-screens>를 사용하면 앱이 호환되는 기기 유형이 제한될 수 있습니다. 다양한 화면 크기를 지원하는 다른 방법은 여러 화면 지원을 참조하세요.

화면 밀도에 기반하는 각 APK에는 APK가 지원하는 화면 유형에 관한 특정 제한이 있는 <compatible-screens> 태그가 포함되므로 여러 APK를 게시하더라도 일부 새 기기는 다중 APK 필터와 일치하지 않습니다. 따라서 Gradle은 항상 모든 화면 밀도를 위한 애셋을 포함하고 <compatible-screens> 태그를 포함하지 않는 추가적인 범용 APK를 생성합니다. 이 범용 APK를 밀도별 APK와 함께 게시하여 <compatible-screens> 태그가 있는 APK와 일치하지 않는 기기에 대체를 제공해야 합니다.

다음 예에서는 ldpi, xxhdpixxxhdpi를 제외하고 지원되는 화면 범위에 나열된 각 화면 밀도에 별도의 APK를 생성합니다. 이 작업은 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 Gradle should not create multiple APKs for.
          exclude "ldpi", "xxhdpi", "xxxhdpi"

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

밀도 이름과 화면 크기 이름의 목록은 여러 화면 지원 방법을 참조하세요. 앱을 특정 화면 유형과 기기에 배포하는 방법에 관한 자세한 내용은 특정 화면에 배포를 참조하세요.

ABI를 위한 다중 APK 구성

다양한 ABI를 위한 별도의 APK를 만들려면 splits 블록 내에 abi 블록을 추가합니다. abi 블록에서 원하는 ABI 목록을 제공합니다.

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

enable
이 요소를 true로 설정하면 Gradle은 개발자가 정의한 ABI에 기반하여 다중 APK를 생성합니다. 기본값은 false입니다.
exclude
Gradle이 별도의 APK를 생성하지 않아야 하는 ABI의 쉼표로 구분된 목록을 지정합니다. 앱에서 지원하지 않는 몇몇 ABI를 제외하고 대부분의 ABI에 APK를 생성하려면 exclude를 사용하세요.
reset()
ABI의 기본 목록을 삭제합니다. include 요소와 결합하여 추가하려는 ABI를 지정할 때만 사용합니다. 다음 스니펫에서는 reset()을 호출하여 목록을 삭제한 다음 include를 사용하여 ABI 목록을 x86x86_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
true이면 Gradle은 ABI별 APK 외에 범용 APK를 생성합니다. 범용 APK에는 단일 APK의 모든 ABI 코드와 리소스가 포함되어 있습니다. 기본값은 false입니다. 이 옵션은 splits.abi 블록에서만 사용 가능합니다. 화면 밀도에 기반한 다중 APK를 빌드할 때 Gradle은 항상 모든 화면 밀도의 코드와 리소스를 포함하는 범용 APK를 생성합니다.

다음 예에서는 각 ABI를 위한 별도의 APK인 x86x86_64를 생성합니다. 이 작업은 reset()을 사용하여 빈 ABI 목록으로 시작한 후 각각 APK를 가져올 ABI 목록과 함께 include를 사용하여 완료됩니다.

    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 we only
          // want APKs for x86 and x86_64.

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

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

          // Specifies that we do not want to also generate a universal APK that includes all ABIs.
          universalApk false
        }
      }
    }
    

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

mips, mips64 및 armeabi

Gradle용 Android 플러그인 3.1.0 이상에서는 기본적으로 mips, mips64, armeabi 등의 ABI에 더 이상 APK를 생성하지 않습니다. 그 이유는 NDK r17 이상에서는 더 이상 이 ABI를 지원되는 타겟으로 포함하지 않기 때문입니다.

먼저 Google Play Console을 검사하여 이러한 ABI를 타겟팅하는 앱의 APK를 다운로드하는 사용자가 있는지 확인합니다. 없다면 빌드에서 이러한 ABI를 생략하는 것이 좋습니다. 이러한 ABI를 타겟팅하는 APK를 계속 빌드하려면 NDK r16b 이하를 사용하고 활성 빌드 변형 및 ABI를 설정한 후 다음과 같이 build.gradle 파일에서 ABI를 지정해야 합니다.

    splits {
        abi {
            include 'armeabi', 'mips', 'mips64'
            ...
        }
    }
    

알려진 문제: Gradle용 Android 플러그인 3.0.1 이하를 NDK r17 이상과 함께 사용하고 있다면 Error:ABIs [mips64, armeabi, mips] are not supported for platform. 오류가 발생할 수 있습니다. ABI별 APK를 빌드할 때 지원되지 않는 ABI가 기본적으로 이전 버전의 플러그인에 여전히 포함되기 때문입니다. 이 문제를 해결하려면 플러그인을 최신 버전으로 업데이트하거나 앱의 build.gradle 파일에서 플러그인의 기본 ABI 목록을 재설정하고 다음과 같이 지원되는 ABI만 포함합니다.

    ...
    splits {
        abi {
            ...
            reset()
            include "x86", "armeabi-v7a", "arm64-v8a", "x86_64"
        }
    }
    

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

Build Variants 패널에는 ModuleActive Build Variant라는 두 개의 열이 있습니다. 모듈의 Active Build Variant 값에 따라 배포되어 편집기에 표시되는 빌드 변형이 결정됩니다.

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

변형 간에 전환하려면 모듈의 Active Build Variant 셀을 클릭하고 목록 필드에서 원하는 변형을 선택합니다.

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

Build Variants 패널에는 Module, Active Build VariantActive ABI라는 세 개의 열이 있습니다. 모듈의 Active Build Variant 값에 따라 배포되어 편집기에 표시되는 빌드 변형이 결정됩니다. 네이티브 모듈의 경우 Active ABI 값에 따라 편집기에서 사용할 ABI가 결정되지만 이 값은 배포되는 빌드 변형에 영향을 주지는 않습니다.

그림 2: Build Variants 패널에는 네이티브/C++ 코드가 있는 프로젝트의 경우 Active ABI 열이 추가됩니다.

빌드 유형 또는 ABI를 변경하려면 Active Build Variant 또는 Active ABI 열의 셀을 클릭하고 목록 필드에서 원하는 변형 또는 ABI를 선택합니다. 새 동기화가 자동으로 실행됩니다. 앱 또는 라이브러리 모듈의 열을 변경하면 모든 종속 행에 변경사항이 적용됩니다.

버전 관리 구성

기본적으로 Gradle에서 다중 APK를 생성하는 경우 각 APK에는 모듈 수준 build.gradle 파일에 지정된 것과 동일한 버전 정보가 있습니다. Google Play 스토어에서는 모두 동일한 버전 정보가 있는 동일한 앱에 다중 APK를 허용하지 않으므로 Play 스토어에 업로드하기 전에 각 APK에 고유한 자체 versionCode가 있는지 확인해야 합니다.

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

다음 예에서 x86 ABI의 APK는 2004라는 versionCode를 가져오고 x86_64 ABI는 3004를 가져옵니다. 버전 코드를 1000과 같이 크게 늘어나도록 할당하면 나중에 앱을 업데이트해야 할 때 고유한 버전 코드를 할당할 수 있습니다. 예를 들어 defaultConfig.versionCode가 후속 업데이트에서 5로 반복되는 경우 Gradle은 x86 APK에 2005 versionCode를, x86_64 APK에 3005를 할당합니다.

도움말: 빌드에 범용 APK가 포함된 경우 다른 APK보다 낮은 versionCode를 할당해야 합니다. Google Play 스토어에서는 타겟 기기와 호환되며 가장 높은 versionCode가 있는 앱 버전을 설치하므로 낮은 versionCode를 범용 APK에 할당하면 Google Play 스토어에서는 범용 APK로 대체하기 전에 APK 중 하나를 설치하려고 시도하게 됩니다. 아래 샘플 코드는 범용 APK의 기본 versionCode를 재정의하지 않고 이 작업을 처리합니다.

    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 like this:
    // 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 does not override the version code for universal APKs.
        // However, because we 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 simply
          // causes Gradle to use the value of variant.versionCode for the APK.
          output.versionCodeOverride =
                  baseAbiVersionCode * 1000 + variant.versionCode
        }
      }
    }
    

대체 버전 코드 체계에 관한 더 많은 예를 보려면 버전 코드 할당을 참조하세요.

다중 APK 빌드

모듈 수준 build.gradle 파일을 구성하여 다중 APK를 빌드한 후에는 Build > Build APK를 클릭하여 Project 창에서 현재 선택된 모듈의 모든 APK를 빌드합니다. Gradle은 각 밀도 또는 ABI의 APK를 프로젝트의 build/outputs/apk/ 디렉터리에 만듭니다.

Gradle은 다중 APK를 구성하는 각 밀도 또는 ABI를 위한 APK를 빌드합니다. 밀도와 ABI 모두를 위해 다중 APK를 사용 설정하면 Gradle은 각 밀도와 ABI 조합을 위한 APK를 만듭니다. 예를 들어 다음 build.gradle 스니펫은 mdpi 및 hdpi 밀도, x86 및 x86_64 ABI의 다중 APK 빌드를 사용 설정합니다.

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

구성 예의 출력에는 다음 네 개의 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를 생성합니다. ABI에 기반한 다중 APK를 빌드할 때 Gradle은 build.gradle 파일의 splits.abi 블록에서 universalApk true를 지정하는 경우 모든 ABI의 코드와 리소스가 포함된 APK만 생성합니다.

APK 파일 이름 형식

다중 APK를 빌드할 때 Gradle은 아래와 같은 체계의 APK 파일 이름을 사용합니다.

modulename-screendensityABI-buildvariant.apk

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

modulename
빌드 중인 모듈 이름을 지정합니다.
screendensity
화면 밀도의 다중 APK가 사용 설정된 경우 'mdpi'와 같이 APK의 화면 밀도를 지정합니다.
ABI
ABI의 다중 APK가 사용 설정된 경우 'x86'과 같이 APK의 ABI를 지정합니다. 화면 밀도와 ABI 모두를 위한 다중 APK가 사용 설정된 경우 Gradle은 'mdpiX86'과 같이 밀도 이름을 ABI 이름과 연결합니다. ABI별 APK에 universalApk가 사용 설정된 경우 Gradle은 범용 APK 파일 이름의 ABI 부분으로 'universal'을 사용합니다.
buildvariant
빌드 중인 빌드 변형을 지정합니다(예: 'debug').

예를 들어 'myApp'의 디버그 버전에 mdpi 화면 밀도 APK를 빌드할 때 APK 파일 이름은 myApp-mdpi-debug.apk입니다. mdpi 화면 밀도와 x86 ABI 모두를 위한 다중 APK를 빌드하도록 구성된 'myApp' 출시 버전의 APK 파일 이름은 myApp-mdpiX86-release.apk입니다.