Following system colour scheme - Python 增強提案 Selected dark colour scheme - Python 增強提案 Selected light colour scheme - Python 增強提案

Python 增強提案 (Python Enhancement Proposals)

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 上使用 Python

×

關於如何提出變更建議,請參閱 PEP 1

摘要

本 PEP 提議將 Android 加入 CPython 的支援平台。初步目標是在 Python 3.13 中使 Android 達到第三級(Tier 3)支援標準。

本 PEP 參考了 Russell Keith-Magee 所撰寫的 PEP 730 ——「將 iOS 加入支援的平台」,並涵蓋了許多相同的議題。兩大平台之間的顯著差異,可透過搜尋「iOS」一詞找到。

動機

過去 15 年來,行動平台在運算領域的重要性日益增加。Android 是目前運行在約 70% 裝置上的作業系統。然而,CPython 目前尚未對 Android 提供官方支援。

ChaquopyBeeWareKivy 等專案多年來皆支援 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-v7a
  • arm64-v8a
  • x86
  • x86_64

幾乎所有現行的實體裝置都使用 ARM 架構。 x86x86_64 僅支援用於模擬器。

對於 Python 3.13,我們提議第三級(Tier 3)支援僅涵蓋 64 位元平台(arm64-v8ax86_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 不可用:

  • cursesreadline
  • dbm.gnudbm.ndbm
  • grp
  • multiprocessing —— 儘管一般情況下允許子處理程序(參閱 應用程式生命週期),但 Android 不支援 System V IPC API 的任何部分。
  • tkinterturtle —— 這些模組需要 Android 版本的 Tk 本身,但官方並不支援。

sys

sys.platform 將會回傳 "android"。儘管 Android 基於 Linux,但其差異性足以區分為獨立名稱。

當嵌入至 Android 應用程式時,C 語言級別的 stdio 串流並未連接至任何裝置。因此在此模式下,sys.stdoutsys.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

modeldevice 之間何者較具唯一性,以及何者較接近行銷名稱,因不同製造商而異。

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_v8a
  • android_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 支援加入至 crossenvcibuildwheel 等工具,可能是實現此目標的方法之一。

Android Wheel 標籤格式亦應加入至 PyPI 接受的標籤清單中。

PEP 11 更新

PEP 11 將會更新,納入兩個受支援的 Android ABI。Autoconf 已透過下列三元組 (triplets) 識別它們:

  • aarch64-linux-android
  • x86_64-linux-android

Petr Viktorin 將擔任這些 ABI 的核心團隊首要聯繫人。

回溯相容性

新增平台不會為 CPython 本身引入任何向後相容性問題。然而,若 CPython 修補程式的最終形式與 BeeWare 與 Kivy 等歷史上提供 CPython 支援的專案所使用的修補程式不一致,則可能會對這些專案產生向後相容性的影響。

安全性影響

新增平台不會帶來任何新的安全性影響。

如何教學

與本 PEP 相關的教育需求涉及兩群開發者。

首先,「應用程式」開發者需要了解如何將 Python 建置至 Android 應用程式中,連同其自身的 Python 程式碼與任何支援套件,以及如何在使用時期執行它們。說明文件將以類似於現有 Windows 可嵌入套件的形式涵蓋此內容。不過,我們將建議開發者優先使用如 BriefcaseChaquopyBuildozer 等高階工具,這些工具皆已具備完善的說明文件。

其次,具有二進位元件的「套件」開發者需要了解如何為 Android 建置並發布它們(參閱 封裝)。

參考實作

Chaquopy 儲存庫 包含參考修補程式與建置腳本。在將它們併入上游之前,必須先將其與 Chaquopy 的其他元件解耦。

Briefcase 提供了在 Android 裝置與模擬器上執行測試套件的參考實作。Toga Testbed 即是一個使用 GitHub Actions 在 Android 模擬器上執行測試套件的範例。

否決的想法

platform.android_ver() 的原始規格進行了以下變更:

  • 移除了 min_api_level 欄位,因為與其他欄位不同,這並非當前裝置的屬性。此資訊仍可透過現有的 sys.getandroidapilevel() 函式取得。
  • 新增了 is_emulator 欄位,因為測試期間的經驗顯示部分問題僅出現在模擬器上。

來源: https://github.com/python/peps/blob/main/peps/pep-0738.rst

最後修改日期: 2024-10-07 17:43:06 GMT