Xây dựng nhiều APK

Lưu ý: Kể từ tháng 8 năm 2021, tất cả ứng dụng mới đều phải được xuất bản dưới dạng App Bundle. Nếu bạn phát hành ứng dụng lên Google Play, hãy tạo và tải một Android App Bundle lên. Khi bạn làm như vậy, Google Play sẽ tự động tạo và phân phát các APK được tối ưu hoá cho từng cấu hình thiết bị của người dùng, nhờ đó, họ chỉ tải mã và tài nguyên cần thiết để chạy ứng dụng của bạn. Việc xuất bản nhiều APK sẽ hữu ích nếu bạn xuất bản lên một cửa hàng không hỗ trợ định dạng AAB. Trong trường hợp đó, bạn phải tự xây dựng, ký và quản lý từng tệp APK.

Mặc dù bạn nên tạo một APK duy nhất để hỗ trợ tất cả các thiết bị mục tiêu bất cứ khi nào có thể, nhưng điều đó có thể dẫn đến kích thước APK rất lớn do tệp đó hỗ trợ nhiều Giao diện nhị phân của ứng dụng (ABI). Một cách để giảm kích thước APK là tạo nhiều APK chứa các tệp cho ABI cụ thể.

Gradle có thể tạo nhiều APK riêng biệt chỉ chứa mã và tài nguyên cụ thể cho từng ABI. Trang này mô tả cách định cấu hình bản dựng để tạo nhiều APK. Nếu cần tạo các phiên bản ứng dụng khác nhau không dựa trên ABI, hãy sử dụng các biến thể bản dựng.

Định cấu hình bản dựng cho nhiều APK

Để định cấu hình bản dựng cho nhiều APK, hãy thêm khối splits vào tệp build.gradle cấp mô-đun. Trong khối splits, hãy cung cấp một khối abi chỉ định cách bạn muốn Gradle tạo APK theo ABI.

Định cấu hình nhiều APK cho ABI

Để tạo các APK riêng cho từng ABI, hãy thêm một khối abi bên trong khối splits của bạn. Trong khối abi, hãy cung cấp một danh sách các ABI mong muốn.

Các lựa chọn DSL Gradle sau đây được dùng để định cấu hình nhiều APK theo ABI:

enable đối với Groovy hoặc isEnable đối với tập lệnh Kotlin
Nếu bạn đặt phần tử này thành true, Gradle sẽ tạo nhiều APK dựa trên ABI mà bạn xác định. Giá trị mặc định là false.
exclude
Chỉ định một danh sách ABI được phân tách bằng dấu phẩy mà bạn không muốn Gradle tạo các APK riêng biệt. Sử dụng exclude nếu bạn muốn tạo APK cho hầu hết các ABI nhưng cần loại trừ một vài ABI mà ứng dụng của bạn không hỗ trợ.
reset()

Xoá danh sách ABI mặc định. Chỉ sử dụng khi kết hợp với phần tử include để chỉ định ABI mà bạn muốn thêm.

Đoạn mã sau đây đặt danh sách ABI thành chỉ x86x86_64 bằng cách gọi reset() để xoá danh sách, rồi sử dụng include:

reset()                 // Clears the default list from all ABIs to no ABIs.
include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.
include
Chỉ định danh sách ABI được phân tách bằng dấu phẩy mà bạn muốn Gradle tạo APK. Chỉ sử dụng kết hợp với reset() để chỉ định danh sách ABI chính xác.
universalApk đối với Groovy hoặc isUniversalApk đối với tập lệnh Kotlin

Nếu true, Gradle sẽ tạo một APK chung ngoài các APK theo ABI. Một APK chung chứa mã và tài nguyên cho tất cả ABI trong một APK duy nhất. Giá trị mặc định là false.

Ví dụ sau đây sẽ tạo ra một APK riêng cho mỗi ABI: x86x86_64. Bạn có thể thực hiện việc này bằng cách sử dụng reset() để bắt đầu với danh sách ABI trống, theo sau là include kèm theo danh sách các ABI, trong đó mỗi ABI sẽ nhận được một APK.

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include "x86", "x86_64"

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      isEnable = true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include("x86", "x86_64")

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      isUniversalApk = false
    }
  }
}

Để biết danh sách các ABI được hỗ trợ, hãy xem Các ABI được hỗ trợ.

Dự án không có mã gốc/C++

Đối với các dự án không có mã gốc/C++, bảng Build Variants (Biến thể bản dựng) có 2 cột: Module (Mô-đun) và Active Build Variant (Biến thể bản dựng đang hoạt động), như minh hoạ trong hình 1.

Bảng điều khiển Biến thể bản dựng
Hình 1. Bảng Build Variants (Biến thể bản dựng) có 2 cột đối với các dự án không có mã gốc/C++.

Giá trị Active Build Variant (Biến thể bản dựng đang hoạt động) của mô-đun xác định biến thể bản dựng được triển khai và xuất hiện trong trình chỉnh sửa. Để chuyển đổi giữa các biến thể, hãy nhấp vào ô Active Build Variant (Biến thể bản dựng đang hoạt động) cho một mô-đun và chọn biến thể mong muốn trong trường danh sách.

Dự án có mã gốc/C++

Đối với các dự án có mã gốc/C++, bảng Build Variants (Biến thể bản dựng) có 3 cột: Module (Mô-đun), Active Build Variant (Biến thể bản dựng đang hoạt động) và Active ABI (ABI đang hoạt động), như minh hoạ trong hình 2.

Hình 2. Bảng Build Variants (Biến thể bản dựng) sẽ thêm cột Active ABI (ABI đang hoạt động) cho các dự án có mã gốc/C++.

Giá trị Biến thể bản dựng đang hoạt động cho mô-đun giúp xác định biến thể bản dựng được triển khai và hiển thị trong trình chỉnh sửa. Đối với các mô-đun gốc, giá trị Active ABI (ABI đang hoạt động) xác định ABI mà trình chỉnh sửa sử dụng, nhưng không ảnh hưởng đến nội dung được triển khai.

Cách thay đổi loại bản dựng hoặc ABI:

  1. Nhấp vào ô cho cột Active Build Variant (Biến thể bản dựng đang hoạt động) hoặc Active ABI (ABI đang hoạt động).
  2. Chọn biến thể hoặc ABI mà bạn muốn trong trường danh sách. Quá trình đồng bộ mới sẽ tự động chạy.

Khi một ứng dụng hoặc mô-đun thư viện có thay đổi thì thay đổi đó sẽ áp dụng cho tất cả các hàng phụ thuộc.

Định cấu hình phiên bản

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

Bạn có thể định cấu hình tệp build.gradle cấp mô-đun để ghi đè versionCode cho từng APK. Bằng cách tạo một mối liên kết sẽ chỉ định một giá trị số duy nhất cho mỗi ABI mà bạn định cấu hình nhiều APK, bạn có thể ghi đè mã phiên bản đầu ra bằng một giá trị kết hợp mã phiên bản được xác định trong khối defaultConfig hoặc productFlavors với giá trị số được chỉ định cho ABI.

Trong ví dụ sau, APK cho ABI x86 sẽ nhận được versionCode là 2004 và ABI x86_64 sẽ nhận được versionCode là 3004.

Việc chỉ định mã phiên bản theo gia số lớn, chẳng hạn như 1000, cho phép bạn chỉ định mã phiên bản duy nhất sau này nếu bạn cần cập nhật ứng dụng. Ví dụ: nếu defaultConfig.versionCode lặp lại đến 5 trong lần cập nhật tiếp theo, Gradle sẽ chỉ định versionCode là 2005 cho APK x86 và 3005 cho APK x86_64.

Lưu ý: Nếu bản dựng của bạn chứa một APK chung, hãy chỉ định cho APK đó một versionCode thấp hơn so với bất kỳ APK nào khác. Do Cửa hàng Google Play cài đặt phiên bản ứng dụng vừa tương thích với thiết bị mục tiêu vừa có versionCode cao nhất, nên việc chỉ định versionCode thấp hơn cho APK chung sẽ đảm bảo Cửa hàng Google Play có thể cài đặt một trong các APK của bạn trước khi quay lại APK chung. Mã mẫu sau đây xử lý vấn đề này bằng cách không ghi đè versionCode mặc định của một APK chung.

Groovy

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

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

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code doesn't override the version code for universal APKs.
    // However, because you want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the
      // version code for only the output APK, not for the variant itself. Skipping
      // this step causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Kotlin

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

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

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code doesn't override the version code for universal APKs.
            // However, because you want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

Để biết thêm ví dụ về các lược đồ mã phiên bản thay thế, hãy xem phần Chỉ định mã phiên bản.

Xây dựng nhiều APK

Sau khi bạn định cấu hình tệp build.gradle hoặc build.gradle.kts cấp mô-đun để xây dựng nhiều APK, hãy nhấp vào Build > Build APK (Xây dựng > Xây dựng APK) để xây dựng tất cả APK cho mô-đun hiện được chọn trong ngăn Project (Dự án). Gradle sẽ tạo các APK cho mỗi ABI trong thư mục build/outputs/apk/ của dự án.

Gradle sẽ tạo một APK cho mỗi ABI mà bạn định cấu hình nhiều APK.

Ví dụ: đoạn mã build.gradle sau đây cho phép tạo nhiều APK cho các ABI x86x86_64:

Groovy

...
  splits {
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

Kotlin

...
  splits {
    abi {
      isEnable = true
      reset()
      include("x86", "x86_64")
    }
  }

Kết quả từ cấu hình mẫu bao gồm 4 APK sau:

  • app-X86-release.apk: Chứa mã và tài nguyên cho ABI x86.
  • app-X86_64-release.apk: Chứa mã và tài nguyên cho ABI x86_64.

Khi xây dựng nhiều APK dựa trên ABI, Gradle chỉ tạo một APK chứa mã và tài nguyên cho tất cả ABI nếu bạn chỉ định universalApk true trong khối splits.abi trong tệp build.gradle (đối với Groovy) hoặc isUniversalApk = true trong khối splits.abi trong tệp build.gradle.kts (đối với tập lệnh Kotlin).

Định dạng tên tệp APK

Khi tạo nhiều APK, Gradle sẽ tạo tên tệp APK theo lược đồ sau:

modulename-ABI-buildvariant.apk

Thành phần của lược đồ này bao gồm:

modulename
Chỉ định tên mô-đun đang được xây dựng.
ABI

Nếu bạn đã bật nhiều APK cho ABI, hãy chỉ định ABI cho APK, chẳng hạn như x86.

buildvariant
Chỉ định biến thể bản dựng đang được xây dựng, chẳng hạn như debug.