Xây dựng nhiều APK

Lưu ý: Kể từ tháng 8 năm 2021, tất cả các tính năng mới ứng dụng phải được xuất bản dưới dạng Gói ứng dụng. Nếu bạn xuất bản ứng dụng lên Google Play, tạo và tải tệp Android App Bundle lên. Thời gian bạn làm như vậy, Google Play sẽ tự động tạo và phân phối các APK được tối ưu hoá cho cấu hình thiết bị của từng người dùng, để họ chỉ tải mã và tài nguyên xuống họ cần để chạy ứng dụng của bạn. Việc phát hành 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 tệp APK duy nhất để hỗ trợ tất cả thiết bị mục tiêu bất cứ khi nào có thể, điều đó có thể dẫn đến một APK rất lớn do các tệp hỗ trợ nhiều mật độ màn hình hoặc Tệp nhị phân của ứng dụng Giao diện (ABI). Một cách để giảm kích thước tệp APK là tạo nhiều APK chứa các tệp cho mật độ màn hình hoặc 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 mật độ hoặc ABI. Trang này mô tả cách định cấu hình bản dựng để tạo nhiều APK. Nếu bạn cần tạo các phiên bản ứng dụng khác nhau không dựa trên mật độ màn hình hoặc ABI, hãy sử dụng 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, cung cấp một khối density chỉ định cách bạn muốn Gradle tạo APK cho mỗi mật độ hoặc một khối abi chỉ định cách bạn muốn Gradle để tạo APK theo ABI. Bạn có thể cung cấp cả khối mật độ và ABI (Giao diện nhị phân ứng dụng), cũng như hệ thống xây dựng sẽ tạo một APK cho mỗi tổ hợp mật độ và ABI (Giao diện nhị phân ứng dụng).

Định cấu hình nhiều APK cho mật độ màn hình

Để tạo các APK riêng biệt cho các mật độ màn hình khác nhau, hãy thêm một Khối density bên trong Khối splits. Trong khối density, hãy cung cấp một danh sách mật độ màn hình mong muốn và kích thước màn hình tương thích. Chỉ sử dụng danh sách nếu bạn cần thông tin cụ thể Phần tử <compatible-screens> trong tệp kê khai của mỗi tệp APK.

Các tùy chọn DSL Gradle sau đây được dùng để định cấu hình nhiều APK cho các mật độ màn hình:

enable cho Groovy, isEnable cho 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 mật độ màn hình mà bạn xác định. Giá trị mặc định là false.
exclude
Chỉ định một danh sách mật độ đượ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 mật độ nhưng cần loại trừ một vài APK mà ứng dụng của bạn không hỗ trợ.
reset()

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

Đoạn mã sau đây đặt danh sách mật độ thành chỉ ldpixxhdpi bằng cách gọi reset() để xoá danh sách, sau đó dùng include:

reset()                  // Clears the default list from all densities
                         // to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities to generate APKs
                         // for.
include
Chỉ định một danh sách mật độ được phân tách bằng dấu phẩy mà bạn muốn Gradle tạo APK cho. Chỉ sử dụng kết hợp với reset() để chỉ định một danh sách mật độ chính xác.
compatibleScreens

Chỉ định danh sách kích thước màn hình tương thích được phân tách bằng dấu phẩy. Công cụ này sẽ chèn phù hợp Nút <compatible-screens> trong tệp kê khai cho từng tệp APK.

Cài đặt này mang lại một cách thuận tiện để quản lý cả hai màn hình mật độ và kích thước màn hình trong cùng một phần build.gradle. Tuy nhiên, việc sử dụng <compatible-screens> có thể giới hạn các loại thiết bị ứng dụng của bạn hoạt động. Để biết những cách khác để hỗ trợ các kích thước màn hình, hãy xem tổng quan về khả năng tương thích với màn hình.

Vì mỗi APK dựa trên mật độ màn hình bao gồm Thẻ <compatible-screens> có các quy định hạn chế cụ thể về các loại màn hình mà APK hỗ trợ, ngay cả khi bạn xuất bản một vài APK—một số thiết bị mới không phù hợp với nhiều bộ lọc APK của bạn. Do vậy, Gradle luôn tạo một APK chung bổ sung để chứa các tài sản cho tất cả các loại mật độ màn hình và không bao gồm Thẻ <compatible-screens>. Xuất bản nội dung này APK chung cùng với APK theo mỗi mật độ của bạn để cung cấp tính năng dự phòng cho không khớp với APK có Thẻ <compatible-screens>.

Ví dụ sau đây tạo ra một tệp APK riêng cho mỗi phiên bản màn hình mật độ ngoại trừ ldpi, xxhdpixxxhdpi. Bạn có thể thực hiện việc này bằng cách sử dụng exclude để xoá ba mật độ đó từ danh sách mặc định của tất cả các mật độ.

Groovy

android {
  ...
  splits {

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

      // Configures multiple APKs based on screen density.
      enable true

      // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
      exclude "ldpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings for the manifest.
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

Kotlin

android {
    ...
    splits {

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

            // Configures multiple APKs based on screen density.
            isEnable = true

            // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
            exclude("ldpi", "xxhdpi", "xxxhdpi")

            // Specifies a list of compatible screen size settings for the manifest.
            compatibleScreens("small", "normal", "large", "xlarge")
        }
    }
}

Để biết thêm thông tin về cách tuỳ chỉnh bản dựng khác các biến thể của ứng dụng theo loại màn hình và thiết bị cụ thể, hãy xem Khai báo bị hạn chế màn hình.

Đị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 danh sách ABI mong muốn.

Các tuỳ chọn DSL Gradle sau đây được dùng để định cấu hình nhiều APK cho mỗi ABI (Giao diện nhị phân ứng dụng):

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 loại trừ của Google.
reset()

Xoá danh sách ABI mặc định. Chỉ sử dụng khi được 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 sẽ đặt danh sách ABI thành chỉ x86x86_64 bằng cách gọi reset() để xoá danh sách, và sau đó 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 cho. Chỉ sử dụng kết hợp với reset() để xác định giá trị chính xác danh sách ABI.
universalApk cho Groovy hoặc isUniversalApk cho Tập lệnh Kotlin

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

Lưu ý rằng tuỳ chọn này chỉ có sẵn trong khối splits.abi. Khi xây dựng nhiều APK dựa trên mật độ màn hình, Gradle luôn tạo một APK chung mà chứa mã và tài nguyên cho tất cả các mật độ màn hình.

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 bằng danh sách ABI trống, theo sau là include danh sách ABI mà 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 hỗ trợ ABI.

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 điều khiển Biến thể bản dựng có hai giá trị cột: Module (Mô-đun) và Active Build Build Build Build (Bản dựng đang hoạt động) Biến thể, như trong hình 1.


Bảng điều khiển Biến thể bản dựng Hình 1. Bảng điều khiển Biến thể bản dựng có 2 cột cho 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) cho thuộc tính mô-đun sẽ 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. Để 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) của một mô-đun rồi chọn biến thể bạn 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 điều khiển Biến thể bản dựng có 3 cột: Module (Mô-đun), Active Build (Bản dựng đang hoạt động) Biến thểABI đang hoạt động, như minh hoạ trong hình 2.

Hình 2. Bảng điều khiển 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ị 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à 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 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 Active Build Variant (Biến thể bản dựng đang hoạt động) hoặc cột Active ABI (ABI đang hoạt động).
  2. Chọn biến thể hoặc ABI mong muốn trong danh sách . Quá trình đồng bộ mới sẽ tự động chạy.

Việc thay đổi cột cho một ứng dụng hoặc mô-đun thư viện sẽ áp dụng thay đổi đó 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 thông tin phiên bản, như được chỉ định ở cấp mô-đun Tệp build.gradle hoặc build.gradle.kts. Vì Cửa hàng Google Play không cho phép nhiều APK cho cùng một ứng dụng mà tất cả đều phải cùng một thông tin phiên bản, bạn cần đảm bảo rằng mỗi APK có một versionCode 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 chỉ định một giá trị số duy nhất cho mỗi ABI và mật độ 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 defaultConfig hoặc Khối productFlavors có giá trị số được gán cho hoặc ABI (Giao diện nhị phân ứng dụng).

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

Việc chỉ định mã phiên bản theo mức tăng 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 cần cập nhật ứng dụng. Cho ví dụ: nếu defaultConfig.versionCode lặp đến 5 trong một cập nhật tiếp theo, Gradle sẽ chỉ định một versionCode của năm 2005 cho APK x86 và 3005 đến APK x86_64.

Mẹo: Nếu bản dựng của bạn chứa một tệp APK chung, hãy chỉ định tệp APK đó versionCode thấp hơn so với bất kỳ APK nào khác của bạn. 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 đè phiên bản APK chung versionCode mặc định.

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]

// For per-density APKs, create a similar map:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 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)

// For per-density APKs, create a similar map:
// val densityCodes = mapOf("mdpi" to 1, "hdpi" to 2, "xhdpi" 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 định cấu hình build.gradle hoặc build.gradle.kts để tạo nhiều APK, hãy nhấp vào Bản dựng > Tạo APK để tạo tất cả APK cho giai đoạn đã chọn trong ngăn Project (Dự án). Gradle tạo các APK cho từng mật độ hoặc ABI trong build/outputs/apk/ của dự án thư mục.

Gradle sẽ xây dựng một APK cho mỗi mật độ hoặc ABI mà bạn định cấu hình nhiều APK. Nếu bạn cho phép nhiều APK cho cả mật độ và ABI, Gradle sẽ tạo một APK cho mỗi tổ hợp mật độ và ABI.

Ví dụ: như sau Đoạn mã build.gradle cho phép tạo nhiều APK cho mdpi và Mật độ hdpi cũng như các ABI x86x86_64:

Groovy

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

Kotlin

...
  splits {
    density {
      isEnable = true
      reset()
      include("mdpi", "hdpi")
    }
    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-hdpiX86-release.apk: Chứa mã và tài nguyên cho Mật độ hdpi và ABI x86.
  • app-hdpiX86_64-release.apk: Chứa mã và tài nguyên cho Mật độ hdpi và ABI x86_64.
  • app-mdpiX86-release.apk: Chứa mã và tài nguyên cho Mật độ mdpi và ABI x86.
  • app-mdpiX86_64-release.apk: Chứa mã và tài nguyên cho Mật độ mdpi và ABI x86_64.

Khi xây dựng nhiều APK dựa trên mật độ màn hình, Gradle luôn tạo một APK chung, bao gồm mã và tài nguyên cho tất cả các mật độ, ngoài các APK theo mật độ.

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 phần Khối splits.abi trong tệp build.gradle của bạn (đối với Groovy) hoặc isUniversalApk = true trong Khối splits.abi trong tệp build.gradle.kts của bạn (đối với tập lệnh Kotlin).

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

Khi xây dựng nhiều APK, Gradle sẽ tạo tên tệp APK bằng cách sử dụng các công cụ sau lược đồ:

modulename-screendensityABI-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.
screendensity
Nếu bạn bật nhiều APK cho mật độ màn hình, hãy chỉ định màn hình cho APK, chẳng hạn như mdpi.
ABI

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

Nếu bạn bật nhiều APK cho cả mật độ màn hình và ABI (Giao diện nhị phân ứng dụng), Gradle nối tên mật độ với tên ABI, ví dụ: mdpiX86. Nếu bạn bật universalApk cho mỗi ABI APK, Gradle sử dụng universal làm phần ABI của APK chung tên tệp.

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

Ví dụ: khi tạo APK mật độ màn hình mdpi cho phiên bản gỡ lỗi của myApp, tên tệp APK là myApp-mdpi-debug.apk Bản phát hành của myApp được định cấu hình để xây dựng nhiều APK cho cả Mật độ màn hình mdpi và ABI x86 có tên tệp APK là myApp-mdpiX86-release.apk.