支援 64 位元架構

2019 年 8 月 1 日起,在 Google Play 上發布的應用程式都必須支援 64 位元架構。 64 位元 CPU 可以為使用者提供更快速、更豐富的體驗。為應用程式新增 64 位元版本,不僅能提升效能、有助日後創新,還能為使用搭載 64 位元硬體的裝置提前做好準備。

本指南說明目前您可採取的步驟,以確保 32 位元應用程式可支援 64 位元的裝置。

評估應用程式

如果應用程式只使用以 Java 程式設計語言或 Kotlin 編寫的程式碼 (包括所有程式庫或 SDK),則應用程式已可支援 64 位元裝置。如果應用程式使用到任何原生程式碼,或您不確定是否已使用,您就必須對應用程式進行評估並採取行動。

快速檢查狀態

如要快速確認應用程式是否已符合 64 位元版本規定,請前往 Play 管理中心查看現有版本,確認是否符合規定:

如果出現與 64 位元版本相關的任何問題,Play 管理中心也會顯示適用於草稿版本的警告。範例如下:

如果看到快訊,請按照下列步驟為應用程式做好準備。

應用程式是否使用原生程式碼?

請先確認應用程式是否使用了任何原生程式碼。 如果符合下列條件,即表示應用程式使用原生程式碼:

  • 在應用程式中使用任何 C/C++ (原生) 程式碼。
  • 連結到任何第三方原生資料庫。
  • 由使用原生資料庫的第三方應用程式建構工具所建構。

應用程式是否使用 64 位元程式庫?

檢查 64 位元程式庫最簡單的方式就是檢查 APK 檔案的結構。建構完成後,APK 會封裝應用程式所需的任何原生資料庫。原生資料庫會根據 ABI 儲存在多個資料夾中。系統不必支援每一個 64 位元架構,但每個支援的 32 位元架構都必須納入對應的 64 位元架構。

如果是 ARM 架構,32 位元程式庫位於 armeabi-v7a。 相應的 64 位元程式庫位於 arm64-v8a

如果是 x86 架構,請為 32 位元尋找 x86,為 64 位元尋找 x86_64

首先,請確認這兩個資料夾都設有原生資料庫。重點回顧:

平台 32 位元程式庫資料夾 64 位元程式庫資料夾
ARM lib/armeabi-v7a lib/arm64-v8a
x86 lib/x86 lib/x86_64

請注意,視應用程式而定,每個資料夾中的程式庫組合可能並不一定完全相同。目標是確保應用程式在僅限 64 位元的環境中正確執行。

在一般情況下,針對 32 位元和 64 位元架構建立的 APK 或軟體包中都會包含 ABI 資料夾,每個資料夾都有對應的原生資料庫組合。如果系統不支援 64 位元版本,您可能會看到 32 位元的 ABI 資料夾,而非 64 位元資料夾。

使用 APK 分析工具尋找原生資料庫

APK 分析工具是一項可讓您評估所建立 APK 各項層面的工具。在這個案例中,我們將使用這項工具來尋找任何原生資料庫,並確保其中存在 64 位元程式庫。

  1. 開啟 Android Studio,然後開啟任何專案
  2. 從選單中選取「建立」>「分析 APK」

    啟動 APK 分析工具

  3. 選擇要評估的 APK。

  4. 查看 lib 資料夾,您可以在其中找到任何的「.so」檔案。如果在應用程式中完全找不到任何「.so」檔案,代表應用程式已準備就緒,不需要採取進一步行動。如果看到 armeabi-v7ax86,就表示有 32 位元程式庫。

  5. 檢查 arm64-v8ax86_64 資料夾中是否有類似的「.so」檔案。

    啟動 APK 分析工具

  6. 如果沒有任何 arm64-v8ax86_64 資料庫,請更新建構程序,以開始在 APK 中建構並封裝那些成果。

  7. 如果兩個資料庫都已封裝完畢,就可以略過這個步驟並直接在 64 位元硬體上測試應用程式

透過解壓縮 APK 尋找原生資料庫

APK 檔案結構與 ZIP 檔案類似,而且也可以解壓縮。如果偏好使用指令列或任何其他擷取工具,解壓縮 APK 會是個可行的方式。

只需將 APK 檔案解壓縮 (取決於解壓縮工具,您可能必須將檔案重新命名為 .zip),然後按照上述指南查看已解壓縮的檔案,確認應用程式是否已為支援 64 位元裝置做好準備。

例如,您可以透過指令列執行下列指令:

:: 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

請注意,在這個範例中,armeabi-v7aarm64-v8a 資料庫的存在,表示應用程式支援 64 位元架構。

透過 64 位元程式庫建立應用程式

建立 64 位元程式庫的操作說明如下。但是,請務必瞭解這麼做只會涵蓋可透過原始碼建構的程式碼和程式庫。

如果使用任何外部 SDK 或程式庫,請按照上述步驟操作,確保使用 64 位元版本。如果無法取得 64 位元版本,請與 SDK 或程式庫擁有者聯絡,並在規劃支援 64 位元裝置時將這一點納入考量。

使用 Android Studio 或 Gradle 進行建構

大部分的 Android Studio 專案都是使用 Gradle 做為基礎建構系統,因此這部分適用於這兩種情況。依據您想支援的架構而定,為原生程式碼啟用版本和在應用程式「build.gradle」檔案中 ndk.abiFilters 的設定加入 arm64-v8a 和/或 x86_64 一樣簡單:

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

使用 CMake 建構

如果使用 CMake 建構應用程式,可以將 arm64-v8a 傳遞到「-DANDROID_ABI」參數,即可建構 64 位元 ABI:

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

使用 externalNativeBuild 時,這個選項不會有任何作用。請參閱使用 Gradle 建構一節。

使用 ndk-build 建構

如果應用程式是以 ndk-build 建構,您可以透過使用 APP_ABI 變數修改 Application.mk 檔案來為 64 位元 ABI 建構:

APP_ABI := armeabi-v7a arm64-v8a x86 x86_64

使用 externalNativeBuild 時,這個選項不會有任何作用。請參閱使用 Gradle 建構一節。

將 32 位元程式碼移植至 64 位元

如果程式碼已在電腦或 iOS 裝置上執行,則不需要為 Android 額外進行任何設定。如果這是第一次為 64 位元系統建構程式碼,您必須解決的主要問題是指標無法再納入如 int 這類 32 位元整數。您必須更新可將指標儲存至 intunsigneduint32_t 等類型指標的程式碼。在 Unix 系統上,long 與指標大小相符,然而在 Windows 上並不是如此,因此您必須改用 uintptr_tintptr_t 這類意圖顯而易見的程式碼。使用 ptrdiff_t 類型來儲存兩個指標間的差異。

請一律優先採用 <stdint.h> 中定義的特定固定寬度整數類型,而非 intlong 等傳統類型,即便是對於非指標的類型也是如此。

請使用以下編譯器旗標,找出程式碼在指標和整數之間轉換錯誤的情況:

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

含有 int 欄位 (其中擁有 C/C++ 物件指標) 的 Java 類別有同樣的問題。在 JNI 來源中搜尋 jint,並確認已切換至 Java 端的 long 和 C++ 端的 jlong

對於 64 位元程式碼,隱式函式宣告更加危險。C/C++ 假設隱式函式宣告的傳回類型 (也就是編譯器未看見宣告的函式) 為 int。如果函式的實際傳回類型是指標,這對於在 32 位元系統上運作並沒有問題,在這類系統上,您的指標可在 int 使用。然而,對 64 位元系統來說,這個編譯器執行到一半就無法正常運作。例如:

// 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`.

以下編譯器旗標會將隱式函式宣告的警告轉變為錯誤,讓您可以更容易找到和修正這個問題:

-Werror=implicit-function-declaration

如果擁有內嵌組譯工具,就必須重新編寫或使用一般 C/C++ 實作。

如果擁有硬式編碼大小的類型 (例如 8 或 16 個位元組),請改用對等的 sizeof(T) 運算式,例如 sizeof(void*)

如需有條件地為 32 位元編譯不同的程式碼而非為 64 位元編譯,可以使用 #if defined(__LP64__) 來處理一般 32/64 間的差異,或為適用於 Android 支援的特定架構使用 __arm____aarch64__ (arm64)、__i386__ (x86) 和 __x86_64__

由於傳統格式指定碼不允許使用對於 32 位元和 64 位元裝置來說的正確方式來指定 64 位元類型,您必須為 printfscanf 這類函式調整格式字串。<inttypes.h> 中的 PRISCN 巨集已解決這個問題,PRIxPTRSCNxPTR 是用於寫入/讀取十六進位指標,而 PRId64SCNd64 則可以寫入/讀取 64 位元值。

轉移時,可能需要使用 1ULL 來取得 64 位元的常數,而非使用只有 32 位元的 1

透過 Android App Bundle 減緩大小的增加

為應用程式新增 64 位元架構這項支援,可能會導致 APK 大小增加。我們強烈建議您善用 Android App Bundle 功能,將 32 位元與 64 位元原生程式碼在同一個 APK 中對大小造成的影響減至最低。

將應用程式改為使用 Android App Bundle 其實可以改善 APK 大小,進而縮減目前應用程式的大小。

遊戲開發人員

我們瞭解遷移第三方遊戲引擎需要很長的前置時間和密集的程序。幸好,目前最常使用的三個引擎現在都支援 64 位元:

  • Unreal:自 2015 年開始支援
  • Cocos2d:自 2015 年開始支援
  • Unity:自 2018 年開始支援

Unity 開發人員

升級至可支援的版本

Unity 剛開始是為版本 2018.22017.4.16 提供 64 位元支援。

如果發現 Unity 版本不支援 64 位元,請決定要升級的版本,並按照 Unity 提供的指南來遷移環境,確保應用程式升級至可建構 64 位元資料庫的版本。Unity 建議升級至最新的 LTS 版本編輯器,才能存取最新功能和更新。

以下圖表概述各種 Unity 版本和建議做法:

Unity 版本 版本支援 64 位元嗎? 建議做法

2020.x

✔️

確保版本設定能夠輸出 64 位元程式庫。

2019.x

✔️

確保版本設定能夠輸出 64 位元程式庫。

2018.4 (LTS)

✔️

確保版本設定能夠輸出 64 位元程式庫。

2018.3

✔️

確保版本設定能夠輸出 64 位元資料庫。

2018.2

✔️

確保版本設定能夠輸出 64 位元程式庫。

2018.1

支援 64 位元實驗功能。

2017.4 (LTS)

✔️

2017.4.16 起開始支援。 確保版本設定能夠輸出 64 位元程式庫。

2017.3

✖️

升級至支援 64 位元的版本。

2017.2

✖️

升級至支援 64 位元的版本。

2017.1

✖️

升級至支援 64 位元的版本。

<=5.6

✖️

升級至支援 64 位元的版本。

將版本設定變更為輸出 64 位元程式庫

如果使用的是支援 64 位元 Android 資料庫的 Unity 版本,您可以調整版本設定來產生 64 位元的應用程式版本。您也必須使用 IL2CPP 後端做為指令碼後端 (詳情請參閱這裡的說明)。如要設定 Unity 專案來建立 64 位元架構,請按照下列指示操作:

  1. 前往版本設定,驗證 Unity 符號是否位於「平台」底下的「Android」旁邊,確認自己是否正在為 Android 建構。
    1. 如果 Unity 符號不在 Android 平台旁邊,請選取「Android」,然後按一下「Switch Platform」
  2. 按一下「Player Settings」

    Unity 中的「Player Settings」

  3. 依序前往「Player Settings Panel」>「Settings for Android」>「Other settings」>「Configuration」

  4. 將「Scripting Backend」設為「IL2CPP」

  5. 勾選「Target Architecture」>「ARM64」核取方塊。

    在 Unity 中設定目標架構

  6. 照常建構!

請注意,如要針對 ARM64 進行建構,您必須為該平台量身打造所有資產。按照 Unity 的指南以減少 APK 大小,並考慮利用 Android App Bundle 功能,以助於緩解大小增加的情況。

多重 APK 和 64 位元版本規定

請注意,如果您目前使用 Google Play 支援多個 APK 的機制發布應用程式,系統會根據發布層級來評估 64 位元版本規定的遵循情形。不過,64 位元版本不適用於未發布至 Android 9 Pie 以上版本裝置的 APK 或應用程式套件。

如果其中一個 APK 已標示為不符合版本規定,但是版本較舊且無法依規定修正,其中一個策略就是在 APK 資訊清單中新增 uses-sdk 元素的 maxSdkVersion="27" 屬性。這個 APK 將無法傳送至搭載 Android 9 Pie 以上版本的裝置,而且不再封鎖版本遵循。

RenderScript 和 64 位元版本遵循

如果應用程式使用 RenderScript,並且以舊版 Android 工具建構,則該應用程式可能會遇到 64 位元版本的法規遵循問題。如果使用 21.0.0 之前的版本工具,編譯器可能可以產生中間碼並將其變為外部 .bc 檔案。64 位元架構不再支援這些舊版 .bc 檔案,因此 APK 中檔案的出現會造成版本遵循的問題。

如要修正問題,請移除專案中的所有 .bc 檔案,將環境升級至 build-tools-21.0.0 以上版本,然後將 Android Studio 中的 renderscriptTargetApi 設為 21 以上版本,以通知編譯器不要發出 .bc 檔案。接著,請重新建構應用程式,檢查 .bc 檔案並上傳到 Play 管理中心。

在 64 位元硬體上測試應用程式

應用程式 64 位元版本應提供與 32 位元版本相同的品質與功能組合。測試應用程式,確保最新版 64 位元裝置的使用者在應用程式中享有良好的體驗。

如要開始測試應用程式,您必須擁有支援 64 位元的裝置。市面上有許多支援 64 位元的熱門裝置,例如 Google 的 Pixel 和其他旗艦裝置。

測試 APK 最簡單的方法是使用 ADB 安裝應用程式。在多數情況下,可以提供 --abi 做為參數,指出要在裝置上安裝的資料庫。這樣就只會在裝置上安裝 64 位元的資料庫。

:: 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

安裝成功後,請照常對應用程式進行測試,以確保品質與 32 位元版本相同。

發布

準備好發布應用程式時,請照常發布。一如往常,請繼續按照這些最佳做法部署應用程式。建議您利用封閉測試群組,僅向少數使用者推出應用程式,以確保應用程式的品質一致。

推出重大更新時,請務必在支援 64 位元的裝置上進行徹底測試,再將應用程式發布給更多目標對象。