ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

python-for-android 的 Kivy 3 Bootstrap 契约:从 `_kivy_bootstrap` 模块到 Activity 提供的完整实现指南

python-for-android 的 Kivy 3 Bootstrap 契约:从 `_kivy_bootstrap` 模块到 Activity 提供的完整实现指南 开发工具构建工具移动开发【免费下载链接】python-for-androidTurn your Python application into an Android APK项目地址https://gitcode.com/gh_mirrors/py/python-for-android点击查看免费下载Kivy 3 改变了与构建工具bootstrap之间的协作方式它不再反射属于自己的 Activity 类名而是向正在运行的应用导入一个约定命名的_kivy_bootstrap模块并询问“当前的 Android Activity 是什么”。本文以 python-for-android 官方文档 doc/source/kivy_bootstrap.rst 为骨架结合仓库中 p4a 自身的实现源码完整讲解这个契约的交付物、三个函数接口、六条实现规则以及 Kivy 消费该契约的细节。读完本文你既能理解 p4a 是如何让未修改的 Kivy 3 跑在自己的 APK 上的也能为自定义 bootstrap 或第三方构建工具实现同样的契约。契约的本质把“类名反射”换成“直接询问”Kivy 2.x 时代Kivy 内部硬编码了org.kivy.android.PythonActivity这类类名并直接通过android模块调用android.remove_presplash()等函数。这意味着 Kivy 与某个特定 bootstrap 绑死了。Kivy 3 的 bootstrap 契约即文档中标注的kivy_bootstrap_contract扭转了这一方向Kivy 3 不持有任何 bootstrap 的 Activity 类名而是向 bootstrap 索取当前的android.app.Activity。因此任何能构建 Android APK 的工具——无论是本仓库 python-for-android、其他构建工具还是你自己编写的 bootstrap——只需要能回答“当前 Activity 是谁”这个问题就能让一个未经修改的 Kivy 3正常跑在它构建的 APK 上。名字属于 Kivy 而不是任何 bootstrap这正是这套安排的精髓Kivy 只依赖这个名字于是不依赖任何特定 bootstrapbootstrap 之间可以自由替换。在 p4a 中这个契约的官方实现位于 pythonforandroid/recipes/android/src/_kivy_bootstrap.py本文的源码级讲解均以此文件为准。你只需要交付一个模块顶层可导入的_kivy_bootstrap契约的交付物极其精简一个纯 Python 模块名为_kivy_bootstrap并且必须能被应用运行时sys.path的顶层直接导入。不需要注册调用Kivy 采用“拉取”而非“推送”模式在首次需要 Activity 时才懒加载该模块bootstrap 无需在启动时主动登记不需要安排导入顺序模块加载时机由 Kivy 决定不能依赖 Kivy模块自身与 Kivy 无任何依赖关系。由于 Kivy 是在第一次需要 Activity 时导入该模块它必须早于应用的main.py运行之前就出现在sys.path上。在 p4a 中这个模块是androidrecipe 的一个py_modules条目见 pythonforandroid/recipes/android/src/setup.pysetup(nameandroid, version1.0, packages[android], package_dir{android: android}, # Top-level, not under android: the name is Kivys, and Kivy imports it # without knowing which bootstrap built the app. py_modules[_kivy_bootstrap], ext_modulescythonized_modules )注意注释里的关键点_kivy_bootstrap被特意放在顶层而不是android包内部因为这个名字属于 Kivy——Kivy 导入它时并不知道是哪个 bootstrap 构建了这个应用。这样它最终会和应用的其他顶层模块一起落入site-packages保证main.py之前即可导入。必需函数get_activity()契约只强制要求一个函数def get_activity(): return SomeActivityClass.mActivity语义要求返回当前android.app.Activity如果没有 Activity返回None。None是合法答案而不是错误——后台 service 运行时本来就没有 ActivityKivy 把它当作普通情况处理Kivy 在每次访问时实时调用该函数从不缓存结果因此你的实现也绝不能缓存 Activity 实例对应规则 2。p4a 的实现如下pythonforandroid/recipes/android/src/_kivy_bootstrap.pydef get_activity(): Return the current android.app.Activity, or None if there is none. None is a legitimate answer — a p4a service runs without an Activity. global _activity_class if _activity_class is None: # Resolved on first use rather than at import so that reflection # failures surface from the call, not from Kivys discovery import. _activity_class autoclass(ACTIVITY_CLASS_NAME) return _activity_class.mActivity实现要点有两个Activity 类名来自构建期生成的android.config而不是硬编码。ACTIVITY_CLASS_NAME由 android recipe 在prebuild_arch阶段写入 pythonforandroid/recipes/android/init.py其值来自self.ctx.activity_class_name——这保证了用户在命令行用--activity-class-name指定的自定义 Activity 能被正确识别而不是被假设成默认值。只缓存 Java 类不缓存 Activity 实例_activity_class autoclass(ACTIVITY_CLASS_NAME)只做一次类反射然后每次调用都读取_activity_class.mActivity这个实时静态字段。这样即使 Android 因旋转、深色模式切换、区域设置变化或多窗口模式销毁并重建了 Activity每次读取拿到的都是最新实例永远不会读到失效对象也不会在 Python 全局变量里钉住一个指向已销毁 Activity 的 JNI 引用。p4a 构建期的默认类名定义在 pythonforandroid/build.pyself.activity_class_name uorg.kivy.android.PythonActivity self.service_class_name uorg.kivy.android.PythonService命令行入口 pythonforandroid/toolchain.py 也提供了对应的--activity-class-name与--service-class-name参数默认值同样是这两个类名。可选函数get_context()def get_context(): return SomeContext返回一个android.content.Context或None。契约规定只有当你构建的 bootstrap 会运行从未持有 Activity 却仍需要 Context 的进程典型如纯后台 service时才需要实现它当该函数缺失或返回None时Kivy 会从当前 Activity 推导出 Application context对 Kivy 使用 Context 的全部场景来说二者等价。p4a 不实现get_context()原因写在其实现的模块文档字符串中p4a 的 Activity 总能提供 Application contextKivy 会回退到自行推导。这正是“可选”二字的含义——不实现是完全合规的。可选函数remove_presplash()def remove_presplash(): ...语义关闭你的启动闪屏boot splash。Kivy 在自己绘制出第一帧之后调用它——一个闪屏该在什么时候消失只有 Kivy 自己知道这正是把该函数放在 bootstrap 模块而不是 Activity 方法上的原因。“怎么关”完全由你决定不同 bootstrap 的机制截然不同p4a 的做法是叠加一个View然后移除它使用 Android 12 系统闪屏的 bootstrap 则是释放一个 keep-on-screen 条件这根本不是 Activity 上的方法。如果根本没有闪屏要关就直接省略该函数Kivy 会把缺失当作无操作处理不产生任何警告。p4a 的实现展示了“机制完全自主”这一点pythonforandroid/recipes/android/src/_kivy_bootstrap.pydef remove_presplash(): activity get_activity() if activity is None: return remove getattr(activity, removeLoadingScreen, None) if remove is not None: remove()这里有三层防御全部来自对“闪屏属于 bootstrap不由 Kivy 操心”的贯彻没有 Activity 就什么都不做service_only等场景合法用getattr探测方法是否存在而不是维护一个硬编码的 bootstrap 名单——注释里专门点出这种名单曾经漂移过android模块旧版remove_presplash只对 sdl2/sdl3 生效而实际上 webview 的 Activity 也有这个方法移除操作本身由 Java 侧的removeLoadingScreen()封装并自行编排到 UI 线程所以 Python 侧无需再安排线程问题。作为对照Kivy 2.3.1 时代的旧式实现位于 pythonforandroid/recipes/android/src/android/_android.pyx可以看到它被编译期宏BOOTSTRAP in [sdl2, sdl3]门控正是契约文档提到的“名单漂移”问题的来源IF BOOTSTRAP in [sdl2, sdl3]: def remove_presplash(): # Remove android presplash in SDL2 bootstrap. mActivity.removeLoadingScreen()另外加载闪屏的隐藏逻辑在 pythonforandroid/recipes/android/src/android/loadingscreen.py 中也有一个hide_loading_screen()辅助函数同样基于ACTIVITY_CLASS_NAME反射并调用removeLoadingScreen()。六条实现规则让契约正确运转的边界契约文档给出了六条必须遵守的规则逐一说明并附源码印证规则 1本模块绝不能导入 Kivy。Kivy 在导入时会读取KIVY_*环境变量并构建自身配置如果在应用main.py有机会设置这些配置之前就导入 Kivy配置会被“冻结”在错误状态。Kivy 之所以从你这里“拉取”信息正是为了让你永远不需要反过来导入它。p4a 的实现只导入jnius和android.config从不触碰 Kivy其模块文档字符串也明确说明了这一点pythonforandroid/recipes/android/src/_kivy_bootstrap.py。规则 2绝不能缓存 Activity。Android 会在配置变化旋转、深色模式、区域设置、多窗口以及进程死亡后销毁并重建 Activity。缓存实例必然过期而且在 Python 全局变量里持有实例等于把一个 JNI 引用钉死在已死亡的 Activity 上。正确做法是每次调用都实时读取——p4a 的get_activity()每次返回_activity_class.mActivity而mActivity是 Java 侧静态字段天然每次都是当前值。规则 3反射要在调用内完成而不是在导入时。可以在首次调用时解析 Java 类并缓存类本身p4a 正是如此但绝不能在模块导入阶段做反射——否则一旦反射失败错误会从 Kivy 的发现式导入中冒出来看起来像“模块缺失”掩盖了真实故障。p4a 的get_activity()在首次调用时才执行autoclass(ACTIVITY_CLASS_NAME)。规则 4导入期只允许抛ImportError且必须是你故意的。Kivy 把ImportError解释为“该 bootstrap 未实现此契约”会继续并抛出诊断信息而任何其他异常逃出模块导入都会被当作你的模块自身故障并原样传播不会被误认为缺少 bootstrap。换句话说ImportError是“我没有这个能力”的协议信号其他异常是“我坏了”的真实告警。规则 5presplash 钩子不得抛异常且必须容忍被重复调用。Kivy 不会防护这个调用也不承诺只调用一次。什么都不做是正确结果抛异常则不是。p4a 的实现天然满足Activity 为None或没有removeLoadingScreen方法时直接返回多次调用也幂等。规则 6除了解析后的类之外不持有任何状态。Kivy 可能从多个线程调用这些函数。无状态模块天然线程安全、无需加锁而契约的设计保证了“无状态”不需要付出任何代价。p4a 的实现中唯一的模块级变量就是_activity_class缓存的那个类Activity 本身从不落盘到任何状态里。Kivy 消费该契约的机制调试时很有用了解 Kivy 侧的行为对调试你自己的实现极有帮助契约文档的“What Kivy does with it”一节懒加载 结果缓存模块在首次使用时才被导入导入结果包括失败结果都会被缓存——因此一个没有该模块的构建不会在每次读取几何信息时都付出一次失败的导入代价缺失即报错如果在 Android 运行时模块缺失或没有可调用的get_activityKivy 会抛出ActivityProviderMissing。这个异常故意不被Kivy 的显示几何 getter 捕获否则一个坏掉的构建会被掩盖成看似合理的默认值kivy.mobile.get_app_context()优先调用你的get_context()否则回退到在当前 Activity 上调用getApplicationContext()kivy.mobile.get_activity()原样返回你的函数返回值包括None。同时支持 Kivy 2.3.1双轨兼容Kivy 2.3.1 早于这个契约它仍然直接走 p4a 的 Pythonandroid模块——调用android.remove_presplash()等函数见 pythonforandroid/recipes/android/src/android/_android.pyx——并硬编码org.kivy.android.PythonActivity。因此一个想同时跑 Kivy 2.3.1 和 Kivy 3的 bootstrap必须两套都满足为 Kivy 3 交付_kivy_bootstrap同时为 2.3.1 提供android模块以及一个该名字的 Activity 类一个只针对 Kivy 3的 bootstrap则只需要本契约无需关心android模块。p4a 的现状正是双轨并存_kivy_bootstrap.py服务 Kivy 3而android包含_android.pyx、loadingscreen.py等服务 Kivy 2.3.1 与旧式调用。两者可以同时存在于同一份构建产物中互不干扰。在 p4a 中验证与深入阅读如果你想在仓库中亲手验证这套契约可以按以下路径继续契约正文doc/source/kivy_bootstrap.rst本文即基于此文档展开p4a 的完整实现pythonforandroid/recipes/android/src/_kivy_bootstrap.py含详细的设计动机注释构建期配置生成pythonforandroid/recipes/android/init.pyACTIVITY_CLASS_NAME、BOOTSTRAP、JAVA_NAMESPACE等常量在此写入android/config.py模块打包位置pythonforandroid/recipes/android/src/setup.pypy_modules[_kivy_bootstrap]决定了它的顶层可导入性旧式 2.3.1 兼容路径pythonforandroid/recipes/android/src/android/_android.pyx 与 pythonforandroid/recipes/android/src/android/loadingscreen.py。一句话总结Kivy 3 的 bootstrap 契约把“Activity 从哪来”这个问题的答案所有权从 Kivy 移交给了构建工具。p4a 用一个 60 余行的纯 Python 顶层模块兑现了它——实时读取 Activity、缓存 Java 类而不缓存实例、不导入 Kivy、不持有状态——从而让一个未经任何修改的 Kivy 3 应用能够在 sdl2、sdl3、webview、service_only 等任意 p4a bootstrap 之上平稳运行。赞分享开发工具构建工具移动开发【免费下载链接】python-for-androidTurn your Python application into an Android APK项目地址https://gitcode.com/gh_mirrors/py/python-for-android点击查看免费下载相关推荐QuantDinger 指标开发指南从图表 Python 契约到 Strategy V2 转换的完整实战QuantDinger 指标开发指南从图表 Python 契约到 Strategy V2 转换的完整实战 本文基于 docs/trading/INDICATO后端金融科技人工智能AI 应用AI AgentMCP 服务Kivy/Python-for-Android 项目中的 distutils/setuptools 集成指南Kivy/Python for Android 项目中的 distutils/setuptools 集成指南 概述 在 Kivy/Python for Andr开发工具构建工具移动开发Feast Data Types for Java从 Protobuf 到 Java 的数据契约模块全解析Feast Data Types for Java从 Protobuf 到 Java 的数据契约模块全解析 导读 java/datatypes 是 FeastMLOps后端数据工程上一篇魔兽争霸3终极优化指南5分钟解锁现代游戏体验 下一篇Sunshine游戏串流服务器深度实战指南打造你的家庭游戏云创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表