Gradle 도움말 및 레시피

Gradle과 Gradle용 Android 플러그인은 Android 앱이나 라이브러리를 컴파일, 빌드 및 패키징하기 위한 유연한 방법을 제공합니다. 이 페이지에는 각 빌드를 최대한 활용하는 데 유용한 팁과 구성이 정리되어 있습니다. 더 빠르게 빌드하는 방법을 알아보려면 빌드 속도 최적화를 참조하세요.

Gradle을 처음 사용하는 분은 빌드 구성을 읽어 기본 사항을 습득하세요. 또한, Android 플러그인의 DSL 참조 문서에서 이 페이지에 사용된 속성에 관해 자세히 알아볼 수 있습니다.

프로젝트 및 소스 관리

다음은 프로젝트의 모듈과 소스를 관리하기 위한 몇 가지 구성입니다. 프로젝트와 모듈을 만들고 관리하는 방법에 관해 자세히 알아보려면 프로젝트 개요를 참조하세요.

기본 소스 세트 구성 변경

Gradle에서 소스 세트의 각 구성요소에 관한 파일을 수집하는 위치를 변경하려면 모듈 수준 build.gradle 파일의 sourceSets 블록을 사용하면 됩니다.

Groovy

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.srcDirs = ['other/res1', 'other/res2']

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Kotlin

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    getByName("main") {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.setSrcDirs("other/java")

      // When you list multiple directories, Gradle uses all of them to collect
      // sources. You should avoid specifying a directory which is a parent to one
      // or more other directories you specify.
      res.setSrcDirs("other/res1", "other/res2")

      // For each source set, you can specify only one Android manifest.
      // The following points Gradle to a different manifest for this source set.
      manifest.srcFile("other/AndroidManifest.xml")
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying
      // the configuration below for the androidTest source set, Gradle looks for
      // Java sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
    }
  }
}
...

라이브러리 및 종속 항목 관리

Gradle에서는 원격 라이브러리든 로컬 라이브러리 모듈이든 상관없이 종속 항목 관리를 위한 강력한 메커니즘을 사용할 수 있습니다.

종속 항목 구성을 통해 특정 빌드 타겟팅

특정 빌드 변형 소스 세트나 테스트 소스 세트에 관해서만 종속 항목을 원하는 경우, 종속 항목 구성 이름을 대문자로 지정하고 이름 앞에 빌드 변형이나 테스트 소스 세트의 이름을 접두사로 붙입니다.

Groovy

android {...}

// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
  // For variants that combine a product flavor and build type, you need to
  // intitialize a placeholder for its dependency configuration.
  freeDebugRuntimeOnly{}
  ...
}

dependencies {
    // Adds an implementation dependency only to the "free" product flavor.
    freeImplementation 'com.google.firebase:firebase-ads:21.5.1'
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

Kotlin

android {...}

dependencies {
    // Use ""() notation for custom flavors and build types
    // Adds an implementation dependency only to the "free" product flavor.
    "freeImplementation"("com.google.firebase:firebase-ads:21.5.1")
    // Adds a runtimeOnly dependency only to the "freeDebug" build variant.
    "freeDebugRuntimeOnly"(fileTree("dir" to "libs", "include" to "*.jar"))
    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")
    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

다른 버전의 앱 생성

Gradle과 Android 플러그인을 사용하면 빌드 변형을 구성하여 단일 모듈로부터 다양한 버전의 앱을 생성할 수 있습니다.

동적 버전 코드 구성

Gradle이 프로젝트의 APK를 생성할 때 기본적으로 각 APK는 모듈 수준 build.gradle 파일에 지정된 것과 동일한 버전 정보를 가집니다. Google Play 스토어에서는 버전 정보가 모두 동일한 여러 APK를 동일한 앱에 사용할 수 없으므로 Play 스토어에 업로드하기 전에 각 APK에 고유한 versionCode가 있는지 확인해야 합니다.

그렇게 하려면 빌드 시 각 APK에 다른 버전 코드를 할당하는 맞춤 빌드 로직을 사용하면 됩니다. 예를 들어, 각 ABI에 관해 별도 APK를 만들 경우 자동 APK 버전 관리는 다음과 같습니다.

Groovy

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

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

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

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

Kotlin

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

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

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

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

여러 제품 버전의 조합

여러 제품 버전의 구성을 조합하려는 경우도 있을 것입니다. 이 작업을 위해 Gradle용 Android 플러그인을 사용하면 버전 차원이라는 제품 버전 그룹을 생성할 수 있습니다.

다음 코드 샘플에서는 flavorDimensions 속성을 사용하여 'full' 및 'demo' 제품 버전을 그룹화하는 'mode' 버전 차원과 API 수준을 기반으로 제품 버전 구성을 그룹화하는 'api' 버전 차원을 생성합니다. 그런 다음, Gradle이 'mode' 차원의 제품 버전을 'api' 차원의 제품 버전과 조합합니다.

Groovy

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion '24'
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion '23'
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion '21'
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Kotlin

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdkVersion(24)
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode = 30000 + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdkVersion(23)
      versionCode = 20000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdkVersion(21)
      versionCode = 10000  + android.defaultConfig.versionCode
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

변형 필터링

모듈의 build.gradle 파일에 variantFilter 블록을 사용하면 원하지 않는 빌드 변형을 필터링할 수 있습니다. 다음 샘플 코드는 'minApi21'과 'demo' 제품 버전을 조합하는 어떠한 변형도 빌드하지 않도록 Gradle에 알려줍니다.

Groovy

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

Kotlin

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enabled = false
        }
    }
}
...

앱 테스트

로컬 및 통합 단위 테스트 실행에 관해 자세히 알아보려면 앱 테스트를 참고하세요.

린트 옵션 구성

모듈 수준 build.gradle 파일에 lintOptions 블록을 사용하여 린트 옵션을 구성할 수 있습니다. Android 프로젝트에 린트를 사용하는 방법에 관해 자세히 알아보려면 린트로 코드 개선을 참고하세요.

Groovy

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable 'TypographyFractions','TypographyQuotes'
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly 'NewApi', 'InlinedApi'
        // If set to true, turns off analysis progress reporting by lint.
        quiet true
        // if set to true (default), stops the build if errors are found.
        abortOnError false
        // if true, only report errors.
        ignoreWarnings true
    }
}
...

Kotlin

android {
    ...
    lintOptions {
        // Turns off checks for the issue IDs you specify.
        disable("TypographyFractions")
        disable("TypographyQuotes")
        // Turns on checks for the issue IDs you specify. These checks are in
        // addition to the default lint checks.
        enable("RtlHardcoded")
        enable("RtlCompat")
        enable("RtlEnabled")
        // To enable checks for only a subset of issue IDs and ignore all others,
        // list the issue IDs with the 'check' property instead. This property overrides
        // any issue IDs you enable or disable using the properties above.
        checkOnly("NewApi", "InlinedApi")
        // If set to true, turns off analysis progress reporting by lint.
        quiet = true
        // if set to true (default), stops the build if errors are found.
        abortOnError = false
        // if true, only report errors.
        ignoreWarnings = true
    }
}
...

계측 매니페스트 설정 구성

Gradle은 테스트 APK를 빌드할 때 AndroidManifest.xml 파일을 자동으로 생성하고 <instrumentation> 노드로 이 파일을 구성합니다. 이 노드의 일부 설정을 변경하려면 또 다른 매니페스트 파일을 테스트 소스 세트에 만들거나 모듈 수준 build.gradle 파일을 구성하면 됩니다(다음 코드 샘플 참고).

Groovy

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest true
  }
}
...

Kotlin

android {
  ...
  // Each product flavor you configure can override properties in the
  // defaultConfig block. To learn more, go to Configure Product Flavors.
  defaultConfig {
    ...
    // Specifies the application ID for the test APK.
    testApplicationId = "com.test.foo"
    // Specifies the fully-qualified class name of the test instrumentation runner.
    testInstrumentationRunner = "android.test.InstrumentationTestRunner"
    // If set to 'true', enables the instrumentation class to start and stop profiling.
    // If set to false (default), profiling occurs the entire time the instrumentation
    // class is running.
    testHandleProfiling = true
    // If set to 'true', indicates that the Android system should run the instrumentation
    // class as a functional test. The default value is 'false'
    testFunctionalTest = true
  }
}
...

테스트 빌드 유형 변경

기본적으로 모든 테스트는 특정 디버그 빌드 유형에 관해 실행됩니다. 모듈 수준 build.gradle 파일에서 testBuildType 속성을 사용하여 이 빌드 유형을 또 다른 빌드 유형으로 변경할 수 있습니다. 예를 들어, 'staging' 빌드 유형에 관해 테스트를 실행하려면 다음 스니펫에 표시된 대로 파일을 수정합니다.

Groovy

android {
    ...
    testBuildType "staging"
}

Kotlin

android {
    ...
    testBuildType "staging"
}

Gradle 테스트 옵션 구성

Gradle에서 모든 테스트를 실행하는 방법을 변경하는 옵션을 지정하려면 testOptions 블록을 모듈 수준 build.gradle에 구성합니다.

Groovy

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

Kotlin

android {
  ...
  // Encapsulates options for running tests.
  testOptions {
    // Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
    // in the path_to_your_project/module_name/build/outputs/reports/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    reportDir "$rootDir/test-reports"
    // Changes the directory where Gradle saves test results. By default, Gradle saves test results
    // in the path_to_your_project/module_name/build/outputs/test-results/ directory.
    // '$rootDir' sets the path relative to the root directory of the current project.
    resultsDir "$rootDir/test-results"
  }
}

로컬 단위 테스트에만 옵션을 지정하려면 testOptions.unitTests 블록을 구성합니다.

Groovy

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

Kotlin

android {
  ...
  testOptions {
    ...
    // Encapsulates options for local unit tests.
    unitTests {
      // By default, local unit tests throw an exception any time the code you are testing tries to access
      // Android platform APIs (unless you mock Android dependencies yourself or with a testing
      // framework like Mockito). However, you can enable the following property so that the test
      // returns either null or zero when accessing platform APIs, rather than throwing an exception.
      returnDefaultValues true

      // Encapsulates options for controlling how Gradle executes local unit tests. For a list
      // of all the options you can specify, read Gradle's reference documentation.
      all {
        // Sets JVM argument(s) for the test JVM(s).
        jvmArgs '-XX:MaxPermSize=256m'

        // You can also check the task name to apply options to only the tests you specify.
        if (it.name == 'testDebugUnitTest') {
          systemProperty 'debug', 'true'
        }
      }
    }
  }
}

빌드 최적화

이 섹션에서는 전체 빌드 및 증분 빌드의 속도를 높여주는 일부 구성을 제공합니다. 자세한 내용은 빌드 속도 최적화를 참조하세요.

코드 축소

Android 스튜디오는 ProGuard 규칙 파일을 사용하는 R8을 사용하여 코드를 축소합니다. 새 프로젝트의 경우 Android 스튜디오는 Android SDK의 tools/proguard/folder에 있는 기본 설정 파일(proguard-android.txt)을 사용합니다. 더 많은 코드 축소를 원하면 같은 위치에 있는 proguard-android-optimize.txt 파일을 사용해 보세요.

Groovy

android {
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                                           'proguard-rules.pro'
    }
  }
  ...
}
...

Kotlin

android {
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                                           'proguard-rules.pro'
    }
  }
  ...
}
...

각 빌드 변형에 특정한 규칙을 추가하려면 버전별로 추가적인 proguardFiles 속성을 구성합니다. 예를 들어, 다음 샘플에서는 flavor2-rules.pro를 'flavor2'에 추가합니다. 이제 출시 버전 'flavor2'는 'release' 블록의 규칙도 함께 적용되므로 세 개의 규칙 파일을 모두 사용합니다.

Groovy

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

Kotlin

android {
  ...
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android.txt'),
             'proguard-rules.pro'
    }
  }
  productFlavors {
    flavor1 {
      ...
    }
    flavor2 {
      proguardFile 'flavor2-rules.pro'
    }
  }
}
...

앱 게시

앱을 Google Play에 게시하는 방법에 관해 자세히 알아보려면 앱 게시를 참조하세요.

앱 서명

Android 스튜디오는 UI에서 출시 빌드용 서명을 구성하는 간단한 방법을 제공하지만, 개발자는 모듈의 build.gradle 파일에서 signingConfigs 블록을 수동으로 구성할 수 있습니다.

Groovy

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

Kotlin

android {
  ...
  defaultConfig { ... }

  // Encapsulates signing configurations.
  signingConfigs {
    // Creates a signing configuration called "release".
    release {
      // Specifies the path to your keystore file.
      storeFile file("my-release-key.jks")
      // Specifies the password for your keystore.
      storePassword "password"
      // Specifies the identifying name for your key.
      keyAlias "my-alias"
      // Specifies the password for your key.
      keyPassword "password"
    }
  }
  buildTypes {
    release {
      // Adds the "release" signing configuration to the release build type.
      signingConfig signingConfigs.release
      ...
    }
  }
}
...

프로젝트에서 개인 서명 정보 삭제

기본적으로, 서명 구성은 일반 텍스트로 모듈의 build.gradle 파일에 기록됩니다. 개발자가 팀이나 오픈소스 프로젝트에서 작업하는 경우, 다음과 같이 하여 빌드 파일에서 민감한 정보를 삭제할 수 있습니다.

  1. 프로젝트의 루트 디렉터리에 이름이 keystore.properties인 파일을 생성하고 다음 정보를 포함합니다.
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. build.gradle 파일에서 다음과 같이 keystore.properties 파일을 로드합니다(android 블록 앞에 있어야 함).

    Groovy

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...

    Kotlin

    // Creates a variable called keystorePropertiesFile, and initializes it to the
    // keystore.properties file.
    def keystorePropertiesFile = rootProject.file("keystore.properties")
    
    // Initializes a new Properties() object called keystoreProperties.
    def keystoreProperties = new Properties()
    
    // Loads the keystore.properties file into the keystoreProperties object.
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
      ...
    }
    ...
  3. keystoreProperties 객체에 저장된 서명 정보를 입력합니다.

    Groovy

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...

    Kotlin

    android {
      signingConfigs {
        config {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile file(keystoreProperties['storeFile'])
          storePassword keystoreProperties['storePassword']
        }
      }
      ...
    }
    ...
  4. 알림바에서 Sync Now를 클릭합니다.

앱 서명에 관해 자세히 알아보려면 앱 서명을 참조하세요.

앱 개발 단순화

다음 팁은 Android 앱 개발을 더 쉽게 만들어줍니다.

맞춤형 필드 및 리소스 값을 앱 코드와 공유

Gradle은 빌드 시 BuildConfig 클래스를 생성하므로 앱 코드가 현재 빌드에 관한 정보를 검사할 수 있습니다. 또한, buildConfigField() 메서드를 사용하여 Gradle 빌드 구성 파일로부터 BuildConfig 클래스에 맞춤 필드를 추가할 수 있으며 앱의 런타임 코드에 있는 값에 액세스할 수 있습니다. 마찬가지로, resValue()로 앱 리소스 값을 추가할 수 있습니다.

Groovy

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

Kotlin

android {
  ...
  buildTypes {
    release {
      // These values are defined only for the release build, which
      // is typically used for full builds and continuous builds.
      buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
      resValue("string", "build_time", "${minutesSinceEpoch}")
      ...
    }
    debug {
      // Use static values for incremental builds to ensure that
      // resource files and BuildConfig aren't rebuilt with each run.
      // If these rebuild dynamically, they can interfere with
      // Apply Changes as well as Gradle UP-TO-DATE checks.
      buildConfigField("String", "BUILD_TIME", "\"0\"")
      resValue("string", "build_time", "0")
    }
  }
}
...

앱 코드에서 다음과 같이 속성에 액세스할 수 있습니다.

Kotlin

...
Log.i(TAG, BuildConfig.BUILD_TIME)
Log.i(TAG, getString(R.string.build_time))

자바

...
Log.i(TAG, BuildConfig.BUILD_TIME);
Log.i(TAG, getString(R.string.build_time));

속성을 매니페스트와 공유

경우에 따라 매니페스트와 코드에서 모두 동일한 속성을 선언해야 합니다(예: FileProvider의 권한을 선언하는 경우). 변경 사항을 적용하기 위해 동일한 속성을 여러 위치에서 업데이트하는 대신, 단일 속성을 모듈의 build.gradle 파일에서 정의하여 이 속성을 매니페스트와 코드에서 모두 사용할 수 있게 합니다(다음 샘플 참조). 자세히 알아보려면 매니페스트에 빌드 변수 삽입을 참고하세요.

Groovy

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    def filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders =
      [filesAuthority: filesAuthorityValue]
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

Kotlin

android {
  // For settings specific to a product flavor, configure these properties
  // for each flavor in the productFlavors block.
  defaultConfig {
    // Creates a property for the FileProvider authority.
    val filesAuthorityValue = applicationId + ".files"
    // Creates a placeholder property to use in the manifest.
    manifestPlaceholders["filesAuthority"] = filesAuthorityValue
      // Adds a new field for the authority to the BuildConfig class.
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  ...
}
...

매니페스트에서 다음과 같이 자리표시자에 액세스합니다.

<manifest>
  ...
  <application>
    ...
    <provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="${filesAuthority}"
      android:exported="false"
      android:grantUriPermissions="true">
      ...
    </provider>
  </application>
</manifest>

앱 코드의 FILES_AUTHORITY 필드에 액세스하려면 다음과 같이 합니다.

Kotlin

...
val contentUri: Uri = FileProvider.getUriForFile(context, BuildConfig.FILES_AUTHORITY, myFile)

Java

...
Uri contentUri = FileProvider.getUriForFile(getContext(),
  BuildConfig.FILES_AUTHORITY,
  myFile);