- 前端
- 跨平台
- 桌面应用
- 移动开发
【免费下载链接】flet
Build realtime web, mobile and desktop apps in Python only. No frontend experience required.
Flet 0.86.0 对 Android 应用的 Python 代码打包方式做了根本性调整:原生扩展模块(.so)改为直接从 APK 内存映射加载,纯 Python 代码则随stdlib.zip/sitepackages.zip就地导入,不再按 ABI 重复打包或首次启动时全量解压。这一改动让 APK 显著变小,但也让少数依赖__file__/pkg_resources读取数据文件的包在设备上运行时崩溃。本文以 android-extract-packages.md 为骨架,结合 Android 打包官方文档 与 flet-cli 源码 的实现细节,完整讲解新打包机制的原理、故障症状、extract_packages的配置方法与优先级规则,帮助你完成迁移并定位解决 zip 内数据文件读取失败的问题。
背景:0.86.0 起 Android 打包方式的三个变化
自 Flet 0.86.0 起,flet build apk/flet build aab生成的应用在 Python 代码的组织方式上发生了如下变化:
- 原生扩展模块(
.so)内存映射加载:.so文件不再被解压到磁盘,而是从已安装的 APK 中直接内存映射(memory-mapped)加载。 - 纯 Python 代码以 stored zip 资产发布:标准库与 site-packages 分别被打包进
stdlib.zip与sitepackages.zip,运行时通过 Python 内置的zipimport就地导入,不再按 ABI 重复复制一份,也不再在首次启动时解压全部内容。 - 移除旧 workaround 的需求:由于不再从 APK 解压原生库与 Python 代码,原先为兼容这些行为而配置的
useLegacyPackaging/keepDebugSymbols之类的规避手段已不再需要。
这一改动带来的直接收益是APK 体积显著缩小。绝大多数包从 zip 中导入是完全透明的,用户无感知;只有少数通过真实文件系统路径定位打包数据文件的包需要特殊处理,这正是本文后续要讲解的extract_packages。
关于"现代打包"(modern)与"遗留打包"(legacy,useLegacyPackaging = true)两种模式下原生库存储方式的详细对比,包括原始 APK 体积、Play Store 下载体积、设备安装体积与启动速度的权衡表,可查阅 Native library packaging (modern vs legacy) 一节。
影响范围:仅限 Android
此变更只影响 Android 目标:
- macOS、iOS、Windows、Linux 上,site-packages 仍然以未压缩的目录形式发布,行为不变;
- Web(Pyodide)目标不受影响;
- 如果应用只面向桌面、iOS 或 Web,或者 Android 应用的依赖全部是 zip 安全的(这是常见情况),则此变更纯粹是体积优化,无需任何操作。
为什么部分包必须解压:__file__与 zip 的不兼容性
从 zip 导入对绝大多数包是透明的,原因是它们定位代码和数据文件都遵循 Python 的标准导入机制。但有一类包存在例外:它们通过真实文件系统路径定位打包的数据文件,典型做法包括:
- 用
__file__拼接数据文件路径后直接open(); - 用
pkg_resources读取元数据或数据资源。
这两种方式在包被导入时,__file__指向的是sitepackages.zip内部路径(例如.../sitepackages.zip/matplotlib/...),而普通open()无法在 zip 压缩包内部打开文件,于是运行时抛错。相比之下,zip 安全的importlib.resourcesAPI 可以在 zip 内解析资源,因此像flet、certifi这类通过importlib.resources读取数据的包不需要加入extract_packages。
从源码注释可以印证这一设计的意图。在 build_base.py 中,android_extract_packages被描述为"path-hungry packages shipped extracted to disk instead of inside the zip(依赖路径的包改为解压到磁盘而非留在 zip 内)",其场景正是"packages that read bundled data via__file__/pkg_resourcesrather thanimportlib.resources"(见 CLI 参数定义处 的 help 文本)。
解压语义
当你把某个包加入extract_packages后,Flet 会将该包对应的顶层目录及其下所有内容解压到应用的文件目录(files directory),而不是留在sitepackages.zip内。这样__file__相对路径的读取就会重新指向真实的磁盘文件,恢复正常工作。
症状:构建成功但设备上运行时崩溃
此问题的典型特征是:构建阶段一切正常(构建过程只负责打包,不负责执行应用代码),但应用安装到设备后,在导入或首次使用该包时报错。错误堆栈中通常会出现以sitepackages.zip或stdlib.zip作为路径目录组件的路径,例如(以matplotlib为例):
FileNotFoundError: [Errno 2] No such file or directory: '/data/user/0/<applicationId>/files/.../sitepackages.zip/matplotlib/mpl-data/matplotlibrc'路径中的<applicationId>对应你应用的 Android application ID。除了FileNotFoundError,NotADirectoryError或OSError(带有类似的sitepackages.zip/...路径)同样是常见信号——说明该包从__file__计算出了数据路径,却把 zip 内部条目当成了普通文件来读取。
迁移指南:把失败的包加入extract_packages
遇到上述崩溃时,迁移动作只有一个:把失败包的导入名加入配置。有两种等价方式。
方式一:在pyproject.toml中配置
[tool.flet.android] extract_packages = ["matplotlib", "sklearn"]方式二:通过命令行参数传递
flet build apk --android-extract-packages matplotlib sklearn命令行参数--android-extract-packages支持nargs="+",可一次传入多个包名(见 参数定义)。
关键:条目是导入名,不是发行名
每个条目必须是该包的import name(导入名)——也就是它在 site-packages 下的顶层目录名——而不是 PyPI 发行名(distribution name):
| 发行名(PyPI) | 导入名(应填写的条目) |
|---|---|
scikit-learn | sklearn |
opencv-python | cv2 |
matplotlib | matplotlib |
例如填写sklearn而非scikit-learn,填写cv2而非opencv-python。
配置优先级与源码实现
extract_packages的取值遵循严格的优先级顺序(resolution order):
- CLI 参数
--android-extract-packages; - 平台配置
[tool.flet.android].extract_packages; - 全局配置
[tool.flet].extract_packages。
也就是说,只要命令行提供了参数,它就覆盖pyproject.toml中的所有配置;[tool.flet.android]下的配置又优先于[tool.flet]下的全局配置。这一顺序在 Android 打包文档的 Resolution order 小节 中有明确说明。
从源码可以进一步看到它如何落地。在 build_base.py 中:
- 仅当
self.package_platform == "Android"时才处理该选项(印证了"仅影响 Android"的约束); - 用户列表依次尝试
options.android_extract_packages→tool.flet.android.extract_packages→tool.flet.extract_packages,取第一个非空值; - 最终通过
list(dict.fromkeys(ANDROID_DEFAULT_EXTRACT_PACKAGES + user_extract_packages))合并内置默认集与用户列表,并利用dict.fromkeys去重。
其中ANDROID_DEFAULT_EXTRACT_PACKAGES定义于 build_base.py 第 78 行,当前为list[str] = [](空列表),即仓库当前版本中内置默认集为空,实际生效条目完全由用户配置决定;从代码注释看,其设计意图是"内置一套覆盖常见坏包的默认集,用户列表(CLI / pyproject)在其基础上合并"。该列表随后通过环境变量传递给serious_python_android的 Gradle 构建步骤(代码注释指出该选项"Consumed by the serious_python_android Gradle split duringflutter build,so the env var is set on build_env")。
通配符与 dist-info 目录
条目本质上是相对于 site-packages 的路径,匹配该路径及其下所有内容。条目中还可以使用*和?通配符,它们针对顶层目录名进行匹配:
[tool.flet.android] extract_packages = ["somepackage*"]通配符形式还能顺带解压同级的somepackage-<version>.dist-info/目录。这对于通过pkg_resources读取元数据或数据文件的包尤为有用——因为pkg_resources有时需要访问dist-info目录中的元数据。
已知受影响的包列表
截至当前文档,以下包已知需要解压才能在 Android 上正常工作(表格来源:Affected packages):
| 包(PyPI) | 应填条目 | 原因 |
|---|---|---|
matplotlib | "matplotlib" | 通过__file__相对路径读取mpl-data(字体、matplotlibrc) |
scikit-learn | "sklearn" | 通过__file__相对路径加载打包的数据文件 |
opencv-python | "cv2" | 通过__file__相对路径解析配置文件并加载原生扩展 |
astropy | "astropy" | 导入时通过__file__读取astropy/CITATION |
thinc | "thinc" | 导入时通过__file__读取thinc/backends/_custom_kernels.cu |
spacy | "spacy", "thinc" | 加载时导入thinc,并通过__file__读取自身语言数据;两者都要列出 |
注意spacy一行:它依赖thinc,因此需要同时列出"spacy"与"thinc"两个条目。
排障建议与补充
如果某个依赖在设备上报出前述 zip 路径错误,但它不在上表中:
- 将该包加入
extract_packages后重新构建验证(加入后 Flet 会将其解压到应用文件目录,__file__相对读取即可恢复); - 可以在 Flet 官方 discussions 中报告该包,或提交 PR 把它补充到已知包列表中,帮助后续用户(见 Android 打包文档 中的相关说明)。
此外,若你的排障涉及原生库加载问题(而非纯 Python 数据文件),注意extract_packages与遗留打包(legacy packaging)解决的是两类不同问题:前者针对 zip 内数据文件的读取,后者针对.so在 APK 内的存储与解压方式。遗留打包只是改变了原生库的存储与加载方式,并不能让不兼容的库变得可用,也不应作为extract_packages的替代方案。
时间线
- 变更版本:
0.86.0
延伸阅读
- Android packaging: extract packages(官方维护的功能文档,含通配符行为与完整示例)
- Native library packaging: modern vs legacy
flet buildCLI 参考- Breaking changes and deprecations index(列出各版本迁移指南)
- flet-cli 对
extract_packages的完整实现:build_base.py
- 前端
- 跨平台
- 桌面应用
- 移动开发
【免费下载链接】flet
Build realtime web, mobile and desktop apps in Python only. No frontend experience required.
相关推荐
Flet 0.86 起 `flet build` 默认将应用与依赖包编译为 `.pyc`:机制解读与迁移指南
Flet 0.86 起 flet build 默认将应用与依赖包编译为 .pyc :机制解读与迁移指南 自 Flet 0.86.0 起, flet build
前端跨平台桌面应用移动开发Flet 0.86 发布详解:dart-bridge 进程内通信、全新 Android 打包与 Python 3.14 支持
Flet 0.86 发布详解:dart bridge 进程内通信、全新 Android 打包与 Python 3.14 支持 Flet 0.86.0 是通往 1
前端跨平台桌面应用移动开发Flet 应用分发打包指南:从 `flet build` 看懂全平台构建机制
Flet 应用分发打包指南:从 flet build 看懂全平台构建机制 flet build 是 Flet CLI 提供的一键打包命令,它能将 Python
前端跨平台桌面应用移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考