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

Trang này dựa trên Thông tin tổng quan về bản dựng, giúp 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. Đồng thời, hướng dẫn bạn 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 khác nhau 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í 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 khác nhau hướng đến các thiết bị khác nhau, 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, 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). Mặc dù không trực tiếp định cấu hình các biến thể bản dựng, bạn có thể định cấu hình các loại bản dựng và các phiên bản sản phẩm tạo thành biến thể đó.

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 về tính năng và thiết bị khác nhau, 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 khác nhau, 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 quả sẽ 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 trong tệp build.gradle cấp mô-đun bên trong khối android. Khi tạo một mô-đun mới, Android Studio sẽ tự động tạo các loại bản dựng gỡ lỗi và phát hành. Mặc dù 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 chữ 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 applicationIdSuffix cho loại bản dựng gỡ lỗi và định cấu hình loại bản dựng "chạy thử" (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 allows you to 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 allows you to 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. Để đồng bộ dự án, bạn có thể nhấp vào Sync Now (Đồng bộ ngay) trên thanh thông báo xuất hiện khi bạn thay đổi hoặc Sync Project (Đồng bộ dự án) trên thanh công cụ. Nếu Android Studio phát hiện bất kỳ 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 có thể định cấu hình cho các loại bản dựng, bạn hãy tham khảo tài liệu về BuildType DSL.

Đị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 vào các tùy chọn 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 là vì thuộc tính defaultConfig này thực sự thuộc về lớp ProductFlavor. Điều này có nghĩa rằng 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 (application ID), hãy đọc bài viết về 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 gán tất cả phiên bản cho một nhóm phiên bản nào đó; nếu không, bạn sẽ thấy lỗi bản dựng xuất hiện như dưới đây. 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 đó.

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

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 cung cấp applicationIdSuffix versionNameSuffix riê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 ý: Với những ứng dụng cũ được phân phối bằng tệp APK trên Google Play (được tạo trước tháng 8 năm 2021), để phân phối ứng dụng bằng tính năng Hỗ trợ nhiều tệp APK trong Google Play, hãy gán 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 giá trị versionCode riêng. Để 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 gán giá trị applicationId khác nhau 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ộ 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:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

Bạn có thể thay đổi biến thể bản dựng thành bất kỳ biến thể nào tuỳ thích và chạy – chỉ cần chuyển đến phần Build (Tạo) > Select Build Variant (Chọn biến thể bản dựng) và chọn một biến thể trong trình đơn thả xuống. Tuy nhiê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 biết cách tạo và quản lý các nhóm tài nguyên.

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ẻ ứng dụng đó bằng mã nhận dạng ứng dụng được định nghĩa 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 các phiên bản ứng dụng khác nhau để xuất hiện dưới dạng 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, trong đó mỗi biến thể có một mã nhận dạng ứng dụng khác nhau. Trong trường hợp này, mỗi biến thể bản dựng phải được định nghĩa như 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ể định nghĩa lại thuộc tính applicationId hoặc thay vào đó bạn có thể bổ sung một phân đoạn (segment) vào mã nhận dạng ứng dụng mặc định thông qua applicationIdSuffix, như thể hiện 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" bây giờ sẽ 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ị. Với các ứng dụng cũ được phân phối bằng tệp APK trên Google Play (được tạo trước tháng 8 năm 2021), nếu 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 đó mỗi tệp APK hướng đến một cấu hình thiết bị riêng (chẳng hạn như 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 phải cung cấp cho mỗi tệp APK một versionCode riêng. Để biết thêm thông tin, hãy tham khảo bài viết về Hỗ trợ nhiều tệp APK. Việc xuất bản thông qua AAB sẽ không bị ảnh hưởng vì AAB sử dụng một cấu phần mềm duy nhất trong đó 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.

Lưu ý: Để đảm bảo khả năng tương thích với các công cụ SDK trước đó, nếu bạn không định nghĩa thuộc tính applicationId trong tệp build.gradle thì công cụ tạo bản dựng sẽ sử dụng tên gói trong tệp AndroidManifest.xml làm mã nhận dạng ứng dụng. Trong trường hợp đó, quá trình tái cấu trúc tên gói cũng sẽ thay đổi mã nhận dạng ứng dụng.

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 tham khảo phần nội dung Chèn các biến bản dựng (build variables) 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 khác nhau cho các phiên bản sản phẩm "full" và "demo" 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 cuối cùng. 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 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"
      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. 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 ?: 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 tích của số lượng phiên bản trong mỗi nhóm phiên bả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. Ví dụ với cấu hình bản dựng ở trên, 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ừ 2 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 tham khảo phần nội dung 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. Bạn có thể xoá một số cấu hình biến thể bản dựng nào đó bằng cách tạo một bộ lọc biến thể trong tệp build.gradle cấp mô-đun.

Sử dụng 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ạ 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.enabled = false
        }
    }
}
...

Sau khi thêm 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ộ 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 bạn đã chỉ định và các biến thể đó không còn xuất hiện trong trình đơn thả xuống khi bạn nhấp vào Build (Tạo) > Select Build Variant (Chọn biến thể bản dựng) từ thanh trình đơn (hoặc Build Variants (Biến thể bản dựng) trong 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 cho các khách hàng khác nhau hoặc chỉ bao gồm 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 giả định bạn sẽ sắp xếp các tệp và thư mục nhóm tài nguyên theo một phương pháp nhất định nào đó, tương tự như tập tài nguyên main/. Ví dụ: Gradle sẽ giả định bạn sẽ đặt các tệp lớp Java dành riêng cho loại bản dựng "debug" trong thư mục src/debug/java/.

Trình bổ trợ Android cho Gradle sẽ 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í 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: compile
build.gradle name: android.sourceSets.debug
Java sources: [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 các bước sau:

  1. Nhấp vào Gradle ở bên phải của cửa sổ IDE.
  2. Chuyển đến MyApplication > 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ộ bằng cách nhấp vào File (Tệp) > Settings (Cài đặt) > Experimental (Thử nghiệm) và 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ộ Gradle) (Android Studio > Preferences (Tuỳ chọn) > Experimental (Thử nghiệm) trên macOS). Sau khi Gradle thực thi tác vụ này, cửa sổ Run (Chạy) sẽ mở ra để hiển thị kết quả đầu ra.
  3. Nếu màn hình không ở chế độ văn bản như trình bày ở trên, hãy nhấp vào Toggle view (Chuyển đổi chế độ xem) ở phía bên trái của cửa sổ Run (Chạy).

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 kiểm thử cho ứng dụng, 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) và 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, bạn có thể yêu cầu Android Studio 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 cùng một ngăn Project (Dự án), 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. Từ trình đơn thả xuống bên cạnh Target Source Set (Nhóm tài nguyên đích), hãy chọn debug (gỡ lỗi).
  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 2.

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

Á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 get an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: You should avoid specifying a directory which 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']
      // You should 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("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 get an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs("other/res1", "other/res2")

    // Note: You should avoid specifying a directory which 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']
    // You should 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ả cá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ự liệt kê ở trê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 java/ đượ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.javasrc/main/Utility.java. Điều này là do Gradle xem xét cả hai thư mục này trong quá trình xây dựng và phát sinh lỗi "lớp trùng lặp". Nếu muốn có các phiên bản Utility.java khác nhau cho các loại bản dựng khác nhau, bạn có thể thiết lập sao cho mỗi loại bản dựng định nghĩa một phiên bản riêng của tệp này 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 ở trên. Có nghĩa 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 tham khảo bài viết về cách hợp nhất các tệp kê khai.
  • Tương tự như vậy, 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 ở trên. Điều này có nghĩa 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 hoặc nhiều nhóm tài nguyên, mức độ ưu tiên sẽ được sắp xếp theo cùng thứ tự như danh sách ở trên.
  • Cuối cùng, 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

Bạn có thể đị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ử bằng cách thêm tên của biến thể bản dựng hoặc nhóm tài nguyên kiểm thử vào trước từ khoá Implementation, như 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.0.2'
}

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

Để tìm hiểu thêm thông tin, hãy tham khảo phần nội dung Thêm các phần phụ thuộc của 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ể

Android plugin 3.0.0 trở lên bao gồm một cơ chế phụ thuộc mới, trong đó cơ chế này sẽ tự động so khớp các biến thể trong quá trình sử dụng thư viện. Điều này có nghĩa 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 cho các trường hợp không thể so khớp trực tiếp. Hãy xem xét liệu ứng dụng có định cấu hình một loại bản dựng có tên là "chạy thử" nhưng một trong những phần phụ thuộc thư viện của ứng dụng thì không chứa bản dựng này. Khi cố gắng tạo phiên bản "chạy thử" 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 tương tự 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 bao gồm 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. Hãy tham khảo bảng dưới đây để xác định xem bạn nên sử dụng thuộc tính DSL nào để xử lý một số lỗi bản dựng liên quan đến việc so khớp phần phụ thuộc có thể nhận biết biến thể.

Nguyên nhân gây ra lỗi bản dựng Cách khắc phục

Ứ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 "chạy thử" nhưng phần phụ thuộc chỉ có loại bản dựng "gỡ lỗi" và "phát hành".

Lưu ý rằng sẽ không có vấn đề gì xảy ra khi phần phụ thuộc thư viện có chứa loại bản dựng mà ứng dụng không có. Lý do đơn giản 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ạ bên dưới:

Groovy


// In the app's build.gradle file.
android {
    buildTypes {
        debug {}
        release {}
        staging {
            // Specifies a sorted list of fallback build types that the
            // plugin should 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 file.
android {
    buildTypes {
        getByName("debug") {}
        getByName("release") {}
        create("staging") {
            // Specifies a sorted list of fallback build types that the
            // plugin should 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ụ thuộc, ứng dụng sẽ bao gồm 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 đơn giản 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" của ứng dụng, thể hiện như dưới đây:

Groovy


// In the app's build.gradle file.
android {
    defaultConfig{
    // Do not configure matchingFallbacks in the defaultConfig block.
    // Instead, you must 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
            // should try to use when a dependency's matching dimension does
            // not include a "free" flavor. You may specify as many
            // fallbacks as you like, and 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 file.
android {
    defaultConfig{
    // Do not configure matchingFallbacks in the defaultConfig block.
    // Instead, you must 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
            // should try to use when a dependency's matching dimension does
            // not include a "free" flavor. You may specify as many
            // fallbacks as you like, and 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". Vì vậy, 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 mà trình bổ trợ phải chọn trong mỗi nhóm bị thiếu, như minh hoạ trong ví dụ dưới đây. 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 should try to use from
    // a given dimension. The following tells the plugin that, when encountering
    // a dependency that includes a "minApi" dimension, it should select the
    // "minApi18" flavor. You can include additional flavor names to provide a
    // sorted list of fallbacks for the dimension.
    missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
    // You should 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 file.
android {
    defaultConfig{
    // Specifies a sorted list of flavors that the plugin should try to use from
    // a given dimension. The following tells the plugin that, when encountering
    // a dependency that includes a "minApi" dimension, it should select the
    // "minApi18" flavor. You can include additional flavor names to provide a
    // sorted list of fallbacks for the dimension.
    missingDimensionStrategy("minApi", "minApi18", "minApi23")
    // You should 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 tham khảo phần nội dung matchingFallbacksmissingdimensionStrategy trong tài liệu tham khảo về Android Plugin DSL.

Định cấu hình 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 (signing configuration) cho bản dựng này. Nếu bạn chưa có khoá ký (signing key), hãy tham khảo cách tạo khoá tải lên và kho khoá bằng Android Studio.

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 kèm mật khẩu trong khoá phát hành và kho khoá bên trong tệp bản dựng. Ngoài ra, bạn có thể định cấu hình tệp bản dựng để lấy những mật khẩu này từ 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 từ 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")

Cách để 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 khi bạn gọi bản dựng từ dòng lệnh:

Groovy

storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLine("\nKey password: ")

Kotlin

storePassword = System.console().readLine("\nKeystore password: ")
keyPassword = System.console().readLine("\nKey password: ")

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 trê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 đảm bảo cất giữ an toàn bản sao lưu của kho khoá và khoá riêng tư này. 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 tuỳ chọn đều cần được ký. Để biết thêm thông tin về việc đóng gói và ký ứng dụng trên Wear OS, hãy tham khảo phần nội dung về Đóng gói và phân phối ứng dụng Wear.