Android 调用 Python 的核心思想是:在 Android 设备上运行一个 Python 环境,并通过某种通信机制(如 Socket、HTTP、gRPC)让 Android 应用(Java/Kotlin)与 Python 脚本进行数据交换和指令传递。
下面我将详细介绍几种主流的实现方法,从最简单到最完整,并给出各自的优缺点和适用场景。
使用 Termux(最简单,适合学习和原型验证)
这种方法利用 Android 上的终端模拟器 Termux 来运行 Python 脚本,然后通过 Android 的 Runtime.exec() 命令行调用。
原理
- 安装 Termux:在 Android 设备上安装 Termux 应用,它提供了一个 Linux 终端环境。
- 安装 Python:在 Termux 中使用包管理器安装 Python。
- 编写 Python 脚本:编写一个可以接受命令行参数或从标准输入读取数据的 Python 脚本。
- Android 调用:在 Android App 中,通过
ProcessBuilder或Runtime.exec()执行一个 shell 命令来启动 Termux 中的 Python 脚本,并传递数据。
步骤详解
-
在 Android 设备上安装 Termux
- 从 F-Droid 或 Google Play Store 安装 Termux。
- 打开 Termux,更新包列表并安装 Python:
pkg update pkg install python
-
编写 Python 脚本
-
在 Termux 的家目录下创建一个脚本,
~/my_script.py。 -
这个脚本可以从命令行参数中读取数据,或者从标准输入读取数据。
-
示例 (从参数读取):
# my_script.py import sys if len(sys.argv) > 1: # 从第二个参数开始获取数据 input_data = " ".join(sys.argv[1:]) print(f"Hello from Python! Received data: {input_data}") # 做一些处理,比如反转字符串 result = input_data[::-1] print(f"Processed result: {result}") else: print("No arguments provided.") -
示例 (从标准输入读取):
# my_script_stdin.py import sys # 读取一行标准输入 input_data = sys.stdin.readline() print(f"Hello from Python! Received data: {input_data.strip()}") # 做一些处理,比如计算字符串长度 result = len(input_data) print(f"Processed result (length): {result}")
-
-
在 Android App (Kotlin) 中调用
-
使用
ProcessBuilder来构建并执行命令。 -
重要:你需要获取 Termux 的可执行文件路径,通常在
/data/data/com.termux/files/usr/bin/。 -
调用
my_script.py(带参数):import android.os.Process import java.io.BufferedReader import java.io.File fun runPythonScriptWithArgs() { val pythonExecutable = "/data/data/com.termux/files/usr/bin/python" val scriptPath = "/data/data/com.termux/files/home/my_script.py" val dataToSend = "Hello Android!" // 检查文件是否存在 if (!File(pythonExecutable).exists() || !File(scriptPath).exists()) { // 处理错误,例如文件未找到 return } try { // 使用 ProcessBuilder 构建命令 // 注意:命令和参数需要是分开的字符串 val process = ProcessBuilder() .command(pythonExecutable, scriptPath, dataToSend) .redirectErrorStream(true) // 合并错误流和输出流 .start() // 读取 Python 脚本的输出 val reader = BufferedReader(process.inputStream) val output = StringBuilder() var line: String? while (reader.readLine().also { line = it } != null) { output.append(line).append("\n") } // 等待进程结束 val exitCode = process.waitFor() if (exitCode == 0) { // 成功,处理输出 println("Python script output:\n$output") } else { // 失败,处理错误 println("Python script exited with code $exitCode") } } catch (e: Exception) { e.printStackTrace() } } -
调用
my_script_stdin.py(通过管道):fun runPythonScriptWithStdin() { val pythonExecutable = "/data/data/com.termux/files/usr/bin/python" val scriptPath = "/data/data/com.termux/files/home/my_script_stdin.py" val dataToSend = "Hello Android from stdin!" try { val process = ProcessBuilder() .command(pythonExecutable, scriptPath) .redirectErrorStream(true) .start() // 向进程的标准输入写入数据 val outputStream = process.outputStream outputStream.write((dataToSend + "\n").toByteArray()) // 写入数据并换行 outputStream.close() // 读取输出 val reader = BufferedReader(process.inputStream) val output = StringBuilder() var line: String? while (reader.readLine().also { line = it } != null) { output.append(line).append("\n") } val exitCode = process.waitFor() if (exitCode == 0) { println("Python script output:\n$output") } else { println("Python script exited with code $exitCode") } } catch (e: Exception) { e.printStackTrace() } }
-
优点
- 简单快捷:无需复杂的编译和配置,几分钟即可上手。
- 轻量级:不需要完整的 Linux 内核。
- 灵活性高:可以方便地使用任何 Python 库。
缺点
- 依赖 Termux:用户设备上必须安装并打开 Termux。
- 性能差:通过进程间通信和文本解析,不适合高频、大数据量的调用。
- 用户体验差:后台运行一个终端进程,可能会被系统杀死,且不优雅。
- 不安全:直接暴露了命令行接口,容易被恶意调用。
使用 Chaquopy(最推荐,用于生产环境)
Chaquopy 是一个商业插件(有免费版和付费版),它允许你直接在 Android Studio 项目中集成 Python 解释器,这是目前最成熟、最方便的解决方案。
原理
Chaquopy 将 Python 解释器(CPython)编译为 Android 的原生库(.so 文件),并将其打包到你的 APK 中,它提供了一个 Java/Kotlin API 来加载 Python 模块,并自动处理数据类型转换(如 Java List -> Python list)。
步骤详解
-
在
build.gradle(Module: app) 中添加依赖plugins { id 'com.chaquo.python' version '15.0.0' // 使用最新版本 } android { // ... defaultConfig { // ... python { // 指定 Python 版本 version "3.8" // 指定要打包的 Python 模块或文件 pip { // 安装第三方库,numpy install "numpy" // 或者指定你自己的本地库路径 // sourceSets { // main { // python.srcDir "src/main/python" // } // } } } } } -
创建 Python 模块
-
在
src/main/目录下创建一个python文件夹。 -
在
python文件夹中创建你的 Python 模块,my_module.py。# src/main/python/my_module.py def process_data(data_list): """一个简单的函数,接收一个列表,返回处理后的列表""" print(f"Python received: {data_list}") # 假设我们想对列表中的每个数字求平方 result = [x * x for x in data_list] print(f"Python returning: {result}") return result class MyPythonClass: def __init__(self, name): self.name = name def greet(self): return f"Hello from {self.name} in Python!"
-
-
在 Kotlin 代码中调用
-
在 Activity 或其他地方,通过 Chaquopy 提供的 API 调用 Python 代码。
-
初始化 Python 环境:在 Application 类或 Activity 的
onCreate中。class MyApplication : Application() { override fun onCreate() { super.onCreate() // 初始化 Chaquopy Chaquopy.start(this) } } -
调用 Python 函数:
import com.chaquo.python.Python import com.chaquo.python.android.AndroidContext class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 获取 Python 实例 val py = Python.getInstance() // 获取 Python 模块 val myModule = py.getModule("my_module") // 调用 Python 函数,并传递数据 val inputData = listOf(1, 2, 3, 4, 5) // Chaquopy 会自动进行类型转换 val processedData = myModule.callAttr("process_data", inputData).toJava(List::class.java) println("Kotlin received from Python: $processedData") // 输出: [1, 4, 9, 16, 25] // 调用 Python 类 val pythonClass = myModule.get("MyPythonClass").call("Alice") val greeting = pythonClass.callAttr("greet").toString() println(greeting) // 输出: Hello from Alice in Python! } }
-
优点
- 无缝集成:直接在 Android Studio 中开发,像调用 Java/Kotlin 代码一样调用 Python。
- 高性能:Python 代码作为本地库运行,IPC 开销极小。
- 类型安全:自动进行数据序列化和反序列化,无需手动处理 JSON 或文本。
- 打包简单:Python 依赖和代码会自动打包进 APK,用户无需额外安装任何东西。
- 支持 PyPI:可以方便地通过
pip安装绝大多数 Python 库。
缺点
- 商业软件:免费版有功能限制(例如不能打包 Python 标准库之外的模块),完整功能需要付费。
- APK 体积增大:打包 Python 解释器和依赖库会使 APK 文件体积显著增加(几十MB)。
- 平台限制:目前主要支持 Android 和 iOS。
使用 gRPC 或 Socket 通信(最灵活,适合复杂应用)
这种方法的核心是将 Python 代码作为一个独立的服务在后台运行,Android 应用通过网络(本地 Socket 或 HTTP)与该服务通信。
原理
- Python 服务端:编写一个 Python 脚本,使用
Flask(HTTP) 或grpc(gRPC) 框架暴露一个 API 接口。 - Android 客户端:在 Android App 中,使用网络库(如 OkHttp, Retrofit for HTTP;或 gRPC Android 库)向 Python 服务端发送请求并接收响应。
步骤详解
-
Python 服务端 (使用 Flask 示例)
-
安装 Flask:
pip install Flask -
创建
server.py:from flask import Flask, request, jsonify import numpy as np app = Flask(__name__) @app.route('/process', methods=['POST']) def process(): data = request.json numbers = data.get('numbers', []) # 使用 numpy 进行计算 arr = np.array(numbers) squared = arr ** 2 return jsonify({ 'status': 'success', 'original': numbers, 'squared': squared.tolist() # numpy array 转为 list }) if __name__ == '__main__': # 在 0.0.0.0 上监听,以便从其他设备访问 # 5001 是一个常用的非特权端口 app.run(host='0.0.0.0', port=5001) -
如何运行:
- 在开发机上:直接
python server.py。 - 在 Android 设备上:需要一种方式让 Python 脚本持续运行,可以使用
Termux+nohup或systemd服务,或者更复杂的方案如Pyodide(WebAssembly) 运行在 WebView 中。
- 在开发机上:直接
-
-
Android 客户端 (使用 Retrofit + OkHttp 示例)
-
添加依赖:
// build.gradle implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
-
定义 API 接口:
import retrofit2.Call import retrofit2.http.Body import retrofit2.http.POST data class ProcessRequest(val numbers: List<Int>) data class ProcessResponse(val status: String, val original: List<Int>, val squared: List<Int>) interface ApiService { @POST("/process") fun processData(@Body request: ProcessRequest): Call<ProcessResponse> } -
创建 Retrofit 实例并发起请求:
val retrofit = Retrofit.Builder() .baseUrl("http://127.0.0.1:5001/") // Python 服务运行在本地 .addConverterFactory(GsonConverterFactory.create()) .build() val apiService = retrofit.create(ApiService::class.java) val request = ProcessRequest(listOf(1, 2, 3, 4, 5)) apiService.processData(request).enqueue(object : Callback<ProcessResponse> { override fun onResponse(call: Call<ProcessResponse>, response: Response<ProcessResponse>) { if (response.isSuccessful) { val result = response.body() println("Received: ${result?.squared}") // 输出: [1, 4, 9, 16, 25] } else { println("Error: ${response.errorBody()}") } } override fun onFailure(call: Call<ProcessResponse>, t: Throwable) { println("Network failure: ${t.message}") } })
-
优点
- 高度解耦:Android 端和 Python 端可以独立开发、部署和更新。
- 技术栈灵活:Python 端可以使用任何 Web 框架,Android 端可以使用任何网络库。
- 可扩展性强:可以轻松将服务部署到远程服务器,实现真正的客户端-服务器架构。
- 适合复杂逻辑:非常适合将计算密集型任务卸载到云端或本地服务器。
缺点
- 架构复杂:需要处理网络通信、错误处理、序列化等。
- 延迟:网络通信会引入额外的延迟。
- 需要持续运行的服务:在 Android 设备上维持一个后台 Python 服务的运行比较麻烦。
总结与选择建议
| 方法 | 核心原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Termux | 通过命令行调用后台进程 | 简单、快速、轻量 | 性能差、依赖Termux、体验不佳 | 学习、原型验证、个人工具 |
| Chaquopy | 将Python解释器打包进APK | 无缝集成、高性能、类型安全 | APK体积大、商业软件(有免费版) | 生产环境、App集成、需要高性能调用 |
| gRPC/Socket | 网络通信(客户端-服务器) | 架构灵活、可扩展、技术栈自由 | 架构复杂、有网络延迟、需后台服务 | 复杂应用、微服务、云端计算、将Python作为独立服务 |
如何选择?
- 如果你是初学者,只是想快速验证一个想法:使用 Termux,它能让你最快地跑通流程。
- 如果你正在开发一个商业 App 或一个功能完善的应用,需要将 Python 逻辑深度集成进去:强烈推荐 Chaquopy,它的便利性和性能优势远超其他方法,是目前 Android 集成 Python 的工业级标准。
- 如果你的应用架构复杂,或者 Python 逻辑需要运行在远程服务器上:使用 gRPC/Socket 通信,这是一种更通用、更灵活的架构模式,尤其适合跨设备或跨平台的服务调用。
