Định cấu hình biến thể bản dựng

Trang này cho bạn biết cách định cấu hình các biến thể bản dựng để tạo nhiều phiên bản ứng dụng từ một dự án duy nhất và cách quản lý hợp lý các phần phụ thuộc cũng như cấu hình ký tên.

Mỗi biến thể bản dựng đại diện cho một phiên bản riêng của ứng dụng được tạo. Ví dụ: bạn muốn tạo một phiên bản miễn phí của ứng dụng, trong đó chỉ giới hạn một số nội dung nhất định, và một phiên bản khác có tính phí và chứa nhiều nội dung hơn. Bạn cũng có thể tạo các phiên bản ứng dụng riêng theo loại thiết bị mà ứng dụng hướng đến, dựa trên cấp độ API hoặc các biến thể thiết bị khác.

Các biến thể bản dựng (build variant) là kết quả của quá trình trong đó Gradle sử dụng một bộ quy tắc cụ thể để kết hợp các chế độ cài đặt, đoạn mã và tài nguyên được định cấu hình trong các loại bản dựng (build type) và phiên bản sản phẩm (product flavor). Tuy không trực tiếp định cấu hình biến thể bản dựng, bạn vẫn có thể định cấu hình các loại bản dựng và phiên bản sản phẩm cấu thành nên những biến thể bản dựng đó.

Ví dụ: một phiên bản sản phẩm "demo" (minh hoạ) có thể nêu rõ các yêu cầu nhất định về tính năng và thiết bị, chẳng hạn như mã nguồn tuỳ chỉnh, tài nguyên và cấp độ API tối thiểu. Trong khi đó, loại bản dựng "debug" (gỡ lỗi) áp dụng các chế độ cài đặt bản dựng và đóng gói riêng biệt, chẳng hạn như các tuỳ chọn gỡ lỗi và khoá ký ứng dụng (signing key). Biến thể bản dựng kết hợp cả hai là phiên bản "demoDebug" của ứng dụng và biến thể này sẽ kết hợp cả cấu hình và tài nguyên có trong phiên bản sản phẩm "demo", loại bản dựng "debug" và nhóm tài nguyên main/.

Định cấu hình loại bản dựng

Bạn có thể tạo và định cấu hình loại bản dựng bên trong khối android của tệp build.gradle cấp mô-đun đối với Groovy, hoặc tệp build.gradle.kts đối với tập lệnh Kotlin. Khi bạn tạo một mô-đun mới, Android Studio sẽ tự động tạo các loại bản dựng tương ứng với bản gỡ lỗi và bản phát hành. Tuy loại bản dựng gỡ lỗi không xuất hiện trong tệp cấu hình bản dựng, nhưng Android Studio sẽ định cấu hình cho loại bản dựng này với debuggable true. Điều này cho phép bạn gỡ lỗi ứng dụng trên các thiết bị Android bảo mật và định cấu hình ký ứng dụng (app signing) bằng kho khoá (keystore) gỡ lỗi chung.

Nếu muốn thêm hoặc thay đổi một số chế độ cài đặt nào đó, bạn có thể thêm loại bản dựng gỡ lỗi này vào phần cấu hình. Mẫu sau đây sẽ chỉ định một applicationIdSuffix cho loại bản dựng gỡ lỗi và định cấu hình loại bản dựng "ra mắt để thử nghiệm" (staging) được khởi chạy bằng chế độ cài đặt cho loại bản dựng gỡ lỗi.

Groovy

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

Kotlin

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

Lưu ý: Khi thay đổi tệp cấu hình bản dựng, Android Studio sẽ yêu cầu bạn đồng bộ dự án với cấu hình mới này. Khi bạn thực hiện thay đổi, hãy nhấp vào Sync Now (Đồng bộ hoá ngay) trên thanh thông báo xuất hiện để đồng bộ hoá dự án, hoặc nhấp vào biểu tượng Sync Project (Đồng bộ hoá dự án) trên thanh công cụ. Nếu Android Studio phát hiện bất cứ lỗi nào trong cấu hình, cửa sổ Messages (Thông báo) sẽ xuất hiện cùng mô tả chi tiết về vấn đề đó.

Để tìm hiểu thêm về tất cả thuộc tính mà bạn có thể định cấu hình cho các loại bản dựng, hãy tham khảo BuildType.

Định cấu hình phiên bản sản phẩm

Quá trình tạo phiên bản sản phẩm cũng tương tự như quá trình tạo các loại bản dựng. Hãy thêm các phiên bản sản phẩm đó vào khối productFlavors trong cấu hình bản dựng và thêm chế độ cài đặt bạn muốn. Các phiên bản sản phẩm hỗ trợ các thuộc tính tương tự như defaultConfig, vì defaultConfig thực sự thuộc về lớp ProductFlavor. Tức là bạn có thể cung cấp cấu hình cơ sở cho tất cả phiên bản trong khối defaultConfig, và mỗi phiên bản có thể thay đổi bất kỳ giá trị mặc định nào trong đó, chẳng hạn như applicationId. Để tìm hiểu thêm về mã nhận dạng ứng dụng, hãy đọc nội dung Thiết lập mã nhận dạng ứng dụng.

Lưu ý: Bạn vẫn cần chỉ định tên gói bằng thuộc tính package trong tệp kê khai main/. Bạn cũng phải sử dụng tên gói đó trong mã nguồn để tham chiếu đến lớp R, hoặc để giải quyết mọi hoạt động hoặc đăng ký dịch vụ có liên quan. Điều này cho phép bạn sử dụng applicationId để cung cấp mã nhận dạng duy nhất cho mỗi phiên bản sản phẩm để đóng gói và phân phối mà không phải thay đổi mã nguồn.

Tất cả phiên bản phải thuộc một nhóm phiên bản (flavor dimension) được đặt tên, đó là một nhóm các phiên bản sản phẩm. Bạn phải chỉ định tất cả phiên bản cho một nhóm phiên bản nào đó. Nếu không làm như vậy, bạn sẽ gặp lỗi bản dựng sau đây.

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

Nếu một mô-đun chỉ được gán cho một nhóm phiên bản thì trình bổ trợ Android cho Gradle sẽ tự động gán tất cả phiên bản của mô-đun cho nhóm phiên bản đó.

Mã mẫu sau đây tạo một nhóm phiên bản có tên là "version" (phiên bản) và thêm các phiên bản sản phẩm "demo" (minh hoạ) và "full" (đầy đủ). Các phiên bản này tự cung cấp applicationIdSuffixversionNameSuffix riêng tương ứng:

Groovy

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

Kotlin

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

Lưu ý: Nếu bạn có một ứng dụng cũ (tạo trước tháng 8 năm 2021) đang phân phối bằng tệp APK trên Google Play, để phân phối ứng dụng bằng tính năng hỗ trợ nhiều tệp APK trên Google Play, hãy chỉ định cùng một giá trị applicationId cho tất cả biến thể và cung cấp cho mỗi biến thể một versionCode riêng biệt. Để phân phối nhiều biến thể của ứng dụng dưới dạng các ứng dụng riêng biệt trong Google Play, bạn cần chỉ định applicationId riêng cho từng biến thể.

Sau khi tạo và định cấu hình các phiên bản sản phẩm, hãy nhấp vào Sync Now (Đồng bộ hoá ngay) trong thanh thông báo. Sau khi quá trình đồng bộ hoàn tất, Gradle sẽ tự động tạo các biến thể bản dựng dựa trên loại bản dựng và phiên bản sản phẩm, đồng thời đặt tên cho các biến thể đó theo định dạng <product-flavor><Build-Type>. Ví dụ: nếu bạn đã tạo các phiên bản sản phẩm "demo" và "full" và giữ lại các loại bản dựng "debug" và "release" mặc định, Gradle sẽ tạo các biến thể bản dựng sau đây:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Để chọn biến thể bản dựng cần tạo và chạy, hãy chuyển đến phần Build (Bản dựng) > Select Build Variant (Chọn biến thể bản dựng) rồi chọn một biến thể bản dựng trên trình đơn. Để bắt đầu tuỳ chỉnh từng biến thể bản dựng bằng các tính năng và tài nguyên riêng, bạn cần tạo và quản lý các nhóm tài nguyên, như mô tả trên trang này.

Thay đổi mã nhận dạng ứng dụng cho các biến thể bản dựng

Khi tạo một tệp APK hoặc AAB cho ứng dụng, các công cụ bản dựng sẽ gắn thẻ bằng mã nhận dạng ứng dụng được xác định trong khối defaultConfig của tệp build.gradle (như trình bày bên dưới). Tuy nhiên, nếu bạn muốn tạo nhiều phiên bản ứng dụng để xuất hiện dưới dạng các trang thông tin riêng biệt trên Cửa hàng Google Play, chẳng hạn như phiên bản "free" (miễn phí) và "pro" (chuyên nghiệp), bạn cần tạo các biến thể bản dựng riêng biệt, trong đó mỗi biến thể có một mã nhận dạng ứng dụng riêng.

Trong trường hợp này, hãy định nghĩa mỗi biến thể bản dựng là một phiên bản sản phẩm riêng biệt. Với mỗi phiên bản trong khối productFlavors, bạn có thể xác định lại thuộc tính applicationId, hoặc thay vào đó bạn có thể bổ sung một mảnh (segment) vào mã nhận dạng ứng dụng mặc định thông qua applicationIdSuffix như minh họa dưới đây:

Groovy

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

Bằng cách này, mã nhận dạng ứng dụng của phiên bản sản phẩm "free" là "com.example.myapp.free".

Bạn cũng có thể sử dụng applicationIdSuffix để bổ sung một phân đoạn dựa trên loại bản dựng của mình, như minh hoạ dưới đây:

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Kotlin

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

Gradle áp dụng cấu hình loại bản dựng sau phiên bản sản phẩm nên mã nhận dạng ứng dụng cho biến thể bản dựng "free debug" ("gỡ lỗi miễn phí") hiện là "com.example.myapp.free.debug". Vì hai ứng dụng không thể nào có cùng một mã nhận dạng ứng dụng nên cách làm này sẽ rất hữu ích khi bạn muốn có cả bản gỡ lỗi và phát hành trên cùng một thiết bị.

Nếu bạn có một ứng dụng cũ (được tạo trước tháng 8 năm 2021) để phân phối bằng tệp APK trên Google Play, và bạn muốn sử dụng cùng một trang thông tin ứng dụng để phân phối nhiều tệp APK trong khi mỗi tệp APK nhắm đến một cấu hình thiết bị riêng (ví dụ: cấp độ API), thì bạn phải sử dụng cùng một mã nhận dạng ứng dụng cho mỗi biến thể bản dựng nhưng cung cấp cho mỗi tệp APK một versionCode riêng. Để biết thêm thông tin, hãy đọc nội dung về việc Hỗ trợ nhiều tệp APK. Việc phát hành thông qua tệp AAB sẽ không bị ảnh hưởng vì tệp AAB sử dụng một cấu phần mềm duy nhất sử dụng một mã phiên bản và mã nhận dạng ứng dụng duy nhất theo mặc định.

Mẹo: Nếu cần tham chiếu đến mã nhận dạng ứng dụng trong tệp kê khai, bạn có thể sử dụng phần giữ chỗ ${applicationId} trong mọi thuộc tính tệp kê khai. Trong quá trình tạo bản dựng, Gradle sẽ thay thế thẻ này bằng mã nhận dạng ứng dụng thực tế. Để biết thêm thông tin, hãy xem nội dung Chèn các biến bản dựng vào tệp kê khai.

Kết hợp nhiều phiên bản sản phẩm với các nhóm phiên bản

Trong một số trường hợp, bạn có thể muốn kết hợp các cấu hình trong nhiều phiên bản sản phẩm. Ví dụ: bạn muốn tạo các cấu hình riêng cho từng phiên bản sản phẩm "full" ("đầy đủ") và "demo" ("minh hoạ") dựa trên cấp độ API. Để thực hiện việc này, trình bổ trợ Android cho Gradle cho phép bạn tạo nhiều nhóm phiên bản sản phẩm, gọi là các nhóm phiên bản (flavor dimension).

Khi tạo ứng dụng, Gradle sẽ kết hợp một cấu hình phiên bản sản phẩm trong mỗi nhóm phiên bản mà bạn định nghĩa cùng với một cấu hình loại bản dựng để tạo biến thể bản dựng hoàn thiện. Gradle không kết hợp các phiên bản sản phẩm thuộc cùng một nhóm phiên bản.

Mã mẫu sau đây sử dụng thuộc tính flavorDimensions để tạo nhóm phiên bản "mode" để nhóm các phiên bản sản phẩm "full" và "demo" với nhau, đồng thời, tạo nhóm phiên bản "api" để nhóm các cấu hình phiên bản sản phẩm theo cấp độ API:

Groovy

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

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their 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, with the first dimension having 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.

      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 the dimensions determines their 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, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 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.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Số lượng biến thể bản dựng Gradle tạo ra bằng với số lượng phiên bản trong mỗi nhóm phiên bản nhân với số lượng loại bản dựng mà bạn định cấu hình. Khi Gradle đặt tên cho từng biến thể bản dựng hoặc các cấu phần mềm tương ứng, các phiên bản sản phẩm thuộc nhóm phiên bản có mức độ ưu tiên cao hơn sẽ xuất hiện trước tiên, tiếp đến là các phiên bản trong các nhóm phiên bản có mức độ ưu tiên thấp hơn, sau đó là loại bản dựng.

Lấy cấu hình bản dựng trước đó làm ví dụ, Gradle sẽ tạo tổng cộng 12 biến thể bản dựng với lược đồ đặt tên như sau:

  • Biến thể bản dựng: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • Tệp APK tương ứng: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Ví dụ:
    Biến thể bản dựng: minApi24DemoDebug
    Tệp APK tương ứng: app-minApi24-demo-debug.apk

Ngoài các thư mục nhóm tài nguyên bạn có thể tạo cho mỗi phiên bản sản phẩm và biến thể bản dựng riêng lẻ, bạn cũng có thể tạo thư mục nhóm tài nguyên cho mỗi tổ hợp phiên bản sản phẩm. Ví dụ: bạn có thể tạo và thêm các tài nguyên Java vào thư mục src/demoMinApi24/java/ và Gradle chỉ sử dụng những tài nguyên đó khi tạo một biến thể kết hợp từ hai phiên bản sản phẩm đó.

Các nhóm tài nguyên bạn tạo cho các tổ hợp phiên bản sản phẩm có mức độ ưu tiên cao hơn các nhóm tài nguyên thuộc từng phiên bản sản phẩm riêng lẻ. Để tìm hiểu thêm về nhóm tài nguyên và cách Gradle hợp nhất các tài nguyên, hãy đọc phần về cách tạo các nhóm tài nguyên.

Lọc biến thể

Gradle sẽ tạo một biến thể bản dựng cho mọi tổ hợp phiên bản sản phẩm và loại bản dựng mà bạn định cấu hình. Tuy nhiên, có thể xuất hiện một số biến thể bản dựng bạn không cần hoặc không phù hợp trong bối cảnh của dự án. Để xoá một số cấu hình biến thể bản dựng nhất định, hãy tạo một bộ lọc biến thể trong tệp build.gradle cấp mô-đun.

Lấy cấu hình bản dựng ở phần trước để làm ví dụ, giả sử bạn dự định chỉ hỗ trợ API cấp 23 trở lên cho bản minh hoạ (demo) của ứng dụng. Bạn có thể sử dụng khối variantFilter để lọc ra tất cả cấu hình biến thể bản dựng tổ hợp từ 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 += listOf("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.enable = false
        }
    }
}
...

Sau khi thêm một bộ lọc biến thể vào cấu hình bản dựng và nhấp vào Sync Now (Đồng bộ hoá ngay) trong thanh thông báo, Gradle sẽ bỏ qua mọi biến thể bản dựng đáp ứng các điều kiện mà bạn chỉ định. Các biến thể bản dựng không còn xuất hiện trong trình đơn khi bạn nhấp vào Build > Select Build Variant (Tạo > Chọn biến thể bản dựng) trên thanh trình đơn hoặc biểu tượng Build Variants (Biến thể bản dựng) trên thanh cửa sổ công cụ.

Tạo nhóm tài nguyên

Theo mặc định, Android Studio sẽ tạo nhóm tài nguyên main/ và các thư mục chứa toàn bộ những gì bạn muốn chia sẻ giữa tất cả biến thể bản dựng của mình. Tuy nhiên, bạn có thể tạo các nhóm tài nguyên mới để kiểm soát chính xác những tệp mà Gradle biên dịch và đóng gói cho các loại bản dựng, phiên bản sản phẩm (và tổ hợp các phiên bản sản phẩm khi sử dụng các nhóm phiên bản) và các biến thể bản dựng cụ thể.

Ví dụ: bạn có thể định nghĩa chức năng cơ bản trong nhóm tài nguyên main/ và sử dụng các nhóm tài nguyên phiên bản sản phẩm để thay đổi nhãn ứng dụng theo khách hàng hoặc chỉ cung cấp các quyền đặc biệt và chức năng ghi nhật ký dành cho các biến thể bản dựng sử dụng loại bản dựng gỡ lỗi.

Gradle cho rằng các tệp và thư mục nhóm tài nguyên sẽ được bố trí theo một cách nhất định, tương tự như nhóm tài nguyên main/. Ví dụ: Gradle cho rằng các tệp lớp Kotlin hoặc Java dành riêng cho loại bản dựng "debug" sẽ được đặt trong các thư mục src/debug/kotlin/ hoặc src/debug/java/.

Trình bổ trợ Android cho Gradle cung cấp một tác vụ Gradle rất hữu ích, giúp bạn biết cách sắp xếp các tệp cho từng loại bản dựng, phiên bản sản phẩm cũng như biến thể bản dựng. Ví dụ: mẫu sau đây trong phần kết quả của tác vụ sẽ giúp mô tả vị trí nơi Gradle dự kiến sẽ tìm một số tệp nhất định nào đó cho loại bản dựng "debug":

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

Để xem kết quả này, hãy tiến hành như sau:

  1. Nhấp vào Gradle trong thanh cửa sổ công cụ.
  2. Chuyển đến MyApplication (Ứng dụng của tôi) > Tasks (Tác vụ) > android rồi nhấp đúp vào sourceSets.

    Để xem thư mục Tasks (Tác vụ), bạn phải cho phép Gradle tạo danh sách tác vụ trong quá trình đồng bộ hoá. Để thực hiện điều này, hãy làm theo các bước sau:

    1. Nhấp vào File > Settings > Experimental (Tệp > Cài đặt > Thử nghiệm) (Android Studio > Preferences > Experimental (Android Studio > Lựa chọn ưu tiên > Thử nghiệm) trên macOS).
    2. Bỏ chọn Do not build Gradle task list during Gradle sync (Không tạo danh sách tác vụ Gradle trong quá trình đồng bộ hoá Gradle).
  3. Sau khi Gradle thực thi tác vụ, cửa sổ Run (Chạy) sẽ mở ra để cho thấy kết quả đầu ra.

Lưu ý: Kết quả đầu ra của tác vụ này cũng giúp bạn biết cách sắp xếp các nhóm tài nguyên cho các tệp bạn muốn sử dụng để chạy các lượt kiểm thử cho ứng dụng của bạn, chẳng hạn như các nhóm tài nguyên kiểm thử test/androidTest/.

Khi tạo một biến thể bản dựng mới, Android Studio sẽ không tạo các thư mục nhóm tài nguyên mà sẽ cung cấp một số tuỳ chọn để giúp bạn thực hiện việc này. Ví dụ: để tạo chỉ thư mục java/ cho loại bản dựng "debug":

  1. Mở ngăn Project (Dự án) rồi chọn chế độ xem Project (Dự án) trong trình đơn thả xuống ở đầu ngăn.
  2. Chuyển đến MyProject/app/src/
  3. Nhấp chuột phải vào thư mục src rồi chọn Mới > Thư mục.
  4. Trên trình đơn trong Bộ nguồn Gradle, hãy chọn full/java.
  5. Nhấn Enter.

Android Studio tạo một thư mục nhóm tài nguyên cho loại bản dựng gỡ lỗi rồi tạo thư mục java/ bên trong thư mục này. Ngoài ra, Android Studio có thể tạo các thư mục này khi bạn thêm một tệp mới vào dự án cho một biến thể bản dựng cụ thể.

Ví dụ: để tạo tệp giá trị XML cho loại bản dựng "debug":

  1. Trong ngăn Project (Dự án), hãy nhấp chuột phải vào thư mục src rồi chọn New (Mới) > XML > Values XML File (Tệp giá trị XML).
  2. Nhập tên cho tệp XML hoặc giữ tên mặc định.
  3. Trên trình đơn bên cạnh Target Source Set (Nhóm tài nguyên đích), hãy chọn debug.
  4. Nhấp vào Finish (Hoàn tất).

Vì loại bản dựng "debug" được chỉ định làm nhóm tài nguyên đích nên Android Studio sẽ tự động tạo các thư mục cần thiết khi tạo tệp XML. Cấu trúc của thư mục kết quả sẽ có dạng như Hình 1.

Hình 1. Các thư mục nhóm tài nguyên mới cho loại bản dựng gỡ lỗi.

Các nhóm tài nguyên đang hoạt động được biểu thị bằng một chỉ báo màu xanh lục trong biểu tượng. Nhóm tài nguyên debug có hậu tố là [main] để cho biết nhóm tài nguyên đó sẽ được hợp nhất vào nhóm tài nguyên main.

Áp dụng quy trình tương tự, bạn có thể tạo thư mục nhóm tài nguyên cho các phiên bản sản phẩm (chẳng hạn như src/demo/) và các biến thể bản dựng (chẳng hạn như src/demoDebug/). Ngoài ra, bạn có thể tạo nhóm tài nguyên kiểm thử dành cho các biến thể bản dựng cụ thể, chẳng hạn như src/androidTestDemoDebug/. Để tìm hiểu thêm, hãy tham khảo nội dung về các nhóm tài nguyên kiểm thử.

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

Nếu bạn có các nguồn tài nguyên chưa được sắp xếp theo cấu trúc tệp nhóm tài nguyên mặc định như Gradle mong muốn (như mô tả trong phần tạo các nhóm tài nguyên ở trên), bạn có thể sử dụng khối sourceSets để thay đổi vị trí Gradle có thể thu thập các tệp cho từng thành phần của một nhóm tài nguyên.

Khối sourceSets phải nằm trong khối android. Bạn không cần phải định vị lại các tệp tài nguyên mà chỉ cần cung cấp cho Gradle (các) đường dẫn liên quan đến tệp build.gradle ở cấp mô-đun, nơi Gradle có thể tìm thấy các tệp đó cho từng thành phần của nhóm tài nguyên. Để tìm hiểu xem bạn có thể định cấu hình những thành phần nào và có thể ánh xạ những thành phần đó với nhiều tệp hoặc thư mục hay không, vui lòng xem tài liệu tham khảo về API của trình bổ trợ Android cho Gradle.

Mã mẫu sau đây sẽ ánh xạ các nguồn tài nguyên trong thư mục app/other/ đến các thành phần nhất định của nhóm tài nguyên main và thay đổi thư mục gốc của nhóm tài nguyên androidTest.

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']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      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 {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("main") {
    // Changes the directory for Java sources. The default directory is
    // 'src/main/java'.
    java.setSrcDirs(listOf("other/java"))

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("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")
      ...
  }
}
...

Vui lòng lưu ý là một thư mục nguồn chỉ có thể thuộc về một nhóm tài nguyên. Ví dụ: bạn không thể chia sẻ cùng một nguồn thử nghiệm với cả nhóm tài nguyên testandroidTest. Điều này là do Android Studio tạo các mô-đun IntelliJ riêng biệt cho mỗi nhóm tài nguyên và không thể hỗ trợ nội dung gốc trùng lặp trên các nhóm tài nguyên.

Bản dựng chứa nhóm tài nguyên

Bạn có thể sử dụng các thư mục nhóm tài nguyên để chứa mã và tài nguyên bạn chỉ muốn đóng gói theo cấu hình nhất định. Ví dụ: nếu bạn đang tạo biến thể bản dựng "demoDebug", là sản phẩm chéo (crossproduct) của phiên bản sản phẩm "demo" và loại bản dựng "debug", Gradle sẽ xem xét các thư mục này và thiết lập mức độ ưu tiên như sau:

  1. src/demoDebug/ (nhóm tài nguyên biến thể bản dựng)
  2. src/debug/ (nhóm tài nguyên loại bản dựng)
  3. src/demo/ (nhóm tài nguyên phiên bản sản phẩm)
  4. src/main/ (nhóm tài nguyên chính)

Các nhóm tài nguyên được tạo cho tổ hợp các phiên bản sản phẩm phải bao gồm tất cả nhóm phiên bản. Ví dụ: nhóm tài nguyên biến thể bản dựng phải là tổ hợp của (loại bản dựng + tất cả các nhóm phiên bản). Việc hợp nhất mã và tài nguyên liên quan đến các thư mục bao gồm nhiều, nhưng không phải tất cả, nhóm phiên bản sẽ không được hỗ trợ.

Lưu ý: Nếu bạn kết hợp nhiều phiên bản sản phẩm thì mức độ ưu tiên giữa các phiên bản sản phẩm sẽ được xác định theo nhóm phiên bản của những phiên bản sản phẩm này. Khi liệt kê các nhóm phiên bản chứa thuộc tính android.flavorDimensions, các phiên bản sản phẩm thuộc nhóm phiên bản liệt kê đầu tiên sẽ có mức độ ưu tiên cao hơn so với các phiên bản thuộc nhóm phiên bản thứ hai, v.v. Ngoài ra, các nhóm tài nguyên bạn tạo cho các tổ hợp phiên bản sản phẩm sẽ có mức độ ưu tiên cao hơn so với các nhóm tài nguyên thuộc từng phiên bản sản phẩm riêng lẻ.

Thứ tự ưu tiên sẽ xác định nhóm tài nguyên nào có mức độ ưu tiên cao hơn khi Gradle kết hợp mã và tài nguyên. Thư mục nhóm tài nguyên demoDebug/ có thể chứa các tệp dành riêng cho biến thể bản dựng đó, do đó nếu demoDebug/ bao gồm một tệp cũng được xác định trong debug/ thì Gradle sẽ sử dụng tệp này trong nhóm tài nguyên demoDebug/. Tương tự như vậy, Gradle sẽ thiết lập mức độ ưu tiên cao hơn cho các tệp trong các nhóm tài nguyên loại bản dựng và phiên bản sản phẩm so với các tệp trong main/. Gradle sẽ xem xét thứ tự ưu tiên này khi áp dụng các quy tắc tạo bản dựng sau đây:

  • Tất cả mã nguồn trong thư mục kotlin/ hoặc java/ sẽ được biên dịch cùng nhau để tạo ra một kết quả đầu ra duy nhất.

    Lưu ý: Đối với một biến thể bản dựng cụ thể, Gradle sẽ tạo ra lỗi bản dựng nếu gặp hai hoặc nhiều thư mục nhóm tài nguyên cùng định nghĩa một lớp Java. Ví dụ: khi tạo một ứng dụng gỡ lỗi, bạn không thể định nghĩa cả src/debug/Utility.ktsrc/main/Utility.kt, vì Gradle sẽ xem xét cả hai thư mục này trong quá trình tạo bản dựng và phát hiện một lỗi "trùng lặp lớp". Nếu bạn muốn có các phiên bản Utility.kt riêng cho từng loại bản dựng, mỗi loại bản dựng phải xác định phiên bản tệp riêng và không đưa tệp này vào nhóm tài nguyên main/.

  • Các tệp kê khai được hợp nhất với nhau thành một tệp kê khai duy nhất. Mức độ ưu tiên sẽ được sắp xếp theo cùng thứ tự như danh sách trong ví dụ trước. Tức là chế độ cài đặt tệp kê khai cho loại bản dựng sẽ ghi đè chế độ cài đặt tệp kê khai cho phiên bản sản phẩm, v.v. Để tìm hiểu thêm, hãy đọc nội dung về cách hợp nhất các tệp kê khai.
  • Các tệp trong thư mục values/ được hợp nhất với nhau. Nếu hai tệp có cùng tên (chẳng hạn như hai tệp strings.xml), mức độ ưu tiên sẽ được sắp xếp theo cùng thứ tự như danh sách ở ví dụ trên. Tức là các giá trị được định nghĩa trong một tệp trong nhóm tài nguyên loại bản dựng sẽ ghi đè các giá trị được định nghĩa trong cùng tệp đó trong một phiên bản sản phẩm, v.v.
  • Các tài nguyên trong các thư mục res/asset/ sẽ được đóng gói cùng nhau. Với các tài nguyên có cùng tên được định nghĩa trong hai nhóm tài nguyên trở lên, mức độ ưu tiên sẽ được sắp xếp theo cùng thứ tự như danh sách ở ví dụ trên.
  • Các tài nguyên và tệp kê khai đi kèm với các phần phụ thuộc mô-đun thư viện được Gradle thiết lập mức độ ưu tiên thấp nhất trong quá trình tạo ứng dụng.

Khai báo phần phụ thuộc

Để định cấu hình phần phụ thuộc cho biến thể bản dựng cụ thể hoặc nhóm tài nguyên kiểm thử, hãy thêm tiền tố cho tên của biến thể bản dựng hoặc nhóm tài nguyên kiểm thử trước từ khoá Implementation, như minh hoạ trong ví dụ sau:

Groovy

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

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

Kotlin

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

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

Để biết thêm thông tin về việc định cấu hình các phần phụ thuộc, hãy xem nội dung Thêm phần phụ thuộc vào bản dựng.

Sử dụng tính năng quản lý phần phụ thuộc có thể nhận biết biến thể

Trình bổ trợ Android cho Gradle 3.0.0 trở lên cung cấp một cơ chế phần phụ thuộc mới sẽ tự động so khớp các biến thể trong quá trình sử dụng thư viện. Tức là biến thể debug của một ứng dụng sẽ tự động sử dụng biến thể debug của một thư viện, v.v. Điều này cũng áp dụng tương tự khi sử dụng các phiên bản — một biến thể freeDebug của ứng dụng sẽ sử dụng một biến thể freeDebug của thư viện.

Để trình bổ trợ so khớp chính xác các biến thể, bạn cần cung cấp các phương án so khớp dự phòng theo mô tả trong phần sau đây, ví dụ như trường hợp không thể so khớp trực tiếp.

Ví dụ: giả sử ứng dụng của bạn định cấu hình loại bản dựng có tên là "staging" ("ra mắt để thử nghiệm"), nhưng một trong các phần phụ thuộc của thư viện thì không. Khi cố gắng tạo phiên bản "staging" cho ứng dụng, trình bổ trợ không biết nên sử dụng phiên bản nào của thư viện và bạn sẽ thấy thông báo lỗi có dạng như sau:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

Khắc phục lỗi bản dựng liên quan đến việc so khớp biến thể

Trình bổ trợ này cung cấp các phần tử DSL, cho phép bạn kiểm soát cách Gradle giải quyết các tình huống trong đó không thể so khớp biến thể trực tiếp giữa ứng dụng và phần phụ thuộc.

Sau đây là danh sách vấn đề liên quan đến việc so khớp phần phụ thuộc có thể nhận biết biến thể và cách giải quyết các vấn đề đó bằng cách sử dụng các thuộc tính DSL:

  • Ứng dụng chứa một loại bản dựng mà phần phụ thuộc thư viện không có.

    Ví dụ: ứng dụng chứa loại bản dựng "staging" ("ra mắt để thử nghiệm") nhưng phần phụ thuộc chỉ có loại bản dựng "debug" ("gỡ lỗi") và "release" ("phát hành").

    Xin lưu ý rằng sẽ không có vấn đề gì khi phần phụ thuộc thư viện chứa loại bản dựng mà ứng dụng không có. Lý do là trình bổ trợ này không bao giờ yêu cầu loại bản dựng nào từ phần phụ thuộc.

    Hãy sử dụng matchingFallbacks để chỉ định phương án so khớp thay thế cho một loại bản dựng cho trước, như minh hoạ tại đây:

    Groovy

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
    

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }
  • Đối với một nhóm phiên bản tồn tại trong cả ứng dụng và thư viện phần phụ thuộc, ứng dụng sẽ chứa các phiên bản không có trong thư viện này.

    Ví dụ: cả ứng dụng và phần phụ thuộc thư viện của ứng dụng đều chứa nhóm phiên bản "tier" (tầng). Tuy nhiên, nhóm "tier" này trong ứng dụng bao gồm phiên bản "free" (miễn phí) và "paid" (có tính phí) nhưng phần phụ thuộc chỉ có các phiên bản "demo" và "paid" trong cùng một nhóm phiên bản này.

    Lưu ý rằng với một nhóm phiên bản nào đó tồn tại trong cả ứng dụng và phần phụ thuộc thư viện, sẽ không có vấn đề gì khi thư viện có chứa một phiên bản sản phẩm mà ứng dụng không có. Lý do là trình bổ trợ này không bao giờ yêu cầu phiên bản đó từ phần phụ thuộc.

    Sử dụng matchingFallbacks để chỉ định các phương án so khớp thay thế cho phiên bản sản phẩm "free" ("miễn phí") của ứng dụng, như minh hoạ dưới đây:

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
    

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }
    
  • Phần phụ thuộc thư viện chứa nhóm phiên bản mà ứng dụng không có.

    Ví dụ: phần phụ thuộc thư viện chứa các phiên bản cho nhóm "minApi" nhưng ứng dụng chỉ chứa phiên bản cho nhóm "tier". Khi bạn muốn tạo phiên bản ứng dụng "freeDebug", trình bổ trợ này sẽ không biết nên sử dụng phiên bản "minApi23Debug" hay "minApi18Debug" của phần phụ thuộc.

    Lưu ý rằng sẽ không có vấn đề gì khi ứng dụng chứa nhóm phiên bản mà phần phụ thuộc thư viện không có. Lý do là trình bổ trợ này chỉ so khớp các phiên bản của nhóm phiên bản tồn tại trong phần phụ thuộc. Ví dụ: nếu một phần phụ thuộc không chứa nhóm phiên bản nào cho ABI (Giao diện nhị phân ứng dụng), phiên bản "freeX86Debug" của ứng dụng chỉ sử dụng phiên bản "freeDebug" của phần phụ thuộc.

    Sử dụng missingDimensionStrategy trong khối defaultConfig để chỉ định phiên bản mặc định cho trình bổ trợ chọn trong mỗi nhóm bị thiếu, như minh hoạ trong ví dụ sau. Bạn cũng có thể ghi đè các lựa chọn của mình trong khối productFlavors, nhờ vậy mỗi phiên bản có thể chỉ định một chiến lược so khớp khác nhau cho nhóm phiên bản bị thiếu.

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }
    

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }
    

Để biết thêm thông tin, hãy xem thông tin về matchingFallbacksmissingDimensionStrategy trong tài liệu tham khảo về DSL của trình bổ trợ Android cho Gradle.

Định cấu hình chế độ cài đặt chữ ký

Gradle không ký tệp APK hoặc AAB của bản phát hành phát hành trừ phi bạn định nghĩa rõ ràng cấu hình ký tên cho bản dựng này. Nếu bạn chưa có khoá ký (signing key), hãy dùng Android Studio để tạo khoá tải lên và kho khoá.

Cách định cấu hình thủ công cho phần cấu hình ký tên cho loại bản dựng phát hành thông qua công cụ cấu hình bản dựng Gradle:

  1. Tạo một kho khoá. Kho khoá (keystore) là một tệp nhị phân chứa một tập hợp các khoá riêng tư. Bạn phải giữ kho khoá của mình ở một nơi an toàn và bảo mật.
  2. Tạo khoá riêng tư. Khoá riêng tư (private key) dùng để ký ứng dụng để phân phối và không bao giờ kèm theo trong ứng dụng hoặc tiết lộ trái phép cho các bên thứ ba.
  3. Thêm cấu hình ký tên vào tệp build.gradle cấp mô-đun:

    Groovy

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

    Kotlin

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

Lưu ý: Bạn không nên cung cấp mật khẩu trong khoá phát hành và kho khoá bên trong tệp bản dựng. Thay vào đó, hãy định cấu hình tệp bản dựng để lấy những mật khẩu này qua các biến môi trường hoặc yêu cầu quy trình tạo bản dựng nhắc bạn về những mật khẩu này.

Cách lấy những mật khẩu này qua các biến môi trường:

Groovy

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

Kotlin

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

Ngoài ra, bạn có thể tải kho khoá qua một tệp thuộc tính cục bộ. Vì lý do bảo mật, đừng thêm tệp này vào chế độ kiểm soát nguồn. Thay vào đó, hãy thiết lập chế độ kiểm soát nguồn cục bộ cho từng nhà phát triển. Để tìm hiểu thêm, hãy đọc nội dung Xoá thông tin ký khỏi tệp bản dựng.

Sau khi hoàn tất quy trình này, bạn có thể phân phối và phát hành ứng dụng lên Google Play.

Cảnh báo: Hãy giữ kho khoá và khoá riêng tư ở một nơi an toàn và bảo mật, đồng thời giữ bản sao lưu của kho khoá và khoá riêng tư này thật an toàn. Nếu bạn sử dụng tính năng Ký ứng dụng của Play và bị mất khoá tải lên, bạn có thể yêu cầu đặt lại khoá này thông qua Play Console. Nếu phát hành ứng dụng không có Tính năng ký ứng dụng của Play (dành cho các ứng dụng được tạo trước tháng 8 năm 2021) và mất khoá ký ứng dụng, bạn sẽ không thể phát hành bất kỳ bản cập nhật ứng dụng nào. Do vậy, bạn luôn phải ký tất cả phiên bản ứng dụng với cùng một khoá.

Ký ứng dụng trên Wear OS

Khi phát hành ứng dụng cho Wear OS, cả tệp APK cho đồng hồ và tệp APK cho điện thoại (không bắt buộc) đều cần được ký bằng cùng một khoá. Để biết thêm thông tin về việc đóng gói và ký ứng dụng trên Wear OS, hãy xem nội dung về Đóng gói và phân phối ứng dụng Wear.