自 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 位元程式庫。
- 開啟 Android Studio,然後開啟任何專案。
從選單中選取「建立」>「分析 APK」…
選擇要評估的 APK。
查看 lib 資料夾,您可以在其中找到任何的「.so」檔案。如果在應用程式中完全找不到任何「.so」檔案,代表應用程式已準備就緒,不需要採取進一步行動。如果看到 armeabi-v7a 或 x86,就表示有 32 位元程式庫。
檢查 arm64-v8a 或 x86_64 資料夾中是否有類似的「.so」檔案。
如果沒有任何 arm64-v8a 或 x86_64 資料庫,請更新建構程序,以開始在 APK 中建構並封裝那些成果。
如果兩個資料庫都已封裝完畢,就可以略過這個步驟並直接在 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-v7a 和 arm64-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 位元整數。您必須更新可將指標儲存至 int
、unsigned
或 uint32_t
等類型指標的程式碼。在 Unix 系統上,long
與指標大小相符,然而在 Windows 上並不是如此,因此您必須改用 uintptr_t
或 intptr_t
這類意圖顯而易見的程式碼。使用 ptrdiff_t
類型來儲存兩個指標間的差異。
請一律優先採用 <stdint.h>
中定義的特定固定寬度整數類型,而非 int
或 long
等傳統類型,即便是對於非指標的類型也是如此。
請使用以下編譯器旗標,找出程式碼在指標和整數之間轉換錯誤的情況:
-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 位元類型,您必須為 printf
或 scanf
這類函式調整格式字串。<inttypes.h>
中的 PRI
和 SCN
巨集已解決這個問題,PRIxPTR
和 SCNxPTR
是用於寫入/讀取十六進位指標,而 PRId64
和 SCNd64
則可以寫入/讀取 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.2 和 2017.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 位元架構,請按照下列指示操作:
- 前往版本設定,驗證 Unity 符號是否位於「平台」底下的「Android」旁邊,確認自己是否正在為 Android 建構。
- 如果 Unity 符號不在 Android 平台旁邊,請選取「Android」,然後按一下「Switch Platform」。
按一下「Player Settings」。
依序前往「Player Settings Panel」>「Settings for Android」>「Other settings」>「Configuration」
將「Scripting Backend」設為「IL2CPP」。
勾選「Target Architecture」>「ARM64」核取方塊。
照常建構!
請注意,如要針對 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 位元的裝置上進行徹底測試,再將應用程式發布給更多目標對象。