Mẹo và công thức cho Gradle

Gradle và trình bổ trợ Android cho Gradle cung cấp một cách linh hoạt để biên dịch, xây dựng và gói ứng dụng hoặc thư viện Android của bạn. Trang này thu thập một số mẹo và cấu hình hữu ích để giúp bạn tận dụng tối đa mỗi bản dựng. Nếu bạn muốn tìm hiểu cách tăng tốc độ các bản dựng, hãy đọc bài viết Tối ưu hóa tốc độ bản dựng.

Nếu bạn mới sử dụng Gradle, hãy tìm hiểu những kiến thức cơ bản bằng cách đọc bài viết Định cấu hình bản dựng. Bạn cũng có thể đọc tài liệu tham khảo DSL của trình bổ trợ Android để tìm hiểu thêm về các thuộc tính được sử dụng trong trang này.

Quản lý dự án và nguồn

Dưới đây là một số cấu hình để quản lý mô-đun và tài nguyên của dự án. Để tìm hiểu thêm về việc tạo và quản lý các dự án và mô-đun, hãy đọc phần Tổng quan về Dự án.

Thay đổi cấu hình nhóm tài nguyên mặc định

Bạn có thể sử dụng khối sourceSets trong tệp build.gradle cấp mô-đun để thay đổi nơi mà Gradle tìm kiếm thu thập các tệp cho từng thành phần của một nhóm tài nguyên.

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

Quản lý thư viện và phần phụ thuộc

Gradle cung cấp một cơ chế vững chắc nhằm quản lý các phần phụ thuộc, cho dù đó là thư viện từ xa hay mô-đun thư viện cục bộ.

Nhắm mục tiêu các bản dựng cụ thể với cấu hình phần phụ thuộc

Nếu bạn muốn một phần phụ thuộc chỉ cho một nhóm tài nguyên biến thể bản dựng hoặc nhóm tài nguyên kiểm thử cụ thể, hãy viết hoa tên cấu hình phần phụ thuộc và thêm tiền tố là tên của biến thể bản dựng hoặc nhóm tài nguyên thử nghiệm vào tên cấu hình phần phụ thuộc.

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

Tạo các phiên bản khác nhau cho ứng dụng của bạn

Gradle và trình bổ trợ Android cho phép bạn tạo nhiều phiên bản của ứng dụng từ một mô-đun duy nhất bằng cách định cấu hình các biến thể bản dựng.

Định cấu hình mã phiên bản động

Theo mặc định, khi Gradle tạo các APK cho dự án của bạn, mỗi APK sẽ có thông tin phiên bản giống nhau, như được chỉ định trong tệp build.gradle cấp mô-đun. Vì Cửa hàng Google Play không cho phép nhiều APK cho cùng một ứng dụng có tất cả các thông tin về phiên bản, nên bạn cần đảm bảo rằng mỗi APK có một Mã phiên bản versionCode riêng trước khi tải lên Cửa hàng Play.

Bạn có thể thực hiện việc này bằng logic bản dựng tùy chỉnh mà sẽ gán một mã phiên bản khác nhau cho mỗi APK tại thời điểm tạo. Ví dụ: khi tạo các APK riêng biệt cho từng ABI, phiên bản APK tự động sẽ có dạng như sau:

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

Kết hợp nhiều phiên bản sản phẩm

Trong một số trường hợp, bạn có thể muốn kết hợp các cấu hình từ nhiều phiên bản sản phẩm. Để thực hiện việc này, trình bổ trợ Android cho Gradle sẽ cho phép bạn tạo các nhóm phiên bản sản phẩm, được gọi là các nhóm phiên bản.

Mã mẫu sau đây sử dụng thuộc tính flavorDimensions để tạo một nhóm phiên bản "mode" nhằm tập hợp các phiên bản sản phẩm "đầy đủ" và "minh họa" và một nhóm phiên bản "api" nhằm tập hợp các cấu hình phiên bản sản phẩm theo cấp độ API. Sau đó, Gradle sẽ kết hợp các phiên bản sản phẩm từ nhóm "mode" với các phiên bản sản phẩm của nhóm "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"
      ...
    }
  }
}
...

Lọc biến thể

Bạn có thể lọc các biến thể bản dựng mà bạn không muốn bằng cách sử dụng khối variantFilter trong tệp build.gradle của mô-đun. Mã mẫu sau đây cho biết Gradle không được tạo ra bất kỳ biến thể nào kết hợp các phiên bản sản phẩm "minApi21" và "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
        }
    }
}
...

Thử nghiệm ứng dụng

Để tìm hiểu thêm về cách chạy thử nghiệm đơn vị cục bộ và tích hợp, hãy đọc bài viết Thử nghiệm ứng dụng.

Định cấu hình tuỳ chọn tìm lỗi mã nguồn

Bạn có thể định cấu hình một số tùy chọn tìm lỗi mã nguồn bằng cách sử dụng khối lintOptions trong tệp build.gradle ở cấp mô-đun của mình. Để tìm hiểu thêm về cách sử dụng tuỳ chọn tìm lỗi mã nguồn cho dự án Android của bạn, hãy đọc bài viết Cải thiện mã của bạn bằng tuỳ chọn tìm lỗi mã nguồn.

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

Định cấu hình cài đặt tệp kê khai khả năng đo lường

Khi tạo tệp APK kiểm thử, Gradle sẽ tự động tạo tệp AndroidManifest.xml và định cấu hình tệp đó bằng nút <instrumentation>. Bạn có thể thay đổi một số chế độ cài đặt cho nút này bằng cách tạo một tệp kê khai khác trong nhóm tài nguyên kiểm thử hoặc định cấu hình tệp build.gradle ở cấp mô-đun, như trong mã mẫu sau.

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

Thay đổi loại bản thử nghiệm

Theo mặc định, tất cả các thử nghiệm đều chạy trên loại bản dựng gỡ lỗi. Bạn có thể thay đổi thuộc tính này thành một loại bản dựng khác bằng cách sử dụng thuộc tính testBuildType trong tệp build.gradle cấp mô-đun của mình. Ví dụ: nếu muốn chạy các kiểm thử dưới dạng bản dựng "chạy thử", chỉnh sửa tệp như được minh hoạ trong đoạn mã sau.

Groovy

android {
    ...
    testBuildType "staging"
}

Kotlin

android {
    ...
    testBuildType "staging"
}

Định cấu hình tuỳ chọn kiểm thử Gradle

Để chỉ định các tuỳ chọn thay đổi cách Gradle chạy tất cả các kiểm thử, hãy định cấu hình khối testOptions trong cấp mô-đun 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"
  }
}

Để chỉ định các tuỳ chọn chỉ dành cho kiểm thử đơn vị cục bộ, hãy định cấu hình khối 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'
        }
      }
    }
  }
}

Tối ưu hoá bản dựng

Phần này cung cấp một số cấu hình để giúp bạn tăng tốc các bản dựng đầy đủ và theo cấp tăng dần. Để tìm hiểu thêm, hãy đọc bài viết Tối ưu hóa tốc độ bản dựng.

Rút gọn mã

Android Studio sử dụng R8 mà sử dụng các tệp quy tắc ProGuard để rút gọn mã của bạn. Đối với các dự án mới, Android Studio sử dụng tệp cài đặt mặc định (proguard-android.txt) từ tools/proguard/folder của Android SDK. Để rút gọn mã hơn nữa, hãy thử dùng tệp proguard-android-optimize.txt ở cùng một vị trí.

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

Để thêm các quy tắc cụ thể cho từng biến thể bản dựng, hãy định cấu hình thuộc tính proguardFiles bổ sung cho từng phiên bản. Ví dụ: mẫu sau đây sẽ thêm flavor2-rules.pro vào "flavor2". Hiện tại, phiên bản phát hành của "flavor2" sử dụng cả 3 tệp quy tắc vì những tệp đó từ khối phát hành cũng được áp dụng.

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

Phát hành ứng dụng

Để tìm hiểu thêm về cách phát hành ứng dụng lên Google Play, hãy đọc bài viết Phát hành ứng dụng của bạn.

Ký ứng dụng

Mặc dù Android Studio cung cấp một cách đơn giản để định cấu hình ký bản phát hành qua giao diện người dùng, nhưng bạn có thể định cấu hình khối signingConfigs theo cách thủ công trong tệp build.gradle của mô-đun:

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

Loại bỏ thông tin ký riêng tư khỏi dự án

Theo mặc định, các cấu hình ký sẽ được ghi ở dạng văn bản thuần túy vào tệp build.gradle của mô-đun. Nếu đang làm việc với một nhóm hoặc một dự án nguồn mở, bạn có thể di chuyển thông tin nhạy cảm này ra khỏi các tệp bản dựng bằng cách thực hiện các bước sau.

  1. Tạo một tệp có tên là keystore.properties trong thư mục gốc của dự án và bao gồm các thông tin sau:
    storePassword=myStorePassword
    keyPassword=myKeyPassword
    keyAlias=myKeyAlias
    storeFile=myStoreFileLocation
    
  2. Trong tệp build.gradle của bạn, hãy tải tệp keystore.properties như sau (phải ở trước khối 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. Nhập thông tin ký ứng dụng được lưu trữ trong đối tượng 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. Nhấp vào Đồng bộ hóa ngay trong thanh thông báo.

Để tìm hiểu thêm về tính năng ký ứng dụng, hãy đọc bài viết Ký ứng dụng của bạn.

Đơn giản hoá hoạt động phát triển ứng dụng

Các mẹo sau đây giúp bạn phát triển ứng dụng Android dễ dàng hơn.

Chia sẻ trường tuỳ chỉnh và giá trị tài nguyên với đoạn mã của ứng dụng

Tại thời điểm tạo, Gradle tạo ra lớp BuildConfig để mã ứng dụng của bạn có thể kiểm tra thông tin về bản dựng hiện tại. Bạn cũng có thể thêm các trường tùy chỉnh vào lớp BuildConfig từ tệp cấu hình của bản dựng Gradle bằng phương thức buildConfigField() rồi truy cập vào các giá trị đó trong mã thời gian chạy của ứng dụng. Tương tự, bạn có thể thêm các giá trị tài nguyên ứng dụng bằng 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")
    }
  }
}
...

Trong mã ứng dụng của mình, bạn có thể truy cập vào các thuộc tính sau:

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

Chia sẻ thuộc tính với tệp kê khai

Trong một số trường hợp, bạn có thể cần khai báo cùng một thuộc tính trong cả tệp kê khai và mã của bạn (ví dụ: khi khai báo cơ quan chức năng cho FileProvider). Thay vì cập nhật cùng một thuộc tính trong nhiều vị trí để phản ánh một thay đổi, hãy xác định một thuộc tính duy nhất trong tệp build.gradle của mô-đun để thuộc tính đó có sẵn cho cả tệp kê khai và mã của bạn, như được minh hoạ trong mẫu sau. Để tìm hiểu thêm, hãy đọc bài viết Chèn các biến bản dựng vào Tệp kê khai.

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

Trong tệp kê khai của bạn, hãy truy cập phần giữ chỗ như sau:

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

Việc truy cập vào trường FILES_AUTHORITY trong mã của ứng dụng sẽ có dạng như sau:

Kotlin

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

Java

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