Tips dan urutan langkah Gradle

Gradle dan plugin Android untuk Gradle menyediakan cara yang fleksibel untuk mengompilasi, mem-build, dan mengemas aplikasi atau library Android Anda. Halaman ini mengumpulkan beberapa tips dan konfigurasi penting untuk membantu Anda memaksimalkan hasil dari masing-masing build. Jika Anda ingin mempelajari cara mempercepat build, baca Mengoptimalkan Kecepatan Build Anda.

Jika Anda baru mengenal Gradle, pelajari dasar-dasarnya dengan membaca Mengonfigurasi Build Anda. Anda juga dapat memeriksa Dokumentasi referensi DSL plugin Android untuk mempelajari properti yang digunakan di halaman ini lebih lanjut.

Mengelola project dan sumber

Berikut adalah beberapa konfigurasi untuk mengelola modul project beserta sumbernya. Untuk mempelajari cara membuat serta mengelola project dan modul lebih lanjut, baca Ringkasan Project.

Mengubah konfigurasi set sumber default

Anda dapat menggunakan blok sourceSets di file build.gradle level modul guna mengubah lokasi pencarian Gradle untuk mengumpulkan file di setiap komponen dari suatu set sumber.

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

Mengonfigurasi properti lingkup project

Untuk project yang mencakup beberapa modul, sebaiknya tentukan properti di level project dan bagikan ke seluruh modul. Anda dapat melakukannya dengan menambahkan properti tambahan ke blok ext dalam file build.gradle level teratas.

Groovy

buildscript {...}
allprojects {...}

// This block encapsulates custom properties and makes them available to all
// modules in the project.
ext {
    // The following are only a few examples of the types of properties you can define.
    compileSdkVersion = 28
    // You can also use this to specify versions for dependencies. Having consistent
    // versions between modules can avoid behavior conflicts.
    supportLibVersion = "28.0.0"
    ...
}
...

Kotlin

buildscript {...}
allprojects {...}

// The following are only a few examples of the types of properties you can define.
extra["sdkVersion"] = 28
extra["supportLibVersion"] = "28.0.0"

...

Untuk mengakses properti-properti ini dari modul dalam project yang sama, gunakan sintaksis berikut pada file build.gradle level modul.

Groovy

android {
  // Use the following syntax to access properties you define at the project level:
  // rootProject.ext.property_name
  compileSdkVersion rootProject.ext.compileSdkVersion
  ...
}
...
dependencies {
    implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    ...
}

Kotlin

android {
  // Use the following syntax to access properties you define at the project level:
  // rootProject.ext.property_name
  val sdkVersion: Int by rootProject.extra
  compileSdkVersion(sdkVersion)
  ...
}
...
dependencies {
    implementation("com.android.support:appcompat-v7:${rootProject.extra["supportLibVersion"]}")
    ...
}

Mengelola library dan dependensi

Gradle menyediakan mekanisme yang kuat untuk mengelola dependensi, baik library jarak jauh maupun modul library lokal.

Menargetkan build tertentu dengan konfigurasi dependensi

Jika Anda menginginkan dependensi hanya untuk set sumber varian build tertentu atau set sumber pengujian, gunakan huruf kapital untuk nama konfigurasi dependensi dan berikan awalan dengan nama varian build atau set sumber pengujian.

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:9.8.0'
    // 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.0.2'
}

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:9.8.0")
    // 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.0.2")
}

Membuat beberapa versi aplikasi

Gradle dan plugin Android memungkinkan Anda membuat beberapa versi aplikasi dari satu modul dengan mengonfigurasi varian build.

Mengonfigurasi dukungan multi-APK

Dengan plugin Android, Anda dapat mem-build multi-APK yang masing-masing menargetkan ABI atau kepadatan layar, dan memanfaatkan dukungan multi-APK Google Play.

Mengonfigurasi APK terpisah untuk setiap kepadatan layar

Jika ingin membuat APK terpisah untuk berbagai kepadatan layar, tambahkan blok android.splits.density ke dalam file build.gradle modul Anda.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on screen density.
    density {

      // Enables building multiple APKs.
      enable true

      // Specifies a list of screen densities Gradle should not create APKs for.
      exclude "ldpi", "mdpi"

      // Alternatively, you can use the following to clear the default list of
      // screen densities and specify only the screen densities you want to build
      // APKs for:
      // reset()
      // include "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings. This property
      // configures the <compatible-screens> element in the manifest. You
      // typically don't need to configure this manifest property, but it's
      // important when building multiple APKs based on screen density.
      compatibleScreens 'normal', 'large', 'xlarge'
    }
  }
}

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on screen density.
    density {

      // Enables building multiple APKs.
      isEnable = true

      // Specifies a list of screen densities Gradle should not create APKs for.
      exclude("ldpi", "mdpi")

      // Alternatively, you can use the following to clear the default list of
      // screen densities and specify only the screen densities you want to build
      // APKs for:
      // reset()
      // include("hdpi", "xhdpi", "xxhdpi", "xxxhdpi")

      // Specifies a list of compatible screen size settings. This property
      // configures the <compatible-screens> element in the manifest. You
      // typically don't need to configure this manifest property, but it's
      // important when building multiple APKs based on screen density.
      compatibleScreens("normal", "large", "xlarge")
    }
  }
}

Mengonfigurasi APK terpisah untuk setiap ABI

Jika Anda perlu membuat APK terpisah untuk setiap ABI, tambahkan blok android.splits.abi ke file build.gradle modul Anda.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that we only
      // want APKs for x86, armeabi-v7a, and mips.
      reset()

      // Specifies a list of ABIs that Gradle should create APKs for.
      include "x86", "armeabi-v7a", "mips"

      // Specify that we want to also generate a universal APK that includes all ABIs.
      universalApk true
    }
  }
}

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs.
      isEnable = true

      // By default all ABIs are included, so use reset() and include to specify that we only
      // want APKs for x86, armeabi-v7a, and mips.
      reset()

      // Specifies a list of ABIs that Gradle should create APKs for.
      include("x86", "armeabi-v7a", "mips")

      // Specify that we want to also generate a universal APK that includes all ABIs.
      isUniversalApk = true
    }
  }
}

Mengonfigurasi kode versi dinamis

Secara default, saat Gradle menghasilkan APK untuk project Anda, setiap APK memiliki informasi versi yang sama, seperti yang ditetapkan dalam file build.gradle level modul. Karena Google Play Store tidak mengizinkan multi-APK untuk satu aplikasi jika semuanya memiliki informasi versi yang sama, Anda harus memastikan setiap APK memiliki versionCode yang unik sebelum menguploadnya ke Play Store.

Anda dapat melakukannya dengan logika build kustom yang menetapkan kode versi berbeda ke setiap APK pada waktu build. Misalnya, saat membuat APK terpisah untuk setiap ABI, pembuatan versi APK otomatis akan terlihat seperti ini:

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

Mengombinasikan beberapa ragam produk

Dalam beberapa situasi, Anda mungkin ingin mengombinasikan konfigurasi dari beberapa ragam produk.. Untuk melakukannya, plugin Android untuk Gradle memungkinkan Anda membuat grup ragam produk, yang disebut dimensi ragam.

Contoh kode berikut menggunakan properti flavorDimensions untuk membuat dimensi ragam "mode" untuk mengelompokkan ragam produk "full" dan "demo", serta dimensi ragam "api" untuk mengelompokkan konfigurasi ragam produk berdasarkan API level. Gradle kemudian mengombinasikan ragam produk dari dimensi "mode" dengan yang ada dalam dimensi "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"
      ...
    }
  }
}
...

Memfilter varian

Anda dapat memfilter varian build yang tidak diinginkan menggunakan blok variantFilter dalam file build.gradle modul. Kode contoh berikut memberi tahu Gradle untuk tidak mem-build varian apa pun yang mengombinasikan ragam produk "minApi21" dan "demo":

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

Menguji aplikasi

Untuk mempelajari cara menjalankan pengujian unit lokal dan terpadu lebih lanjut, baca Menguji Aplikasi Anda.

Mengonfigurasi opsi lint

Anda dapat mengonfigurasi opsi lint tertentu menggunakan blok lintOptions di file build.gradle level modul Anda. Untuk mempelajari lebih lanjut penggunaan lint untuk project Android, baca Meningkatkan Kode Anda dengan Lint.

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

Mengonfigurasi setelan manifes instrumentasi

Saat mem-build APK pengujian Anda, Gradle otomatis membuat file AndroidManifest.xml dan mengonfigurasinya dengan node <instrumentation>. Anda dapat mengubah beberapa setelan untuk node ini dengan membuat file manifes lain dalam set sumber pengujian atau mengonfigurasi file build.gradle level modul, seperti yang ditunjukkan pada contoh kode berikut.

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

Mengubah jenis build pengujian

Secara default, semua pengujian dijalankan terhadap jenis build debug. Anda dapat mengubahnya ke jenis build lain menggunakan properti testBuildType dalam file build.gradle level modul. Misalnya, jika Anda ingin menjalankan pengujian terhadap jenis build "staging", edit file tersebut seperti yang ditunjukkan dalam cuplikan berikut ini.

Groovy

android {
    ...
    testBuildType "staging"
}

Kotlin

android {
    ...
    testBuildType "staging"
}

Mengonfigurasi opsi pengujian Gradle

Untuk menentukan opsi yang mengubah cara Gradle menjalankan semua pengujian Anda, lakukan konfigurasi blok testOptions di build.gradle level modul.

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

Untuk menentukan opsi pada pengujian unit lokal saja, lakukan konfigurasi blok 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'
        }
      }
    }
  }
}

Mengoptimalkan build

Bagian ini menyediakan beberapa konfigurasi untuk membantu mempercepat build "full" dan "incremental" Anda. Untuk mempelajari lebih lanjut, baca Mengoptimalkan Kecepatan Build Anda.

Menyingkat kode

Android Studio menggunakan R8, yang menggunakan file aturan ProGuard, untuk menyingkat kode Anda. Untuk project baru, Android Studio menggunakan file setelan default (proguard-android.txt) dari tools/proguard/folder Android SDK. Untuk penyingkatan kode yang lebih maksimal, cobalah file proguard-android-optimize.txt yang ada di lokasi yang sama.

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

Untuk menambahkan aturan yang berlaku khusus pada setiap varian build, lakukan konfigurasi properti proguardFiles tambahan untuk setiap ragam. Misalnya, contoh berikut menambahkan flavor2-rules.pro ke "flavor2". Sekarang, versi rilis “flavor2” menggunakan ketiga file aturan tersebut karena aturan yang berasal dari blok rilis juga diterapkan.

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

Memublikasikan aplikasi

Untuk mempelajari cara memublikasikan aplikasi ke Google Play lebih lanjut, baca Memublikasikan Aplikasi Anda.

Menandatangani aplikasi

Meskipun Android Studio menyediakan cara mudah untuk mengonfigurasi penandatanganan untuk build rilis dari UI, Anda dapat mengonfigurasi blok signingConfigs secara manual di file build.gradle modul:

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

Menghapus informasi penandatanganan pribadi dari project Anda

Secara default, konfigurasi penandatanganan dicatat dalam teks biasa ke file build.gradle modul. Jika Anda bekerja dengan tim atau project open source, Anda dapat memindahkan informasi sensitif ini keluar dari file build dengan melakukan langkah berikut.

  1. Buat file bernama keystore.properties dalam direktori utama project Anda dan sertakan informasi berikut:
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. Pada file build.gradle Anda, muat file keystore.properties sebagai berikut (ini harus dilakukan sebelum blok 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. Masukkan informasi penandatanganan yang disimpan di objek 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. Klik Sync Now di baris notifikasi.

Untuk mempelajari penandatanganan aplikasi lebih lanjut, baca Menandatangani Aplikasi Anda.

Menyederhanakan pengembangan aplikasi

Tips berikut ini membantu memudahkan proses pengembangan aplikasi Android Anda.

Menggunakan nilai resource dan kolom kustom yang sama dengan kode aplikasi Anda

Pada waktu build, Gradle membuat class BuildConfig sehingga kode aplikasi Anda dapat memeriksa informasi tentang build saat ini. Anda juga dapat menambahkan kolom kustom ke class BuildConfig dari file konfigurasi build Gradle dengan menggunakan metode buildConfigField() dan mengakses nilai-nilai tersebut dalam kode waktu proses aplikasi. Demikian juga, Anda dapat menambahkan nilai resource aplikasi dengan 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")
    }
  }
}
...

Dalam kode aplikasi, Anda bisa mengakses properti dengan cara berikut:

Kotlin

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

Java

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

Menggunakan properti yang sama dengan manifes

Dalam beberapa situasi, Anda mungkin perlu mendeklarasikan properti yang sama pada manifes dan kode Anda (misalnya, saat mendeklarasikan otoritas untuk sebuah FileProvider). Daripada mengupdate properti yang sama di beberapa lokasi untuk merefleksikan perubahan, cukup tetapkan satu properti dalam file build.gradle modul Anda agar tersedia untuk manifes dan kode Anda, seperti yang ditunjukkan dalam contoh berikut. Untuk mempelajari lebih lanjut, baca Memasukkan Variabel Build ke dalam Manifes.

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

Di manifes, akses placeholder dengan cara berikut:

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

Mengakses kolom FILES_AUTHORITY dalam kode aplikasi Anda akan terlihat seperti ini:

Kotlin

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

Java

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