PEP 738 – 將 Android 加入支援的平台
- 作者:
- Malcolm Smith <smith at chaquo.com>
- 贊助人:
- Petr Viktorin <encukou at gmail.com>
- 討論於:
- Discourse 討論串
- 狀態:
- 最終 (Final)
- 類型:
- 標準軌跡 (Standards Track)
- 建立日期:
- 2023年12月12日
- Python 版本:
- 3.13
- 決議:
- Discourse 訊息
摘要
本 PEP 提議將 Android 加入 CPython 的支援平台。初步目標是在 Python 3.13 中使 Android 達到第三級(Tier 3)支援標準。
本 PEP 參考了 Russell Keith-Magee 所撰寫的 PEP 730 ——「將 iOS 加入支援的平台」,並涵蓋了許多相同的議題。兩大平台之間的顯著差異,可透過搜尋「iOS」一詞找到。
動機
過去 15 年來,行動平台在運算領域的重要性日益增加。Android 是目前運行在約 70% 裝置上的作業系統。然而,CPython 目前尚未對 Android 提供官方支援。
Chaquopy、BeeWare 與 Kivy 等專案多年來皆支援 Android,且都曾被用於開發已成功在 Google Play 商店發布的應用程式。這證明了 Android 支援在技術上的可行性。
對於 Python 作為一種程式語言的未來而言,能夠在所有廣泛採用的平台上運行至關重要。否則,潛在使用者將會選擇「確實」提供這些平台支援的其他語言。這在教育領域尤其明顯,因為新一代開發者在行動平台上花費的時間往往已超過桌面平台。
原理
總則
Android 大體上屬於 POSIX 平台,基於 Linux 核心與 ELF 二進位格式。它不使用 glibc,而是提供了一套稱為 Bionic 的自有 C 函式庫實作。因此,即使架構相符,它通常也與其他 Linux 發行版不具二進位相容性。它亦擁有獨特的檔案系統配置,與其他 Unix 系統並不相似。
然而,Android 與 Linux 的原始碼相容性相當良好。在 Android 早期階段,其 C 函式庫非常不完整,但大部分缺口在 2014 年左右已被補齊。自那時起,任何針對 Linux 編譯的 C 程式碼通常皆可為 Android 編譯,除非該程式碼涉及硬體裝置或作業系統服務的直接存取。
對於 CPython 而言亦是如此。儘管它從未正式支援 Android,但近期的版本(自 3.6 起)已能透過極少的修補程式完成編譯。
作業系統版本
每個 Android 版本可透過三種方式識別:
- 傳統的點分版本號(儘管近期版本皆已改用整數)
- 連續整數的「API 等級」(開發者說明文件中最常見的形式)
- 以糖果為主題的字母代號(雖已不再用於行銷,但仍出現在開發者說明文件中)
上述三者之間並無一致的對應規律,必須查詢 對照表。
Android 每年都會發布一個新的主要版本,但每個裝置可用的更新完全由製造商掌控。遺憾的是,許多製造商在使用者準備丟棄裝置前,很早就停止發送更新。例如,截至 2023 年 10 月,尚在接收安全性更新的最舊 Android 版本為 API 等級 30,但根據 Google 自家的統計數據,僅有 60% 的裝置處於該版本或更新的版本。
因此,針對 Python 3.13,我們提議最低 Android 版本設定為 5.0(API 等級 21),該版本於 2014 年發布。根據上述統計,這將涵蓋 99% 的活躍裝置。
開發工具
Android 開發工具在 Linux (x86_64)、Windows (x86_64) 與 macOS (x86_64 與 ARM64) 上皆獲得相同支援。對於 CPython 而言,最重要的工具包括:
- NDK (Native Development Kit):包含 C 與 C++ 編譯器 (clang)、連結器 (lld),以及所有系統函式庫的標頭檔。
不同 NDK 版本編譯的函式庫之間的二進位相容性通常很好,但為了可重現性,每個 Python 版本在其生命週期內最好固定使用單一 NDK 版本。對於 Python 3.13,這將是目前的 NDK 長期支援版本:r26。
每個 NDK 版本皆可設定為以廣泛的 Android 版本為目標。例如,NDK r26 支援 API 等級 21 到 34。然而,針對較舊 Android 版本編譯的二進位檔案通常可在較新版本上無限期地持續運作;僅在安全性考量下才會打破此規則。
- Gradle:用於構建完整、可部署應用程式的工具。
- 模擬器:基於 QEMU,是在開發機上模擬運行的 Android 裝置。與 iOS 不同,模擬器使用與相同架構的真實裝置相同的 ABI,並可執行相同的二進位檔案。
這些工具皆可透過命令列或基於 IntelliJ IDEA 的 Android Studio IDE 使用。
架構
Android 目前支援 4 種架構。其在 Android 工具中的名稱分別為:
armeabi-v7aarm64-v8ax86x86_64
幾乎所有現行的實體裝置都使用 ARM 架構。 x86 與 x86_64 僅支援用於模擬器。
對於 Python 3.13,我們提議第三級(Tier 3)支援僅涵蓋 64 位元平台(arm64-v8a 與 x86_64)。
x86自 2020 年起即不再作為開發平台支援,且自該年起未再發布任何新的模擬器映像檔。armeabi-v7a在活躍裝置中的比例目前已 低於 10% 且持續下降。由於沒有可用於模擬器的原生宿主(ARM64 Mac 沒有硬體支援 ARM32 程式碼),使用可靠的構建機器人(buildbot)來覆蓋此架構也更為困難。雖然跨架構模擬是可行的,但其效能與穩定性較差,這也是為什麼
armeabi-v7a模擬器映像檔自 2016 年後即未再更新的原因。不過,它仍持續用於手錶與超低價手機。如果此情況持續,我們可能需要在未來的 Python 版本中考慮將其加入。
即使 32 位元架構未獲得正式支援,也不應做出任何妨礙下游專案(若仍希望構建該架構)的變更。
應用程式生命週期
Android 應用程式的主要程式語言為 Java,或其現代後繼者 Kotlin。因此,應用程式本身不提供可執行檔案。相反地,所有應用程式皆以 Java 虛擬機啟動,並運行由作業系統提供的可執行檔。應用程式的 Java 程式碼隨後可透過載入動態函式庫並透過 JNI 呼叫,將原生程式碼加入至該處理程序中。
與 iOS 不同,Android「確實」支援建立子處理程序。然而,應用程式僅能執行 特定位置的可執行檔,且無一處可在執行時期寫入。長時間運行的子處理程序 在官方文件中並不建議使用,且未來版本亦不保證支援。
Android 雖提供命令列 Shell,但僅供開發者使用,一般終端使用者無法使用。
基於上述理由,在 Android 上運行 Python 的建議方式是將 libpython3.x.so 載入到主應用程式處理程序中。此平台將不正式支援 python3.x 可執行檔。
規範
工作範圍
本項工作的重點在於製作一個與現有 Windows 可嵌入套件 (embeddable package) 等效的 Android 版本,即一組編譯好的函式庫,開發者可將其加入至應用程式中。無需安裝程式。
將 Android 加入為第三級(Tier 3)平台,僅需新增對從未修補的 CPython 原始碼編譯 Android 相容建置的支援即可。雖然未來可能會加入,但這不必然要求 python.org 上必須有任何官方發布的 Android 成品。
Android 的構建將使用與其他 POSIX 平台相同的 configure 與 Makefile 系統,因此必須「在」POSIX 平台上進行構建。Linux 與 macOS 皆會受到支援。
我們將提供一個 Gradle 專案,用於運行 CPython 測試套件。並將提供自動化工具,以執行建置測試套件應用程式、啟動模擬器、安裝測試套件並進行執行的流程。
連結 (Linkage)
基於 應用程式生命週期 所討論的原因,Python 將作為動態 libpython3.x.so 函式庫包含在應用程式中,並可透過 dlopen 載入。
與 Linux 不同,Android 並不會自動使用已 dlopen 的函式庫來解析後續載入函式庫中的重定位,即使使用了 RTLD_GLOBAL 亦然。因此,所有 Python 擴充模組在為 Android 構建時,皆必須明確連結至 libpython3.x.so。
連結至 libpython3.x.so 的擴充模組無法由已靜態連結至 libpython3.x.a 的可執行檔載入。因此,Android 將不支援靜態 libpython3.x.a 函式庫。此模式與 CPython 在 Windows 上的運作方式相同。
此做法同時允許使用 -Wl,--no-undefined 選項在建置時期檢測缺失的符號,這能顯著節省時間。
與 iOS 不同,Android 允許從任何位置載入動態函式庫,因此包含共存 .py、.pyc 與 .so 檔案的目錄樹可由 Python 的標準匯入器進行處理。
標準函式庫
不支援的模組
部分標準函式庫模組將無法在 Android 上支援,因為基礎的 C API 不可用:
curses與readlinedbm.gnu與dbm.ndbmgrpmultiprocessing—— 儘管一般情況下允許子處理程序(參閱 應用程式生命週期),但 Android 不支援 System V IPC API 的任何部分。tkinter與turtle—— 這些模組需要 Android 版本的 Tk 本身,但官方並不支援。
sys
sys.platform 將會回傳 "android"。儘管 Android 基於 Linux,但其差異性足以區分為獨立名稱。
當嵌入至 Android 應用程式時,C 語言級別的 stdio 串流並未連接至任何裝置。因此在此模式下,sys.stdout 與 sys.stderr 將被重新導向至系統的 Logcat,可透過 Android 開發工具進行檢視。sys.stdin 將永遠回傳 EOF。
platform
platform 模組回傳的大多數數值將與 os.uname() 的回傳值相符,但以下除外:
platform.system()-"Android",而非預設的"Linux"platform.release()- Android 版本號(以字串形式,例如"14"),而非 Linux 核心版本
此外,將會新增一個 platform.android_ver() 方法,該方法會回傳一個包含下列項目的 namedtuple:
release- 裝置的 Android 版本(以字串形式,例如"14")api_level- 裝置的 API 等級(以整數形式,例如34)manufacturer- 裝置的 製造商(以字串形式,例如"Google")model- 裝置的 型號名稱(以字串形式,例如"Pixel 7")device- 裝置的 裝置名稱(以字串形式,例如"panther")is_emulator- 若裝置為模擬器則為True;若為實體裝置則為False。
model 與 device 之間何者較具唯一性,以及何者較接近行銷名稱,因不同製造商而異。
os
os.uname() 將回傳 POSIX uname() 呼叫的原始結果。這將得到以下數值:
sysname-"Linux"release- Linux 核心版本(例如"5.10.157-android13-4-00003-gdfb1120f912b-ab10994928")
此做法將 os 模組視為系統 API 的「原始」介面,而 platform 則視為提供更實用值的較高層級 API。
持續整合 (CI) 資源
由於 Android 模擬器與實體裝置使用相同的 ABI,並附帶相同或極相似的作業系統二進位檔案,因此在模擬器上進行測試已足夠。x86_64 模擬器可在 Linux、macOS 或 Windows 上運行,但 ARM64 模擬器僅在 ARM64 Mac 上受到支援。
Anaconda 已主動提供實體硬體來運行 Android 建置機器人 (buildbots)。這些機器人將包含 Linux x86_64 與 macOS ARM64 機型,可涵蓋兩個受支援的執行時期架構與兩個受支援的建置平台。
CPython 目前未在 GitHub Actions 上測試第三級(Tier 3)平台,但若未來有所改變,其 Linux 與 macOS 運行環境亦可託管 Android 模擬器。ARM64 Mac 運行環境自 2024 年 1 月起已免費提供給所有公開儲存庫使用。
套件封裝 (Packaging)
Android Wheel 套件將使用 android_<api-level>_<abi> 格式的標籤。例如:
android_21_arm64_v8aandroid_21_x86_64
關於 <api-level> 的意義,請參閱 作業系統版本。在 Wheel 標籤的語境中,它代表編譯 Wheel 時所選定的最低 Android 版本。如 pip 等安裝工具應以類似於現有 macOS 標籤的方式解讀此標籤,即最低 API 等級為 N 的應用程式,可引入標記為 API 等級 N 或更舊的 Wheel。
此格式起源於 Chaquopy 專案,該專案目前維護一個標籤範圍在 API 等級 16 到 21 之間的 Wheel 儲存庫。
然而,僅依靠少數 Android 愛好者來構建整個 Python 生態系並非可擴展的解決方案。在著名函式庫定期發布其 Android Wheel 之前,社群在 Android 上採用 Python 的能力將受到限制。
因此,必須清楚記錄各專案如何將 Android 建置加入至其 CI 與發布工具鏈。將 Android 支援加入至 crossenv 與 cibuildwheel 等工具,可能是實現此目標的方法之一。
Android Wheel 標籤格式亦應加入至 PyPI 接受的標籤清單中。
PEP 11 更新
PEP 11 將會更新,納入兩個受支援的 Android ABI。Autoconf 已透過下列三元組 (triplets) 識別它們:
aarch64-linux-androidx86_64-linux-android
Petr Viktorin 將擔任這些 ABI 的核心團隊首要聯繫人。
回溯相容性
新增平台不會為 CPython 本身引入任何向後相容性問題。然而,若 CPython 修補程式的最終形式與 BeeWare 與 Kivy 等歷史上提供 CPython 支援的專案所使用的修補程式不一致,則可能會對這些專案產生向後相容性的影響。
安全性影響
新增平台不會帶來任何新的安全性影響。
如何教學
與本 PEP 相關的教育需求涉及兩群開發者。
首先,「應用程式」開發者需要了解如何將 Python 建置至 Android 應用程式中,連同其自身的 Python 程式碼與任何支援套件,以及如何在使用時期執行它們。說明文件將以類似於現有 Windows 可嵌入套件的形式涵蓋此內容。不過,我們將建議開發者優先使用如 Briefcase、Chaquopy 與 Buildozer 等高階工具,這些工具皆已具備完善的說明文件。
其次,具有二進位元件的「套件」開發者需要了解如何為 Android 建置並發布它們(參閱 封裝)。
參考實作
Chaquopy 儲存庫 包含參考修補程式與建置腳本。在將它們併入上游之前,必須先將其與 Chaquopy 的其他元件解耦。
Briefcase 提供了在 Android 裝置與模擬器上執行測試套件的參考實作。Toga Testbed 即是一個使用 GitHub Actions 在 Android 模擬器上執行測試套件的範例。
否決的想法
對 platform.android_ver() 的原始規格進行了以下變更:
- 移除了
min_api_level欄位,因為與其他欄位不同,這並非當前裝置的屬性。此資訊仍可透過現有的sys.getandroidapilevel()函式取得。 - 新增了
is_emulator欄位,因為測試期間的經驗顯示部分問題僅出現在模擬器上。
版權
本文件已進入公有領域或遵循 CC0-1.0-Universal 授權,以較寬鬆者為準。
來源: https://github.com/python/peps/blob/main/peps/pep-0738.rst
最後修改日期: 2024-10-07 17:43:06 GMT