Hỗ trợ kiến trúc 64 bit

Từ ngày 1 tháng 8 năm 2019, ứng dụng được phát hành trên Google Play sẽ phải hỗ trợ kiến trúc 64 bit. CPU 64 bit mang lại trải nghiệm nhanh hơn và phong phú hơn cho người dùng. Việc thêm phiên bản 64 bit của ứng dụng sẽ giúp cải thiện hiệu năng, giúp bạn đổi mới sau này và thiết lập cho các thiết bị chỉ có phần cứng 64 bit.

Hướng dẫn này giải thích các bước bạn có thể thực hiện ngay hôm nay để đảm bảo rằng ứng dụng 32 bit của mình đã sẵn sàng hỗ trợ thiết bị 64 bit.

Đánh giá ứng dụng

Nếu ứng dụng của bạn chỉ sử dụng mã được viết bằng ngôn ngữ lập trình Java hoặc Kotlin (bao gồm cả toàn bộ thư viện hoặc SDK), thì ứng dụng đó đã sẵn sàng cho thiết bị 64 bit. Nếu ứng dụng có sử dụng một mã gốc nào đó, hoặc bạn không chắc chắn đó phải mã gốc hay không, bạn sẽ phải đánh giá ứng dụng và hành động.

Kiểm tra trạng thái nhanh

Có một cách nhanh chóng để kiểm tra xem ứng dụng đã sẵn sàng cho yêu cầu đối với phiên bản 64 bit hay chưa, đó là truy cập vào Play Console rồi xem các bản phát hành hiện có để xem các bản phát hành này có tuân thủ hay không:

Play Console cũng sẽ cho thấy các cảnh báo đối với bản phát hành nháp nếu có vấn đề nào liên quan đến yêu cầu về phiên bản 64 bit. Sau đây là một ví dụ:

Nếu bạn thấy cảnh báo, hãy làm theo các bước dưới đây để chuẩn bị ứng dụng.

Ứng dụng có sử dụng mã gốc không?

Điều đầu tiên cần làm là kiểm tra xem ứng dụng có sử dụng mã gốc nào hay không. Ứng dụng được coi là sử dụng mã gốc nếu:

  • Có sử dụng mã C/C++ (gốc) bất kỳ trong ứng dụng.
  • Liên kết với thư viện gốc của bên thứ ba.
  • Do một trình tạo ứng dụng bên thứ ba sử dụng các thư viện gốc tạo ra.

Ứng dụng có bao gồm thư viện 64 bit không?

Cách đơn giản nhất để kiểm tra thư viện 64 bit là kiểm tra cấu trúc của tệp APK. Khi được tạo, tệp APK sẽ được đóng gói với thư viện gốc bất kỳ mà ứng dụng cần. Thư viện gốc được lưu trữ trong nhiều thư mục dựa trên ABI. Không bắt buộc phải hỗ trợ mọi kiến trúc 64 bit, nhưng đối với mỗi kiến trúc 32 bit gốc mà bạn hỗ trợ, bạn phải đưa vào kiến trúc 64 bit tương ứng.

Đối với kiến trúc ARM, thư viện phiên bản 32 bit được đặt tại armeabi-v7a. Thư viện tương ứng cho phiên bản 64 bit là arm64-v8a.

Đối với kiến trúc x86, hãy tìm từ khoá x86 cho 32 bit và x86_64 cho 64 bit.

Điều đầu tiên cần làm là đảm bảo rằng bạn có thư viện gốc trong cả hai thư mục này. Tóm lại:

Nền tảng Thư mục thư viện 32 bit Thư mục thư viện 64 bit
ARM lib/armeabi-v7a lib/arm64-v8a
x86 lib/x86 lib/x86_64

Hãy lưu ý rằng tuỳ thuộc vào ứng dụng , có thể có hoặc không có cùng một bộ thư viện trong mỗi thư mục. Mục tiêu là để đảm bảo rằng ứng dụng chạy chính xác trong môi trường 64 bit.

Trong trường hợp thông thường, một tệp APK hoặc gói được tạo cho cả kiến trúc 32 bit và 64 bit sẽ có các thư mục cho cả hai ABI, mỗi ABI có một bộ thư viện gốc tương ứng. Nếu không hỗ trợ phiên bản 64 bit, thì có thể bạn sẽ thấy thư mục ABI 32 bit chứ không phải thư mục 64 bit.

Tìm thư viện gốc bằng Công cụ phân tích APK

Công cụ phân tích APK (APK Analyzer) là một công cụ cho phép bạn đánh giá nhiều khía cạnh của tệp APK đã xây dựng. Trong trường hợp của chúng ta, công cụ này dùng để tìm mọi thư viện gốc và đảm bảo có thư viện 64 bit.

  1. Mở Android Studio rồi mở một dự án bất kỳ.
  2. Trên trình đơn, hãy chọn Build (Xây dựng) > Analyze APK (Phân tích tệp APK)

    chạy công cụ phân tích APK

  3. Chọn tệp APK mà bạn muốn đánh giá.

  4. Tìm trong thư mục lib. Đây là nơi bạn sẽ tìm thấy các tệp ".so". Nếu không tìm thấy tệp ".so" nào trong ứng dụng, thì tức là ứng dụng đã sẵn sàng và bạn không cần làm gì thêm. Nếu bạn thấy armeabi-v7a hoặc x86, thì tức là bạn có thư viện 32 bit.

  5. Hãy kiểm tra xem bạn có các tệp ".so" tương tự trong thư mục arm64-v8a hoặc x86_64 hay không.

    chạy công cụ phân tích APK

  6. Nếu không có thư viện arm64-v8a hoặc x86_64 nào, bạn cần cập nhật quy trình tạo bản dựng để bắt đầu xây dựng và đóng gói các cấu phần phần mềm đó trong tệp APK.

  7. Nếu thấy cả hai thư viện cùng được đóng gói, bạn có thể bỏ qua bước này để kiểm tra ứng dụng trên phần cứng 64 bit.

Tìm kiếm thư viện gốc bằng cách giải nén tệp APK

Tệp APK có cấu trúc như tệp zip và cũng có thể được giải nén như tệp zip. Bất kể bạn sử dụng dòng lệnh hay công cụ trích xuất nào khác, việc giải nén tệp APK sẽ được thực hiện.

Bạn chỉ cần giải nén tệp APK (tuỳ thuộc vào công cụ trích xuất, có thể bạn sẽ phải đổi tên tệp thành .zip) rồi duyệt qua các tệp được giải nén theo hướng dẫn ở trên để tìm hiểu xem bạn đã sẵn sàng cho thiết bị 64-bit hay chưa.

Ví dụ: bạn có thể chạy lệnh sau đây qua dòng lệnh:

:: Command Line
> zipinfo -1 YOUR_APK_FILE.apk | grep \.so$
lib/armeabi-v7a/libmain.so
lib/armeabi-v7a/libmono.so
lib/armeabi-v7a/libunity.so
lib/arm64-v8a/libmain.so
lib/arm64-v8a/libmono.so
lib/arm64-v8a/libunity.so

Lưu ý trong ví dụ này: sự hiện diện của các thư viện armeabi-v7aarm64-v8a có nghĩa là ứng dụng sẽ hỗ trợ cấu trúc 64 bit.

Xây dựng ứng dụng bằng thư viện 64 bit

Sau đây là hướng dẫn cách xây dựng thư viện 64 bit. Tuy nhiên, bạn cần hiểu rằng phần này sẽ chỉ bao gồm mã bản dựng và các thư viện mà bạn có thể tạo từ nguồn.

Nếu bạn đang sử dụng SDK hoặc thư viện bên ngoài nào đó, hãy đảm bảo rằng bạn đang sử dụng phiên bản 64 bit bằng cách làm theo các bước ở trên. Hãy liên hệ với chủ sở hữu của SDK hoặc thư viện nếu chưa có phiên bản 64 bit và lưu ý điều này khi lập kế hoạch hỗ trợ cho thiết bị 64 bit.

Xây dựng bằng Android Studio hoặc Gradle

Hầu hết dự án Android Studio đều sử dụng Gradle làm hệ thống tạo bản dựng cơ bản. Vì vậy, phần này áp dụng cho cả hai trường hợp. Việc bật bản dựng cho mã gốc cũng đơn giản như việc thêm arm64-v8a và/hoặc x86_64, tuỳ thuộc vào (các) kiến trúc bạn muốn hỗ trợ đối với chế độ cài đặt ndk.abiFilters trong tệp "build.gradle" của ứng dụng:

Groovy

// Your app's build.gradle
plugins {
  id 'com.android.app'
}

android {
   compileSdkVersion 27
   defaultConfig {
       appId "com.google.example.64bit"
       minSdkVersion 15
       targetSdkVersion 28
       versionCode 1
       versionName "1.0"
       ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
// ...

Kotlin

// Your app's build.gradle
plugins {
    id("com.android.app")
}

android {
    compileSdkVersion(27)
    defaultConfig {
        appId = "com.google.example.64bit"
        minSdkVersion(15)
        targetSdkVersion(28)
        versionCode = 1
        versionName = "1.0"
        ndk {
            abiFilters += listOf("armeabi-v7a","arm64-v8a","x86","x86_64")
        }
// ...

Xây dựng bằng CMake

Nếu ứng dụng được xây dựng bằng CMake, bạn có thể tạo bản dựng cho ABI 64 bit bằng cách chuyển arm64-v8a vào tham số "-DANDROID_ABI":

:: Command Line
> cmake -DANDROID_ABI=arm64-v8a … or
> cmake -DANDROID_ABI=x86_64 …

Tuỳ chọn này không có hiệu lực khi sử dụng externalNativeBuild. Xem phần Xây dựng bằng Gradle.

Xây dựng bằng ndk-build

Nếu ứng dụng được xây dựng bằngndkbuild, bạn có thể tạo cho ABI 64 bit bằng cách sửa đổi tệp "Application.mk" bằng cách sử dụng biến APP_ABI:

APP_ABI := armeabi-v7a arm64-v8a x86 x86_64

Tuỳ chọn này không có hiệu lực khi sử dụng externalNativeBuild. Xem phần Xây dựng bằng Gradle.

Chuyển đổi 32 bit sang 64 bit

Nếu mã của bạn đã chạy trên máy tính hoặc iOS, bạn không cần làm gì thêm cho Android. Nếu đây là lần đầu tiên mã được xây dựng cho một hệ thống 64 bit, thì vấn đề chính bạn cần giải quyết là con trỏ (pointer) không còn phù hợp với các kiểu dữ liệu (type) số nguyên (integer) 32 bit như int. Bạn sẽ cần cập nhật mã lưu trữ các con trỏ trong những kiểu dữ liệu như int, unsigned hoặc uint32_t. Trên các hệ thống Unix, long khớp với kích thước con trỏ, nhưng điều này không đúng trên Windows, vì vậy, bạn nên sử dụng các kiểu dữ liệu rõ nghĩa (intention-revealing) uintptr_t hoặc intptr_t. Hãy sử dụng kiểu ptrdiff_t để lưu trữ sự khác biệt giữa hai con trỏ.

Bạn nên luôn ưu tiên các kiểu số nguyên có chiều rộng cố định cụ thể được xác định trong <stdint.h> thay vì các kiểu truyền thống như int hoặc long, ngay cả đối với các kiểu không phải con trỏ.

Sử dụng cờ sau của trình biên dịch để phát hiện các trường hợp mã đang chuyển đổi không chính xác giữa các con trỏ và số nguyên:

-Werror=pointer-to-int-cast
-Werror=int-to-pointer-cast
-Werror=shorten-64-to-32

Lớp Java có trường int chứa con trỏ đến đối tượng C/C++ cũng gặp vấn đề tương tự. Tìm kiếm jint trong nguồn JNI và đảm bảo rằng bạn chuyển sang long ở phía Java và jlong ở phía C++.

Việc khai báo hàm ngầm sẽ nguy hiểm hơn rất nhiều đối với mã 64 bit. C/C++ giả định rằng loại dữ liệu trả về của một hàm được khai báo ngầm (nghĩa là một hàm mà trình biên dịch không thấy khai báo) là int. Nếu loại dữ liệu trả về thực tế của hàm là con trỏ, thì phương thức này sẽ hoạt động tốt trên hệ thống 32 bit, trong đó con trỏ vừa với int, nhưng trên hệ thống 64 bit trình biên dịch sẽ bỏ qua nửa trên của con trỏ. Ví dụ:

// This function returns a pointer:
// extern char* foo();

// If you don't include a header that declares it,
// when the compiler sees this:
char* result = foo();

// Instead of compiling that to:
result = foo();

// It compiles to something equivalent to:
result = foo() & 0xffffffff;

// Which will then cause a SIGSEGV if you try to dereference `result`.

Cờ trình biên dịch sau đây sẽ biến các cảnh báo khai báo hàm ngầm thành các lỗi để bạn có thể tìm thấy và khắc phục sự cố này dễ dàng hơn:

-Werror=implicit-function-declaration

Nếu có trình tập hợp cùng dòng (inline assembler), bạn sẽ phải viết lại hoặc sử dụng triển khai C/C++ thuần tuý.

Nếu bạn có mã kích thước cố định là các kiểu (ví dụ: 8 hoặc 16 byte), hãy thay thế bằng biểu thức sizeof(T) tương đương, chẳng hạn như sizeof(void*).

Nếu cần biên dịch có điều kiện mã khác cho 32-bit so với 64-bit, bạn có thể dùng #if defined(__LP64__) cho các điểm khác biệt chung 32/64 hoặc __arm__, __aarch64__ (arm64), __i386__ (x86) và __x86_64__ cho các kiến trúc cụ thể mà Android hỗ trợ.

Bạn sẽ cần phải điều chỉnh chuỗi định dạng cho các hàm giống như printf hoặc scanf, vì tham số định dạng truyền thống không cho phép bạn chỉ định kiểu 64 bit theo cách chính xác cho cả hai thiết bị 32 bit và 64 bit. Macro PRISCN trong <inttypes.h> đã giải quyết vấn đề này, PRIxPTRSCNxPTR để viết/đọc con trỏ hex và PRId64 cùng SCNd64 để viết/đọc các giá trị 64-bit di động.

Khi chuyển đổi, bạn có thể cần phải sử dụng 1ULL để có được hằng số 64 bit để chuyển đổi thay vì sử dụng 1 (vốn chỉ 32 bit).

Dùng Android App Bundle để hạn chế việc gia tăng kích thước

Việc thêm tính năng hỗ trợ kiến trúc 64 bit vào ứng dụng có thể làm kích thước tệp APK tăng lên. Bạn nên tận dụng tính năng Android App Bundle để giảm thiểu tác động về kích thước của cả mã gốc 32 và 64 bit trong cùng một tệp APK.

Việc chuyển ứng dụng sang sử dụng Android App Bundle thực sự có thể là một cải tiến cho kích thước tệp APK, giảm kích thước ứng dụng xuống mức thấp hơn so với hiện tại.

Nhà phát triển trò chơi

Chúng tôi hiểu rằng việc di chuyển công cụ phát triển trò chơi (game engine) của bên thứ ba là một quá trình chuyên sâu với thời gian thực hiện dài. Rất may là 3 công cụ được sử dụng nhiều nhất hiện nay đều hỗ trợ 64 bit:

  • Unreal từ năm 2015
  • Cocos2d từ năm 2015
  • Unity từ năm 2018

Nhà phát triển Unity

Nâng cấp lên các phiên bản có tính khả thi cao

Unity bắt đầu hỗ trợ phiên bản 64 bit với các phiên bản 2018.22017.4.16.

Nếu bạn thấy mình đang sử dụng một phiên bản Unity không hỗ trợ 64 bit, hãy xác định phiên bản mà bạn muốn nâng cấp rồi làm theo hướng dẫn mà Unity cung cấp để di chuyển môi trường, đảm bảo ứng dụng được nâng cấp lên một phiên bản có thể tạo thư viện 64 bit. Unity khuyến nghị bạn nên có quyền truy cập vào các tính năng và bản cập nhật mới nhất bằng cách nâng cấp lên phiên bản LTS mới nhất của trình sửa đổi này.

Sau đây là biểu đồ trình bày các phiên bản Unity và việc bạn nên làm:

Phiên bản Unity Phiên bản có hỗ trợ 64 bit? Hành động đề xuất

2020.x

✔️

Đảm bảo các chế độ cài đặt bản dựng xuất ra thư viện 64 bit.

2019.x

✔️

Đảm bảo các chế độ cài đặt bản dựng xuất ra thư viện 64 bit.

2018.4 (LTS)

✔️

Đảm bảo các chế độ cài đặt bản dựng xuất ra thư viện 64 bit.

2018.3

✔️

Đảm bảo các chế độ cài đặt bản dựng xuất ra thư viện 64 bit.

2018.2

✔️

Đảm bảo các chế độ cài đặt bản dựng xuất ra thư viện 64 bit.

2018.1

Có tính năng hỗ trợ thử nghiệm 64 bit.

2017.4 (LTS)

✔️

Được hỗ trợ từ 2017.4.16. Đảm bảo các chế độ cài đặt bản dựng xuất ra thư viện 64 bit.

2017.3

✖️

Nâng cấp lên phiên bản hỗ trợ 64 bit.

2017.2

✖️

Nâng cấp lên phiên bản hỗ trợ 64 bit.

2017.1

✖️

Nâng cấp lên phiên bản hỗ trợ 64 bit.

<=5.6

✖️

Nâng cấp lên phiên bản hỗ trợ 64 bit.

Thay đổi chế độ cài đặt bản dựng để xuất thư viện 64 bit

Nếu đang sử dụng phiên bản Unity hỗ trợ thư viện Android 64 bit, bạn có thể tạo phiên bản ứng dụng 64 bit bằng cách điều chỉnh chế độ cài đặt bản dựng. Bạn cũng sẽ cần sử dụng phần phụ trợ IL2CPP làm Phần phụ trợ tập lệnh (Scripting Backend) (xem thông tin chi tiết tại đây). Để thiết lập dự án Unity nhằm xây dựng kiến trúc 64 bit, hãy làm như sau:

  1. Chuyển đến phần Build Settings (Tuỳ chọn cài đặt bản dựng) và đảm bảo rằng bạn đang xây dựng cho Android bằng cách xác minh rằng biểu tượng Unity nằm bên cạnh Android trong Platform (Nền tảng).
    1. Nếu biểu tượng Unity không ở bên cạnh nền tảng Android, hãy chọn Android rồi nhấp vào Switch Platform (Chuyển nền tảng).
  2. Nhấp vào Player Settings (Cài đặt trình phát).

    Phần cài đặt trình phát trong Unity

  3. Chuyển đến Player Settings Panel (Bảng cài đặt trình phát) > Settings for Android (Cài đặt cho Android) > Other settings (Chế độ cài đặt khác) > Configuration (Cấu hình)

  4. Đặt Scripting Backend (Phần phụ trợ tập lệnh) thành IL2CPP.

  5. Chọn hộp đánh dấu Target Architecture (Cấu trúc mục tiêu) > ARM64.

    đặt kiến trúc mục tiêu trong Unity

  6. Hãy tạo bản dựng như bình thường!

Lưu ý rằng việc xây dựng ARM64 sẽ yêu cầu bạn phải tạo tất cả nội dung dành riêng cho nền tảng đó. Hãy làm theo hướng dẫn của Unity để giảm kích thước tệp APK, đồng thời cân nhắc tận dụng tính năng Android App Bundle để giúp giảm mức tăng kích thước.

Nhiều tệp APK và tuân thủ 64 bit

Nếu bạn đang sử dụng tính năng hỗ trợ nhiều tệp APK của Google Play để phát hành ứng dụng, hãy lưu ý rằng việc tuân thủ yêu cầu 64 bit sẽ được đánh giá ở cấp độ bản phát hành. Tuy nhiên, yêu cầu về phiên bản 64 bit không áp dụng cho các tệp APK hoặc gói ứng dụng không được phân phối cho thiết bị chạy Android 9 Pie trở lên.

Nếu một trong các tệp APK được đánh dấu là không tuân thủ nhưng đó lại là phiên bản cũ hơn và không thể đưa vào trạng thái tuân thủ, thì một chiến lược bạn nên làm là thêm thuộc tính maxSdkVersion="27" trong phần tử uses-sdk ở tệp kê khai của tệp APK đó. Tệp APK này sẽ không được phân phối cho thiết bị chạy Android 9 Pie trở lên và tệp APK này sẽ không còn bị chặn vì lý do tuân thủ nữa.

RenderScript và tuân thủ 64 bit

Nếu ứng dụng sử dụng RenderScript và được xây dựng bằng các công cụ Android phiên bản cũ hơn, thì bạn có thể thấy các vấn đề về tuân thủ 64 bit của ứng dụng. Với các công cụ tạo trước phiên bản 21.0.0, trình biên dịch có thể tạo mã bit vào tệp .bc bên ngoài. Các tệp .bc cũ này không còn được hỗ trợ cho kiến trúc 64 bit, do đó, sự hiện diện của tệp này trong tệp APK sẽ gây ra sự cố tuân thủ.

Để khắc phục vấn đề này, hãy xoá mọi tệp .bc trong dự án, nâng cấp môi trường lên build-tools-21.0.0 trở lên rồi đặt renderscriptTargetApi trong Android Studio thành 21 trở lên để yêu cầu trình biên dịch (compiler) không chuyển phát (emit) tệp .bc. Sau đó, hãy tạo lại ứng dụng, kiểm tra các tệp .bc rồi tải lên Play Console.

Kiểm tra ứng dụng trên phần cứng 64 bit

Phiên bản 64 bit của ứng dụng phải có cùng bộ tính năng và chất lượng giống như phiên bản 32 bit. Hãy kiểm tra ứng dụng để đảm bảo rằng người dùng trên các thiết bị 64 bit mới nhất có trải nghiệm tuyệt vời trong ứng dụng.

Để bắt đầu kiểm tra ứng dụng, bạn cần có một thiết bị hỗ trợ 64 bit. Có rất nhiều thiết bị phổ biến hỗ trợ 64 bit, chẳng hạn như Pixel và các thiết bị hàng đầu khác của Google.

Cách dễ nhất để kiểm tra tệp APK là cài đặt ứng dụng bằng adb. Trong hầu hết trường hợp, bạn có thể cung cấp --abi dưới dạng một tham số để cho biết cần cài đặt thư viện nào vào thiết bị. Thao tác này sẽ cài đặt ứng dụng chỉ với thư viện 64 bit trên thiết bị.

:: Command Line
# A successful install:
> adb install --abi armeabi-v7a YOUR_APK_FILE.apk
Success

# If your APK does not have the 64-bit libraries:
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
adb: failed to install YOUR_APK_FILE.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]

# If your device does not support 64-bit, an emulator, for example:
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
ABI arm64-v8a not supported on this device

Sau khi cài đặt thành công, hãy kiểm tra ứng dụng như bình thường để đảm bảo chất lượng giống như phiên bản 32 bit.

Phát hành

Khi bạn cảm thấy ứng dụng đã sẵn sàng, hãy phát hành như bình thường. Như thường lệ, hãy tiếp tục làm theo các phương pháp hay nhất để triển khai ứng dụng. Bạn nên tận dụng các kênh thử nghiệm khép kín để phát hành cho số lượng người dùng có giới hạn, nhằm đảm bảo chất lượng của ứng dụng được nhất quán.

Khi ra mắt một bản cập nhật lớn, hãy đảm bảo rằng bạn đã thử nghiệm kỹ lưỡng trên các thiết bị hỗ trợ 64 bit trước khi phát hành cho thêm nhiều đối tượng.