在Android应用中通过Chaquopy使用Python [译]

通过Python脚本和包为Android应用带来更多的功能

翻译自https://proandroiddev.com/chaquopy-using-python-in-android-apps-dd5177c9ab6b

欢迎通过我的Blog访问此文章.

Python在开发者社区中时最受欢迎的语言之一, 因为其简单,健壮并且有着庞大的软件生态使其可以在多个领域发挥作用. 类似NumPy和SciPy这样的包允许你在项目中使用高等数学计算, 而这样的计算在其它的语言中是无法简单实现的. 那么如果将Python引入到Android应用中又会带来什么样效果呢?

Chaquopy是一个可以帮助开发者通过Java/Kotlin在Android平台中运行Python脚本的框架. 和其它跨语言库不同, 它不再有NDK依赖的烦恼, 也不需要native code[1], 并且安装也十分的简单. 在这篇文件中, 我们将探索Chaquopy, 并通过Kotlin来进行构建和使用.

Chaquopy是什么? 它是如何让Python在Android中运行的?

和大多数跨语言接口工作原理一样, Python和Android都有着C/C++的血统, 使其可以通过中间件来进行通信. Android的NDK允许开发者使用通过C/C++编写的本地库, 来帮助Android应用获得更好的图形和科学计算效果.

Chaquopy使用CPython, 一个通过C来实现的Python实现. 不同于一般的误解, Python并不是一个纯粹的解释性语言. Python的源码最开始会被构建为可以被CPython执行的特殊字节码. 当然, CPython只是Python的几种解释器之一, 其它的还有PyPy, IronPython, Jython等等.

Chaquopy通过Android NDK工具链来构建CPython, CPython在项目构建的时候通过Chaquopy Gradle插件从Maven仓库中心进行下载, 在这个过程中用户并不需要下载NDK. 它还下载Chaquopy运行支持通过JNI将Java/Kotlin和Python连接起来.

同时, 我们还需要Python包管理工具pip, 它可以下载为解释器下载包. 像NumPySciPy这样的受欢迎的包可以通过原生代码执行高密集的CPU计算, 我们需要事先安装这些包. 因此, Chaquopy团队维护了自己的存储库,其中包含专门为Android的ARM架构构建的本地软件包. 这些软件包的维护者不会为Android平台构建他们的本地代码,因为用户数量较少,因此Chaquopy团队会针对Android平台构建它们并通过自己的存储库进行发布.

对于纯粹的Python包, 不需要额外的构建并且Chaquopy可以直接运行这些.从更宏观来看, Chaquopy包含了三个主要的组件.

  1. Chaquopy Gradle Plugin
  2. Chaquopy Runtime
  3. Package Repository

1.在Android项目中添加Chaquopy

1.1 Gradle依赖和ABI规则

在新/现有的Android项目中添加Chaquopy, project级的build.gradle文件的顶部, 我们定义项目的plugin并且添加Chaquopy的Gradle插件.

plugins {
id 'com.android.application' version '7.4.2' apply false
id 'com.android.library' version '7.4.2' apply false
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
id 'com.chaquo.python' version '13.0.0' apply false
}

下一步, 我们在module级的build.gradle文件中添加Chaquopy plugin和指定ABI规则,

plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.chaquo.python'
} android {
...
defaultConfig {
...
ndk {
abiFilters "armeabi-v7a" //, "arm64-v8a", "x86", "x86_64"
}
}
...
}

正如官方文档提及的, Python解释器是使用Android NDK来构建的本机组件,NDK为指定的版本构建原生代码, 比如like arm,x86或x86_64. 不同的设备支持不同的架构. 所以我们只能包含特定版本的Python解释器, 而不是为所有架构都进行构建, 因为这会增加应用程序的大小. Android官方文档中是这么说的,

构建系统的默认行为是将每个ABI的二进制文件包括在单个APK也称为胖 APK)内. 与仅包含单个ABI的二进制文件的APK相比,胖APK要大得多, 要权衡的是兼容性更广,但APK更大. 强烈建议您利用app bundle和APK拆分减小 APK的大小,同时仍保持最大限度的设备兼容性.

1.2 Python版本和PIP包

下一步, 我们将配置Python构建版本, 我们可以通过修改module级build.gradle文件来指定版本.

plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.chaquo.python'
} android {
...
defaultConfig {
...
ndk {
abiFilters "armeabi-v7a" //, "arm64-v8a", "x86", "x86_64"
}
python {
version "3.10"
}
}
...
}

不同的Chaquopy支持不同的Python版本有着不同的最小API需要. 通过这个表可以查找到你需要匹配的版本. 下一步, 我们指定在Python解释器需要的包的版本.

defaultConfig {
python {
pip {
// A requirement specifier, with or without a version number:
install "scipy"
install "requests==2.24.0" // An sdist or wheel filename, relative to the project directory:
install "MyPackage-1.2.3-py2.py3-none-any.whl" // A directory containing a setup.py, relative to the project
// directory (must contain at least one slash):
install "./MyPackage" // "-r"` followed by a requirements filename, relative to the
// project directory:
install "-r", "requirements.txt"
}
}
}

这是在Chaquopy中安装包的几种不同方法. 它可以是具有特定版本的包名, 也可以是自定义包或者requirement.txt包列表.

通过Java/Kotlin获取Python对象

在Python中,我们使用属于Python模块的函数或者数据成员, 一个Python模块包含.py文件. 要使用任何Python模块中的成员. 第一步是将Python源代码放入<project>/app/src/main/python文件夹中.

# Contents of my_module.py

import numpy as np

def get_exec_details():
return __file__ def sumOp( nums ):
return sum( nums ) def powOp( a , x ):
return a**x def npMatrixSum( m , n ):
mat = np.ones( ( m , n ) )
mat_sum = np.sum( mat , axis=1 )
return mat_sum class Operations: num_ops = 2 def meanOp( self , nums ):
return sum( nums ) / len( nums ) def maxOp( self , nums ):
return max( nums ) nums_len = 10
nums_len_str = "ten"
ops = Operations()

为了使用my_module中的成员, 我们需要使用Python.getModule方法传递模块的名称. 在这之前, 我们需要运行Python在应用中被允许, 这可以在Application的onCreate方法中执行,

class App : Application() {

    override fun onCreate() {
super.onCreate()
if( !Python.isStarted() ) {
Python.start( AndroidPlatform( this ) )
}
} }

App添加到 AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> <application
android:name=".App"
...
</application> </manifest>

那么在MainActivity中, 我们就可以使用Python.getInstance(否则的话会出现PyException异常),

val py = Python.getInstance()
val module = py.getModule( "my_module" )

访问变量(数据成员)

为了使用数据成员, 像my_module.py中的nums_len,

val numsLength = module[ "nums_len" ]?.toInt()
println( "Nums Length is $numsLength" )
Nums Length is 10

访问对象ops类中的属性

val ops = module[ "ops" ]!!
println( "Operations: $ops" )
println( "num_ops : ${ ops[ "num_ops" ] }" )
println( "mean func : ${ ops[ "meanOp" ] }" )
Operations: <my_module.Operations object at 0xb9339ce8>
num_ops : 2
mean func : <bound method Operations.mean of <my_module.Operations object at 0xb9339ce8>>

执行方法

由于Python中的函数是对象, 因此允许将函数作为模块的值进行访问.然后,我们使用PyObject.call方法来向函数传递参数并获取结果(如果函数返回一个值).

val sumFunc = module[ "sumOp" ]
val sum = sumFunc?.call( intArrayOf( 12 , 25 , 32 ) )
val powFun = module[ "powOp" ]
val pow = powFun?.call( 5 , 2 )
println( "Sum: $sum" )
println( "Pow: $pow" )
Sum: 69
Pow: 25

要从ops对象访问成员函数,

val meanFunc = ops[ "meanOp" ]
val mean = meanFunc?.call( intArrayOf( 23 , 45 , 12 , 91 ) )
println( "Mean: $mean" ) // OR val mean = ops.callAttr( "meanOp" , intArrayOf( 23 , 45 , 12 , 91 ) )
println( "Mean: $mean" )
Mean: 42.75

这是一个示例, 其中Python函数使用numpy并返回类型为np.ndarray的结果

# my_module.py
import numpy as np def npMatrixSum( m , n ):
mat = np.ones( ( m , n ) )
mat_sum = np.sum( mat , axis=1 )
return mat_sum val npSumFunc = module[ "npMatrixSum" ]
val output = npSumFunc?.call( 2 , 3 ) // OR val output = module.callAttr( "npMatrixSum" , 2 , 3 ) println( "Output: $output" )
println( "Output shape: ${output!![ "shape" ] }")
Output: [3. 3.]
Output shape: (2,)

最后

希望我为您的Android开发工具箱添加了一个新工具! Chaquopy是一个非常好用的工具, 具有清晰明了的语法和无需费心安装的优点.你可以在下一个Android项目中使用它.继续学习,祝您度过愉快的一天!

完成代码可以访问我的GitHub


  1. 没能完全理解这里的native code具体代指的是什么, 但是我觉得这里的意思是不需要在Android应用中引入C/C++代码, 也就是说不需要在Android应用中引入NDK.

在Android应用中通过Chaquopy使用Python的更多相关文章

  1. android模拟器(genymotion)+appium+python 框架执行基本原理(目前公司自己写的)

    android模拟器(genymotion)+appium+python 框架执行的基本过程: 1.Push.initDate(openid)方法     //业务数据初始化 1.1   v5db.p ...

  2. android开发中的5种存储数据方式

    数据存储在开发中是使用最频繁的,根据不同的情况选择不同的存储数据方式对于提高开发效率很有帮助.下面笔者在主要介绍Android平台中实现数据存储的5种方式. 1.使用SharedPreferences ...

  3. Android 上多方式定位元素(python)

    Android 上多方式定位元素(python) 在学习实际UI自动化测试的时候,首先就需要定位元素,然而定位元素也是最头疼的地方,因为元素各种控件名称的缺失会影响元素的准确定位.下面针对Androi ...

  4. Android 开发中 SQLite 数据库的使用

    SQLite 介绍 SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能.此外它还是开源的,任何人都可以使用它.许多开源项目((Mozilla, PHP, ...

  5. 在Android 开发中使用 SQLite 数据库笔记

    SQLite 介绍   SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能.此外它还是开源的,任何人都可以使用它.许多开源项目((Mozilla, PH ...

  6. 从Android设备中提取内核和逆向分析

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/57074695 一.手机设备环境 Model number: Nexus 5 OS ...

  7. Android学习探索之Java 8 在Android 开发中的应用

    前言: Java 8推出已经将近2年多了,引入很多革命性变化,加入了函数式编程的特征,使基于行为的编程成为可能,同时减化了各种设计模式的实现方式,是Java有史以来最重要的更新.但是Android上, ...

  8. Stack Overflow 排错翻译 - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder

    Stack Overflow 排错翻译  - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder 转自:ht ...

  9. Google官方关于Android架构中MVP模式的示例续-DataBinding

    基于前面的TODO示例,使用Data Binding库来显示数据并绑定UI元素的响应动作. 这个示例并未严格遵循 Model-View-ViewModel 或 Model-View-Presenter ...

  10. android studio 中移除module和恢复module

    一.移除Android Studio中module 在Android Studio中想要删除某个module时,在Android Studio中选中module,右键发现没有delete,如图: An ...

随机推荐

  1. impdp,depdp 常用参数

    转载于:https://www.cnblogs.com/halberd-lee/p/7807032.html 1 导数据注意事项 检查数据库版本(用于决定导出时生成为哪个版本的dmp头文件) sele ...

  2. 5、MySQL中的锁

    1.6. MySQL中的锁 InnoDB中锁非常多,总的来说,可以如下分类: 这些锁都是做什么的?具体含义是什么?我们现在来一一学习. 1.6.1.解决并发事务问题 我们已经知道事务并发执行时可能带来 ...

  3. Hadoop编程——Java编写MapReduce:WordCount案例

    一.MapReduce简介 MapReduce是一种面向大数据平台的分布式并行计算框架,它允许使用人员在不会分布式并行编程的情况下,将程序运行在分布式系统上.它提供的并行计算框架,能自动完成计算任务的 ...

  4. SpringBoot测试类注入Bean失败原因

    首先针对SpringBoot的测试类,2.2版本之前和2.2版本之后是不一样的,在2.2版本之前需要贴注解@SpringBootTest和@RunWith(SpringRunner.class)需要在 ...

  5. SpingBoot面试大汇总

    1.什么是SpringBoot? 1)用来优化Spring应用的初始搭建以及开发过程,使用特定的方式来配置(properties和yml文件) 2)嵌入式的内置服务器tomcat无需部署war文件简化 ...

  6. redis远程连接错误SocketTimeoutException: connect timed out

    redis远程连接错误SocketTimeoutException: connect timed out 解决方案: 编辑redis.conf文件(建议将其 先备份 编辑备份文件 启动redis服务时 ...

  7. 【内存管理】ION内存管理器浅析(system heap)(基于linux 4.14)

    什么是ION ION具体不知道是什么的缩写,只知道是android系统上google引入的内存管理方式,为了实现用户与内核间数据共享时零拷贝.多用于多媒体,比如camera和display,graph ...

  8. SQLServer数据库执行时间

    with kk AS( SELECT TOP 2000 total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution_count [运行次数], qs.tot ...

  9. 什么是js严格模式?

    (use strict)严格模式是一种将更好的错误检查引入代码中的方法. 在使用严格模式时,无法使用隐式声明的变量.将值赋给只读属性或将属性添加到不可扩展的对象等. 1. 严格模式的目的 1) 消除J ...

  10. WPF中的DataGrid支持Excell粘贴数据

    其中XMAL文件中的表格样式: <DataGrid x:Name="DataListView" AutoGenerateColumns="False" H ...