支援 64 位元架構

在 Google Play 上發布的應用程式必須支援 64 位元架構。為應用程式新增 64 位元版本,不僅能提升效能,還能為使用僅限 64 位元硬體的裝置提前做好準備。

請按照下列步驟操作,確保 32 位元應用程式支援 64 位元的裝置。

評估應用程式

如果應用程式只使用以 Java 程式設計語言或 Kotlin 編寫的程式碼 (包括所有程式庫或 SDK),則應用程式可支援 64 位元裝置。如果您的應用程式使用任何原生程式碼,或是您不確定這一點,請評估應用程式。

快速檢查狀態

前往 Play 管理中心查看現有版本,確認其是否符合規定。

如果草稿版本有與 64 位元版本要求相關的任何問題,Play 管理中心也會顯示警告,參見下圖範例。

如果系統顯示快訊,請按照下列步驟,讓應用程式與 64 位元裝置相容。

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

如果符合下列條件,即表示應用程式使用原生程式碼:

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

應用程式中是否有 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. 從選單中依序選取「Build」>「Analyze APK」

    啟動 APK 分析工具

  3. 選擇要評估的 APK。

  4. 查看「lib」資料夾,其中會代管任何「.so」檔案。如果未看到任何代管項目,表示應用程式支援 64 位元裝置,您不必採取其他行動。如果看到 armeabi-v7ax86,就表示有 32 位元程式庫。

  5. 查看「arm64-v8a」或「x86_64」資料夾中是否有類似的「.so」檔案。

    啟動 APK 分析工具

  6. 如果您沒有任何 arm64-v8ax86_64 程式庫,請更新建構程序,開始在 APK 中建構並封裝這些構件。

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

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

APK 檔案結構與 ZIP 檔案類似。使用指令列或其他擷取工具,將 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,並確定已切換至 long (Java 端) 和 jlong (C++ 端)。

對於 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 功能,這樣當您在同一個 APK 中納入 32 位元與 64 位元的原生程式碼時,才能將對大小造成的影響減至最低。

遊戲開發人員

目前最常用的三種引擎均支援 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. 前往「Build Settings」,確認「Platform」下方的「Android」旁是否顯示 Unity 符號,確保您是針對 Android 進行建構。
    1. 如果 Android 平台旁未顯示 Unity 符號,請選取「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 Architectures」下方的「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" 屬性。這樣一來,系統就不會向搭載 Android 9 Pie 以上版本的裝置提供該 APK,因此不會再違反規定。

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 位元的嚴格環境中測試應用程式:

搭載僅限 64 位元系統映像檔的 Google Pixel

為協助開發及測試應用程式,我們為部分 Pixel 裝置提供僅限 64 位元嚴格環境的特殊系統映像檔。這些僅限 64 位元的映像檔會與 Android 預覽版本的標準工廠系統映像檔同時提供。

Android 14 (Beta 版)

如要取得適用於 Android 14 的僅限 64 位元映像檔,請參閱下載頁面的僅限 64 位元映像檔相關章節

與工廠系統映像檔類似,您可以使用 Android Flash Tool 將 Android 14 64 位元專用映像檔刷新到裝置上,也可以手動刷新裝置

Android 13 (QPR3 Beta 3.2)

如要取得適用於 Android 13 QPR3 的 64 位元專用映像檔,請參閱下載頁面的「64 位元專用映像檔」一節。

與工廠系統映像檔類似,您可以使用 Android Flash Tool 將 Android 13 64 位元專用映像檔刷新到裝置上,也可以手動刷新裝置

Android Emulator

自 Android 12 (API 級別 31) 起,Android Emulator 系統映像檔僅提供 64 位元版本。請使用 Android 12 (API 級別 31) 以上版本的系統映像檔建立 Android 虛擬裝置 (AVD),在嚴格的僅限 64 位元環境中測試應用程式。

其他裝置選項

如果您沒有上述任一裝置,或無法使用 Android Emulator,下一個最佳做法是使用支援 64 位元的裝置,例如 Google Pixel 或其他裝置製造商在近期推出的旗艦款裝置。

安裝及測試應用程式

測試 APK 最簡單的方法是使用 Android Debug Bridge (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 位元裝置上順利運作。即使您的應用程式未直接依附受影響的程式庫,也不代表依附元件中的第三方程式庫和 SDK 不會依附這類程式庫。

SoLoader

如果您使用了原生程式碼載入器 SDK SoLoader,請更新至 0.10.4 以上版本。假如您的應用程式使用了依附於 SoLoader 的 SDK,請務必一併將受影響的 SDK 更新至最新穩定版。

SoLoader 0.9.0 以下版本會假設系統程式庫位於 /vendor/lib:/system/lib。這項錯誤不會影響已有此路徑的裝置 (例如 Pixel 7),但如果 /vendor/lib64:/system/lib64 是裝置上存放系統程式庫的唯一路徑,這項假設就會造成裝置異常終止。

如要進一步瞭解如何修正此問題和 SoLoader 造成的其他問題,請參閱 Google 說明中心的相應解答

OpenSSL

如果您使用 OpenSSL 程式庫,請更新至 OpenSSL 1.1.1i 以上版本。要是應用程式使用了透過 HTTPS 提供通訊功能的 SDK,或其他依附於 OpenSSL 的 SDK,請務必一併更新至使用新版 OpenSSL 的 SDK 最新版本。萬一找不到符合規定的 SDK 版本,請洽詢 SDK 供應商。

ARMv8.3 PAC 功能會在執行階段驗證指標,因此能在硬體輔助下實現控制流程完整性。然而,由於舊版 OpenSSL 未正確使用這項功能,因此在所有搭載 ARMv8.3a 以上版本處理器的裝置上,都會發生執行階段異常終止的情形。

如要進一步瞭解如何修正此問題和 OpenSSL 造成的其他問題,請參閱 Google 說明中心的相應解答

BTI

ARMv8.5 以上版本會使用分支目標指令 (BTI) 來協助防範 JOP 攻擊。舊版模糊處理 SDK 會分支成帶有隨機偏移值且使用 BTI 建構的程式庫,這可能導致應用程式異常終止。這些指令已編碼為 HINT,因此在不支援 BTI 的裝置上不會發生這個錯誤。

發布

如果您認為應用程式已準備妥當,即可照常發布。一如往常,請繼續按照最佳做法部署應用程式。建議您利用封閉測試群組,僅向少數使用者推出應用程式,確保應用程式的品質一致。

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