PEP 738 – 将 Android 作为受支持平台
- 作者:
- Malcolm Smith <smith at chaquo.com>
- 发起人:
- Petr Viktorin <encukou at gmail.com>
- 讨论至:
- Discourse 帖子
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2023年12月12日
- Python 版本:
- 3.13
- 决议:
- Discourse 消息
摘要
本 PEP 提议将 Android 添加为 CPython 的受支持平台。最初的目标是让 Android 在 Python 3.13 中获得 Tier 3 支持。
本 PEP 基于 PEP 730 – Russell Keith-Magee 的“将 iOS 作为受支持平台”,并涵盖了许多相同的问题。搜索“iOS”一词可以找到这两个平台之间的显著差异。
动机
在过去的15年中,移动平台已成为计算领域越来越重要的组成部分。Android 是运行在约 70% 的这些设备上的操作系统。然而,CPython 中没有对 Android 的官方支持。
Chaquopy、BeeWare 和 Kivy 项目都已支持 Android 多年,并且它们都已被用于生成已获准在 Google Play 商店发布的应用程序。这证明了 Android 支持的技术可行性。
对于 Python 作为一门语言的未来而言,它能够在任何广泛采用的平台上使用至关重要。否则,潜在用户将选择 确实 为这些平台提供支持的其他语言。在教育领域尤其如此,新一代的开发者在许多情况下已经将更多时间花在使用移动平台而非桌面平台上。
基本原理
通用
Android 大体上是一个 POSIX 平台,基于 Linux 内核和 ELF 二进制格式。它不使用 glibc,而是提供自己的 C 库实现,称为 Bionic。因此,它通常与任何其他 Linux 发行版不具备二进制兼容性,即使架构匹配。它还有自己独特的文件系统布局,与其他任何 Unix 系统都不同。
然而,Android 与 Linux 的源代码兼容性非常好。在其早期,C 库非常不完整,但大部分空白在2014年左右被填补。自那时以来,任何为 Linux 编译的 C 代码通常都可以为 Android 编译,除非它涉及直接访问硬件设备或操作系统服务。
CPython 也是如此。尽管它从未正式支持 Android,但最新版本(3.6及更高版本)已经可以通过最少的补丁编译到 Android。
操作系统版本
每个 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(原生开发工具包)包含 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 代码的硬件)。尽管跨架构模拟是可能的,但它的性能和稳定性要差得多,这就是
armeabi-v7a模拟器镜像自 2016 年以来没有更新的原因。然而,它仍然用于手表和超低成本手机。如果这种情况持续下去,我们可能需要在未来的 Python 版本中考虑添加它。
即使不正式支持 32 位架构,也不应进行任何会阻碍仍希望构建它们的下游项目的更改。
应用程序生命周期
Android 应用程序中的主要编程语言是 Java,或者是其现代衍生语言 Kotlin。因此,应用程序不提供自己的可执行文件。相反,所有应用程序都从运行操作系统提供的可执行文件的 Java 虚拟机开始。应用程序的 Java 代码可以通过加载动态库并使用 JNI 调用它们来向进程添加原生代码。
与 iOS 不同,Android 支持 创建子进程。但是,应用程序只能在 某些位置 运行可执行文件,这些位置在运行时都不可写入。长时间运行的子进程 被官方不鼓励,并且不保证在未来的 Android 版本中得到支持。
Android 确实提供了一个命令行 shell,但这仅供开发人员使用,普通最终用户无法使用。
出于这些原因,在 Android 上运行 Python 的推荐方式是将 libpython3.x.so 加载到主应用程序进程中。此平台将不正式支持 python3.x 可执行文件。
规范
工作范围
这项工作的重点是生成一个与现有 Windows 可嵌入包 等效的 Android 版本,即一套已编译的库,开发人员可以将其添加到他们的应用程序中。无需安装程序。
将 Android 添加为 Tier 3 平台仅需要添加从未经修补的 CPython 源代码编译 Android 兼容构建的支持。这不一定要求在 python.org 上有任何官方分发的 Android 工件,尽管这些可能在未来添加。
Android 将使用与其他 POSIX 平台相同的配置和 Makefile 系统进行构建,因此必须在 POSIX 平台上构建。Linux 和 macOS 都将受支持。
将提供一个 Gradle 项目,用于运行 CPython 测试套件。将提供工具来自动化构建测试套件应用程序、启动模拟器、安装测试套件和执行它的过程。
链接
由于 应用程序生命周期 中讨论的原因,Python 将作为一个动态的 libpython3.x.so 库包含在应用程序中,该库可以使用 dlopen 加载到应用程序中。
与 Linux 不同,Android 不会隐式地使用 dlopened 库来解析后续加载的库中的重定位,即使使用 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 的标准导入器处理。
标准库
不支持的模块
由于底层 C API 不可用,Android 将不支持许多标准库模块:
curses和readlinedbm.gnu和dbm.ndbmgrpmultiprocessing– 尽管通常允许子进程(参见 应用程序生命周期),但 Android 不支持 System V IPC API 的任何部分。tkinter和turtle– 这些将需要 Tk 本身的 Android 构建,而这并未得到官方支持。
sys
sys.platform 将返回 "android"。尽管 Android 基于 Linux,但它在足够多的重要方面存在差异,以至于一个单独的名称是合理的。
当嵌入到 Android 应用程序中时,C 语言级别的 stdio 流未连接到任何东西。因此,在此模式下,sys.stdout 和 sys.stderr 将被重定向到系统 Logcat,该 Logcat 可以通过 Android 开发工具查看。sys.stdin 将始终返回 EOF。
platform
platform 模块返回的大多数值将与 os.uname() 返回的值匹配,但以下情况除外:
platform.system()-"Android",而不是默认的"Linux"platform.release()- Android 版本号,字符串形式(例如"14"),而不是 Linux 内核版本
此外,将添加 platform.android_ver() 方法,该方法返回一个具名元组,包含以下内容:
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 构建机器人。这些将包括 Linux x86_64 和 macOS ARM64 机器,这将涵盖所有受支持的运行时架构和所有受支持的构建平台。
CPython 目前不在 GitHub Actions 上测试 Tier 3 平台,但如果这种情况发生变化,他们的 Linux 和 macOS 运行器也能够托管 Android 模拟器。自 2024 年 1 月 以来,macOS ARM64 运行器对所有公共仓库都是免费的。
打包
Android 轮子将使用 android_<api-level>_<abi> 格式的标签。例如:
android_21_arm64_v8aandroid_21_x86_64
有关 <api-level> 的含义,请参阅 操作系统版本。在轮子标签的上下文中,它表示编译轮子时选择的最低 Android 版本。pip 等安装工具应以类似于现有 macOS 标签的方式解释这一点,即最小 API 级别为 N 的应用程序可以合并标记为 API 级别 N 或更旧的轮子。
此格式源自 Chaquopy 项目,该项目目前维护着一个 轮子仓库,标签在 API 级别 16 到 21 之间变化。
然而,依靠一小群 Android 爱好者来构建整个 Python 生态系统并不是一个可扩展的解决方案。在主要库日常发布其自己的 Android 轮子之前,社区在 Android 上采用 Python 的能力将受到限制。
因此,有必要清楚地记录项目如何将其 Android 构建添加到其 CI 和发布工具中。将 Android 支持添加到诸如 crossenv 和 cibuildwheel 等工具中可能是一种实现方式。
Android 轮子标签格式也应添加到 PyPI 接受的标签列表中。
PEP 11 更新
PEP 11 将更新以包含两个受支持的 Android ABI。Autoconf 已经使用以下三元组识别它们:
aarch64-linux-androidx86_64-linux-android
Petr Viktorin 将担任这些 ABI 的初始核心团队联系人。
向后兼容性
添加新平台本身不会给 CPython 带来任何向后兼容性问题。但是,如果任何 CPython 补丁的最终形式与 BeeWare 和 Kivy 等项目历史上使用的补丁不一致,则可能会对这些项目产生一些向后兼容性影响。
安全隐患
添加新平台不会带来任何新的安全隐患。
如何教授此内容
与此 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