
1. 移植前的灵魂拷问你要的是“能跑”还是“能上架”我接到的需求其实很常见手里有一个用Python开发的内部工具平时在电脑上跑得很舒服但领导突然说“把它搬到Android上我想在手机上直接用”。这个工具大概长这样——从Excel读取库存数据按安全库存公式算补货建议再输出一个报告主流程不到1000行依赖也简单就是openpyxl和几个标准库。换成你你第一反应是什么我当时的想法很直接Python能写到Android上吗搜索一圈号称能“运行Python”的方案多得让人眼花缭乱。真上手之后才明白所谓Python项目移植Android首先要回答的不是“用什么工具”而是“你要的到底是哪一种使用形态”。这里面的差异非常大。同样是“在Android上运行Python代码”实际上最典型的只有三类一是把Python整个Progressive Web App一样做成独立APK用户装一个应用就能用二是把Python作为“算法引擎”嵌入到已有的Android原生项目里UI用Java/Kotlin来写三是干脆不打包直接在手机的终端环境里跑Python脚本用浏览器或者一个壳来当界面。这三个目标对应的技术路线几乎不互通选错了方向后面几周基本就是反复推倒重来。所以我在一开始强烈建议你问自己四个问题。第一这个工具要不要给普通人用如果给普通用户必须做成独立可安装的AppTermux这种终端方案直接出局。第二UI复杂度高不高是按钮加列表就够还是需要自定义画布、手势、复杂布局这直接影响选Kivy还是选BeeWare。第三有没有已经存在的Android原生项目如果有而且你只是想复用一段Python计算逻辑那最合理的答案几乎必然是Chaquopy。第四性能要求如何纯Python打包的方案性能虽然日常够用但做大计算量、多线程渲染时会明显吃力。我把这四层需求整理成一张对照表方便你对照自己的项目现状来看使用形态典型场景优先考虑方案独立App从零开发内部工具、表单应用、工具型产品Kivy / BeeWare嵌入现有原生App已有Android工程只想复用Python算法Chaquopy临时原型、自用脚本个人验证、演示给同事看Termux Flask跨平台桌面移动同一套代码还要跑Windows/macOSBeeWare 或 Kivy这个表不是万能的但它能帮你先卡掉一大半错误方向。我这次就是拿同一个“库存补货建议工具”挨个跑了四条路Kivy Buildozer打成独立APKBeeWare Toga做成原生控件界面Chaquopy嵌入到一个建好的空Android工程里以及Termux Flask配合WebView壳来跑。下面每一节我都会给出实测过程、构建数据以及我踩过的坑最后再给一张完整对比表。你直接照着抄作业就行。2. 方案一Kivy Buildozer把纯Python项目直接打成APK2.1 为什么先选Kivy一套GUI代码全平台复用Kivy是Python生态里最成熟的一套GUI框架它最大的特点是界面的每个控件都是自己用OpenGL绘制的不依赖Android原生控件。这意味着同一套代码在Windows、Linux、macOS、Android、iOS上都能跑视觉表现一致。代价就是界面看起来不那么“Android”控件和系统原生的交互习惯有割裂感。但对一个内部库存工具来说界面丑一点完全不是问题能把Excel逻辑跑起来才是关键。我的演示项目叫stock_tool核心计算逻辑从Excel读数据、算安全和补货量最后生成一张“需要补货的商品清单”。在桌面上它原本是一个命令行脚本。移植到Kivy我需要给这个逻辑套一层简单的UI一个“加载Excel”按钮一个显示补货建议的列表顶部留一个状态栏。这层UI用Kivy写大概100行就够真正的时间全花在打包环境上。2.2 Buildozer打包的完整流程环境、配置、构建先说环境。Buildozer官方支持Linux和macOSWindows用户必须走WSL或虚拟机我这边是在Ubuntu 22.04上完成的Python版本用的3.10。首先安装基础依赖然后装Buildozersudo apt update sudo apt install -y git zip unzip openjdk-17-jdk python3-pip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev pip3 install --user buildozer kivy然后是项目目录结构。Kivy项目要能够被Buildozer识别主程序一般叫main.pyPython逻辑单独放一个包目录。我的stock_tool目录长这样stock_tool/ ├── main.py ├── stock_logic/ │ ├── __init__.py │ └── calc.py └── buildozer.specmain.py里写Kivy的App子类计算逻辑全部调用stock_logic里的纯函数。Buildozer打包时requirements就是告诉它要往APK里塞哪些Python依赖。我的配置是这样的# buildozer.spec 关键片段 [app] title StockTool package.name stocktool package.domain org.example source.dir . source.include_exts py,png,jpg,kv,atlas version 0.1 requirements python3,kivy,openpyxl android.api 33 android.minapi 21 android.archs arm64-v8a, armeabi-v7a android.accept_sdk_license True这里有个特别容易踩的坑如果你的逻辑里用了ssl、sqlite3这类标准库模块注意有些在Android上不是默认带进APK的。我们在计算里不用外部数据库但如果你用了sqlite3requirements里得显式加上openssl或者相关的配方否则运行时直接ModuleNotFoundError。第一次构建时Buildozer会下载Android SDK、NDK、python-for-android等一大堆工具网速差的话等20到40分钟非常正常耐心点别中途关掉。构建命令只有一行buildozer -v android debug构建成功后会生成bin/StockTool-0.1-arm64-v8a_armeabi-v7a-debug.apk把这个APK发到手机上就能装。需要注意的是debug签名的APK在部分国产手机上安装时会提示“未知来源”这是正常的关掉安装保护就行。2.3 实测结果与三个我不得不写下来的坑我的库存工具用Kivy方案打包后实测数据如下APK体积约35MB冷启动到看到主界面约2.1秒操作过程中内存峰值约120MB。对一个内部工具来说这个表现可以接受。但构建过程里我栽了三个跟头每一个都值得单独写一笔。第一个坑是中文显示。Kivy默认字体在Android上不支持中文运行起来所有中文全部变成方块。解决方法是准备一个中文字体文件比如DroidSansFallback.ttf放到项目目录然后在main.py里通过Label的font_name参数指定Label(text补货建议, font_namedata/fonts/DroidSansFallback.ttf)第二个坑是openpyxl的体积。我一开始把openpyxl和pandas都放进了requirements结果APK直接干到60MB而且pandas在Android上的python-for-android配方很重编译时间翻倍。后来发现这个工具只需要读Excelopenpyxl就够去掉pandas后APK降到35MB。所以打Android包时依赖越少越好能用标准库解决绝不引入第三方包。第三个坑是Android架构的选择。如果你只配置了arm64-v8a在旧手机上会直接装不上或崩溃。建议保留arm64-v8a和armeabi-v7a两个架构代价是APK稍微大一点但兼容性好很多。Kivy这条路的优缺点我后来总结得很清楚优点是纯Python、跨平台、社区资料多适合工具型小App缺点是构建时间长、界面非原生、打包体积偏大而且一旦用了比较冷门的Python包大概率要折腾配方。如果你的需求是“快速把现有Python脚本变成独立App”这是最不容易翻车的一条路。3. 方案二BeeWare/Briefcase Toga界面更接近原生的新选择3.1 BeeWare的定位把Python翻译成平台原生控件BeeWare和Kivy的思路完全不一样。Kivy是自己画界面而BeeWare坚持“用Python写一套API在Android上映射成Android原生控件在macOS上映射成AppKit控件”。也就是说你写的Toga按钮在Android手机上最终是被系统渲染成原生Button的。这个特性让它对“Android原生化”有天然吸引力。但Toga目前的问题也很明显控件数量比Kivy少很多。像表格、树形控件这些Toga虽然有但功能相对简陋复杂交互需要自己造轮子。我的库存工具界面只要一个按钮和一个列表用Toga来做就很合适这也算是我选它做实测的原因。3.2 Briefcase命令走一遍从新建工程到打包APKBeeWare的工程管理和打包工具叫Briefcase。它的使用体验比Buildozer更“正规”每一步都有明确的子命令。先安装pip install briefcase然后新建项目它会交互式问你项目名、应用名、包名等。我这边为了和Kivy版本对比项目名也叫StockToolbriefcase newBriefcase会生成一个标准Python项目结构并且自动创建一个Toga窗口程序。核心代码在src/stocktool/app.py里我改成了和Kivy版一样的库存计算界面import toga from toga.style import Pack from toga.style.pack import COLUMN, ROW from .stock_logic.calc import calc_suggestion def build(app): box toga.Box(stylePack(directionCOLUMN)) label toga.Label(补货建议列表, stylePack(padding10)) items toga.ListView(stylePack(flex1)) button toga.Button(加载Excel, on_presslambda widget: load_items(items), stylePack(padding10)) box.add(label) box.add(items) box.add(button) return box然后执行Android构建命令链briefcase create android briefcase build android briefcase run android第一次运行create时Briefcase会下载Android SDK和Gradle依赖网络不好又是个漫长的等待。构建完成后运行APK会自动安装到连接的设备或模拟器上。如果你想产出可分发APK用briefcase package android这个命令会在dist目录下生成正式安装包。整体流程比Buildozer更“标准化”报错信息也更友好但底层依赖Gradle在中国网络环境下经常卡在Gradle Distribution下载解决方案是手动把Gradle zip下载后放到~/.gradle/wrapper/dists对应目录里再重新跑build。3.3 和Kivy的实测对比界面确实原生但控件太少我的Toga版本实测数据APK体积约42MB冷启动约2.8秒内存峰值约100MB。启动比Kivy略慢半秒但界面上的按钮、列表都是Android原生风格观感清爽不会像Kivy那样一眼看上去是“游戏引擎界面”。Toga最大的坑是“看起来能用用起来要命”。我的列表展示需要点击按钮后动态更新数据Kivy只需要改ListAdapter的data属性而Toga的ListView更新机制比较原始费了好大劲才刷新成功。期间还遇到Toga的某个控件在Android API 33上有点击无响应问题查Issue才知道是已知bug升级到最新版才解决。如果你的界面就是表单加列表BeeWare能给你很好的体验但如果是复杂布局、手势、自绘控件现阶段我建议绕道。另外Toga的历史比Kivy短社区示例少遇到问题很多时候只能读源码。这个学习成本在项目排期里必须算进去。我的建议是界面简单且极度看重原生感选BeeWare界面复杂或对开发速度有要求选Kivy。4. 方案三Chaquopy把Python引擎塞进Android原生项目4.1 什么时候该用Chaquopy而不是整个App都用Python如果你的项目不是“把一个Python工具变成App”而是“我已经有一个Android原生App只想把某个Python算法模块移植进去”那么Kivy和BeeWare几乎可以直接划掉。这两者的思路都是整个应用用Python编写强行改造原生项目等于重写。这时候你需要的是Chaquopy。Chaquopy是一个Gradle插件它把完整的Python运行环境打包进Android应用并提供了Java/Kotlin与Python之间的互调接口。最典型的场景是你有一个训练好的机器学习模型或者一段复杂的文本处理算法用Python写很顺手但不想用Java重写一遍。用Chaquopy你就可以在原生Activity的点击事件里直接调用Python函数拿到计算结果后继续用原生UI展示。4.2 集成步骤Gradle配置与Python互调代码Chaquopy的集成非常依赖Android Studio因为它是Gradle生态的一部分。我用的是Android Studio Hedgehog版本AGP版本8.2。首先在项目的settings.gradle里加入仓库和插件地址pluginManagement { repositories { maven { url https://chaquo.com/maven } google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositories { maven { url https://chaquo.com/maven } } }然后在app模块的build.gradle里应用插件并配置plugins { id com.android.application id com.chaquo.python } android { defaultConfig { ndk { abiFilters arm64-v8a, armeabi-v7a } } } chaquopy { defaultConfig { version 3.10 pip { install openpyxl } sourceSets { main { python { srcDir src/main/python } } } } }Python源码放在src/main/python目录下。我依然是放同一个库存计算逻辑模块。Java这边调用就非常简单了import com.chaquo.python.Python; import com.chaquo.python.PyObject; Python py Python.getInstance(); PyObject module py.getModule(stock_logic.calc); PyObject result module.callAttr(calc_suggestion, excelPath); String suggestion result.toString();这里有几个必须注意的点。第一Python.start需要在Application或Activity的onCreate里先调用否则getInstance会报错。第二任何耗时操作都不能放在主线程Python计算虽然大部分时候很快但万一Excel文件很大一样会卡住UI。第三Chaquopy对minSdkVersion有要求太低的版本不支持实测至少要API 21起步。4.3 性能与包体积实测Python嵌入到底值不值我把同一个Excel文件分别放在Java层用原生方式解析、以及通过Chaquopy调用Python解析对比结果更直观。Chaquopy的冷启动加载Python运行时约0.8秒之后的函数调用单次约50毫秒这个性能对绝大多数业务逻辑完全够用。代价是APK体积增加了约18MB这是Python运行时的必要开销。如果项目本身就五六MB增加18MB可能有点肉疼但如果你的核心算法用Java重写需要两周这个体积换开发效率我认为非常值。实测中最容易踩坑的是pip依赖。Chaquopy要求安装的Python包必须能在Android的ABI上编译运行。纯Python包大多没问题但带C扩展的包就要看运气了。我在测试过程中尝试装过一个第三方库Gradle构建直接报找不到合适的预编译wheel最后放弃了那个依赖改用纯Python实现。所以用Chaquopy前最好先看看核心逻辑用的第三方包是否支持Android。另外Chaquopy不是让你用来写UI的。它的UI还需要用Java/Kotlin写Python只作为算法引擎存在。如果你想着“那我全部逻辑用Python写UI用Kotlin画”那确实可以但别把Python当主语言来架构整个App否则跨语言通信代码会变得非常难维护。5. 方案四Termux Flask WebView不打包也能交付的“旁门左道”5.1 Termux本质Android上的Linux用户空间不是虚拟机在所有方案里Termux是门槛最低的一个。它不是模拟器也不是虚拟机而是在Android的应用层直接提供了一个Linux用户空间环境你可以通过包管理器安装Python、Git、openssh等工具。打开Termux后跑这样几行命令就能得到一个完整的Python运行环境pkg update pkg upgrade pkg install python pip install flask openpyxl对很多Python开发者来说这可能是最快能在手机上跑起自己脚本的方法。但代价也很明显——普通用户大概率不知道Termux是什么更不可能自己去装包、运行命令行。所以Termux方案只适合自己用、演示给同事看或者做临时原型验证绝对不适合作为产品交付。5.2 用Flask起本地服务再用WebView壳包一层我的做法是让Python脚本变成Flask服务跑在手机本地端口上然后通过浏览器访问。具体来说在stock_tool目录下写一个app.pyfrom flask import Flask, render_template_string, request from stock_logic.calc import calc_suggestion app Flask(__name__) app.route(/) def index(): return render_template_string(form...button typesubmit加载Excel/button/form) app.route(/calc, methods[POST]) def calc(): result calc_suggestion(data/inventory.xlsx) return str(result) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)然后在Termux里运行python app.py在手机浏览器里打开localhost:5000就能看到页面。如果你希望它看起来像一个App可以再用Android Studio花几分钟建一个极简WebView壳加载http://localhost:5000并处理一下网络权限和页面自适应。我实测这个方案Flask服务启动约3秒内存约占60MB简单计算接口耗时20毫秒。由于页面是Web渲染UI想做成什么样都行这是它最大的优势。5.3 这个方案的两大硬伤必须提前说清硬伤一是服务生命周期。Flask进程在Termux里跑着只要手机息屏或者系统把Termux进程杀了服务就断了。你需要保持Termux在前台或者用termux-wake-lock保证持续运行这会增加耗电就算保住进程普通用户也不会愿意为了用你的工具得先忍受一个终端黑框常驻在后台。硬伤二是分发路径。你只能把Termux APK、你的Python代码、还有安装步骤一起发给对方对方要按步骤装、跑、开浏览器。这已经不是“交付App”而是“教用户搭服务器”。所以我在用完之后只把它当作快速验证工具——先把业务逻辑跑通、UI原型定下来然后再决定用Kivy还是Chaquopy做正式版。如果你真的只是想自用它无可替代地方便。6. 横向对比与最终选型建议6.1 四个方案关键指标汇总四条路都跑完之后我把关键指标整理成下面这张表你可以直接用来对照选择。对比维度Kivy BuildozerBeeWare TogaChaquopyTermux FlaskApp形态独立APK独立APK嵌入原生App无APK浏览器/WebViewUI风格自绘非原生原生控件原生控件Java/KotlinWeb页面APK体积约35MB约42MB原APK 18MB不涉及冷启动耗时约2.1秒约2.8秒Python引擎约0.8秒服务启动约3秒构建复杂度高依赖Buildozer/NDK中依赖Gradle中依赖Gradle/Chaquopy低无需构建第三方Python包支持一般要配配方一般依赖包能否编译到Android基本都行适合用户内部工具/个人开发者界面简单的工具型App已有原生App想复用算法自用原型/演示6.2 按你的项目现状做选择如果你是从零开始、没有Android原生开发背景、目标就是把Python脚本变成独立App我个人最推荐Kivy。它的社区文档最全遇到问题基本能搜到答案坑虽然多但都被前人踩得差不多了。如果界面元素很少只有几个按钮和列表而且你希望它看起来更“正统Android”可以优先试BeeWare但要做好控件功能欠缺导致临时改方案的心理准备。如果你已经有一个Android Studio工程只是想把某个Python算法模块嵌进去不要犹豫直接用Chaquopy。它不会帮你的UI做任何加分项但它是唯一能让原生项目和Python代码优雅共存的方案。相比之下Kivy和BeeWare都要求你的整个App以Python为核心这在现有原生项目里几乎不可行。如果你只是临时给一个小工具做验证或者这个工具只给你自己用Termux Flask绝对是最节省时间的。你可以在1小时内从零跑通全部业务逻辑然后等需求稳定了再去做正式打包。我自己现在遇到任何Python工具的“Android化”需求第一件事都是先在手机Termux里把脚本跑起来再考虑要不要打包。6.3 心得提前把核心逻辑和UI彻底分离是唯一能让你少加班的“银弹”这四条路线跑下来我最大的感触是无论你最终选了哪个方案在动手之前一定要把Python代码拆成“纯计算逻辑”和“界面胶水层”两部分。我因为一开始偷懒把文件读取、按钮点击、结果展示全部写在一个脚本里导致后来试Chaquopy时不得不花一个晚上把计算函数从命令行参数耦合中拆出来。如果一开始就定义好calc_suggestion(excel_path)这样一个与UI无关的函数三条路线的迁移都会轻松很多。具体建议是把所有业务代码写成一个纯Python包输入输出尽量用基本类型str、int、list、dict不要依赖任何GUI框架的类。这样Kivy、BeeWare、Chaquopy甚至Flask都能直接import同一个模块。我在最终版里就是把stock_logic这个包原封不动地用在了四条路径上接不同UI时只改界面代码不用动算法。最后再分享一个小技巧任何方案在正式投入前先花半天时间做一个“最小冒烟测试”——就是写一个HelloWorld级别的小App完整跑一遍你预想的打包环境和运行流程再开始迁移完整代码。因为Buildozer下载环境、Chaquopy配置Gradle这些环节很容易卡住如果等到完整代码写完再处理你会同时面对“代码bug”和“构建bug”两堆问题。先跑通空壳再往里填业务逻辑整个过程会顺畅得多。Python项目移植Android本来就没有一步到位的万能方案但只要你按使用形态选对路线再留好拆分的余地这条路远比想象中靠谱。