我的编程空间,编程开发者的网络收藏夹
学习永远不晚

不懂这几点就落后了:Android、Py

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

不懂这几点就落后了:Android、Py

Android 平台的Python:

(本文以Python3为例,Python3是未来,大家都懂的)
Python作为一个功能强大又语法简洁的语言,其应用已无需多言。要想在Android平台运行起Python,也有方案实现,其实质就是在Android系统上搭建Python环境。对此Google已经提供了SL4A(Scripting Layer for Android )方案,支持多种脚本语言,除此之外,还可以使用一个叫QPython的app,可以直接在Android上编写以及运行Python代码。但其实意义不大,写好的Python代码并不是以一个独立的app进程运行的,只不过是在QPython这个应用中运行而已。这两者都不符合我现在要讨论的东西,如题,笔者想要讨论的是如何在Android平台使用Java与Python代码相互调用,换言之,就是如何在Android工程中嵌入一个Python解释器。

首先谈一点,为什么要在Android平台使用Python?Python拥有众多强大的第三方库和框架,在机器学习、大数据处理等诸多方面都有不俗的应用。另外,就语法而言,Python比Java更加简洁,同时又功能强大,既可面向过程亦可面向对象,而不像Java一样,是一种纯粹的面向对象语言,哪怕打印一句话也需要先创建类。Python作为一种脚本语言,可以边解释边执行,而不需编译,另外Python中存在的元类,可以使我们动态的创建类,如此可以在不需要重新编译安装apk的情况下,动态的由远程服务端为Android项目添加功能。我们还可以将Python已有的一些东西移植到Android平台,例如tornado、django等,总之玩法多多。

在Android平台,官方并不支持直接使用Python开发app,基于虚拟机的Java(或kotlin)才是更好的选择,其他语言是无法自如的使用官方Framework提供的api的,尤其是在程序界面的表现上,典型的反例就是kivy。什么是kivy,可自行了解,但要解决Android平台上Java与Python的交互,kivy确实是一个方向,而且是一个醍醐灌顶的方向。kivy实际上已经解决我们需要实现的目的,模仿Android平台上的kivy实现机制即可。但是,kivy使用的是Cython解释器,非CPython解释器,需要学习Cython语法,并且在其他一些方面存在一些限制。kivy给我们提供的思路就是借助Java的jni机制,实现Python与Java的交互。即在一个安卓apk工程中包含一个cython.so解释器,通过jni机制调用解释器去解释执行Python代码,通过Java调C,C调Python实现交互。有一点需要说明,Python作为一门胶水语言,Python与C的交互是非常方便的,因此才能实现这一系列调用。

也为大家推荐了技术教程:

Android视频编码和直播推流

Android高手进阶

一天掌握Scrapy爬虫框架

【Python全栈】网络爬虫体验课

Django Web框架/Python最牛框架

探究Linux的总线、设备、驱动模型

Linux 实用讲解+实操+面试题

zabbix企业实战应用

Linux视频教程

关于该种方案,已有国外网友实践,原理如下

这里写图片描述

链接地址

除此之外,本博客将通过另外两种方案实现。其中第一种类似上述方案,但集成CPython解释器,非Cython,因此需要掌握如何实现Python与C的交互。

Python与C交互基础
C调用Python

简单使用
流程:

初始化Python解析器
执行Python代码,字符串,对象或模块。
关闭Python解析器。
创建一个.c源文件,代码如下,创建一个pytest.py文件,实现一个printTime函数

#include<Python.h>
int main()
{
Py_Initialize();//初始化Python解析器
if (!Py_IsInitialized())
{
printf("Initialize failed");
return -1;
}
PyRun_SimpleString("print('hello C !')");
PyRun_SimpleString("import pytest");
PyRun_SimpleString("pytest.printTime()");
Py_Finalize();/关闭Python解析器
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意:除了用PyRun_SimpleString函数直接运行代码,还可以使用PyRun_SimpleFile函数运行一个Python脚本
原型:PyRun_SimpleFile(FILE fp, const char filename) ,由于版本差异,使用该方式可能会造成崩溃,推荐另一种替代方式
PyRun_SimpleString(“execfile(“test.py”)”)

调用Python函数
pytest.py

import time

def printTime():
print('invoke printTime:'+str(time.time()))
return (1,)#元组只有一个元素时,需在末尾加逗号
1
2
3
4
5
C 代码

int main()
{
PyObject module_name,module,func,dic;
char fun_name = "printTime";//需调用的Python函数名
PyObject
resultValue;

Py_Initialize();
if (!Py_IsInitialized())
{
    printf("Initialize failed");
    return -1;
}

//导入Python 模块并检验
module_name = Py_BuildValue("s", "pytest");
module = PyImport_Import(module_name);

if (!module)
{
    printf("import test failed!");
    return -1;
}

//获取模块中的函数列表,是一个函数名和函数地址对应的字典结构
dic = PyModule_GetDict(module);
if (!dic)
{
    printf("failed !\n");
    return -1;
}

func = PyDict_GetItemString(dic, fun_name);
if (!PyCallable_Check(func))
{
    printf("not find %s\n", fun_name);
    return -1;
}

int r;
//获取Python函数返回值,是一个元组对象
resultValue = PyObject_CallObject(func, NULL);
PyArg_ParseTuple(resultValue, "i", &r);
printf("result :%d\n", r);

Py_DECREF(module);
Py_DECREF(dic);
Py_Finalize();
return 0;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
基础api

C API 调用 Python 对应
PyImport_ImportModel import module
PyImport_ReloadModule reload(module)
PyImport_GetModuleDict module.dict
PyDict_GetItemString dict[key]
PyDict_SetItemString dict[key] = value
PyDict_New dict = {}
PyObject_GetAttrString getattr(obj, attr)
PyObject_SetAttrString setattr(obj, attr, val)
PyObject_CallObject funcobj(argstuple)
PyEval_CallObject funcobj(
argstuple)
PyRun_String eval(exprstr) , exec(stmtstr)
PyRun_File exec(open(filename().read())
Py_BuildValue()函数
作用:将C/C++类型类型的数据转变成PyObject*对象。

原型:PyAPI_FUNC(PyObject) Py_BuildValue(const char format, …);

参数解释:
format及转换格式,类似与C语言中%d,%f,后面的不定参数对应前面的格式,具体格式如下:

“s”(string) [char *] :将C字符串转换成Python对象,如果C字符串为空,返回NONE。

“s#”(string) [char *, int] :将C字符串和它的长度转换成Python对象,如果C字符串为空指针,长度忽略,返回NONE。

“z”(string or None) [char *] :作用同”s”。

“z#” (stringor None) [char *, int] :作用同”s#”。

“i”(integer) [int] :将一个C类型的int转换成Python int对象。

“b”(integer) [char] :作用同”i”。

“h”(integer) [short int] :作用同”i”。

“l”(integer) [long int] :将C类型的long转换成Pyhon中的int对象。

“c”(string of length 1) [char] :将C类型的char转换成长度为1的Python字符串对象。

“d”(float) [double] :将C类型的double转换成python中的浮点型对象。

“f”(float) [float] :作用同”d”。

“O&”(object) [converter, anything] :将任何数据类型通过转换函数转换成Python对象,这些数据作为转换函数的参数被调用并且返回一个新的Python对象,如果发生错误返回NULL。

“(items)”(tuple) [matching-items] :将一系列的C值转换成Python元组。

“[items]”(list) [matching-items] :将一系列的C值转换成Python列表。

“{items}”(dictionary) [matching-items] :将一系类的C值转换成Python的字典,每一对连续的C值将转换成一个键值对。

例:
后面为PyObject的返回值

Py_BuildValue("")None

Py_BuildValue("i",123) 123

Py_BuildValue("iii",123, 456, 789) (123, 456, 789)

Py_BuildValue("s","hello") 'hello'

Py_BuildValue("ss","hello", "world") ('hello', 'world')

Py_BuildValue("s#","hello", 4) 'hell'

Py_BuildValue("()")()

Py_BuildValue("(i)",123) (123,)

Py_BuildValue("(ii)",123, 456) (123, 456)

Py_BuildValue("(i,i)",123, 456) (123, 456)

Py_BuildValue("[i,i]",123, 456) [123, 456] Py_BuildValue("{s:i,s:i}", "abc",123, "def", 456) {'abc': 123, 'def': 456}

Py_BuildValue("((ii)(ii))(ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PyArg_ParseTuple函数
作用:此函数其实相当于sscanf(str,format,…),是Py_BuildValue的逆过程,这个函数将PyObject参数转换成C/C++数据类型,传递的是指针,但这个函数与Py_BuildValue有点不同,这个函数只能解析Tuple元组,而Py_BuildValue函数可以生成元组,列表,字典等。

原型:PyAPI_FUNC(int) PyArg_ParseTuple(PyObject args, const char format,…)

Args:一般为Python程序返回的元组。

Foramt:与Py_BulidValue类型,就不在累述咯。

元组操作函数:
因为程序之间传递的参数,大多数为Tuple类型,所以有专门的函数来操作元组:

PyAPI_FUNC(PyObject *)PyTuple_New(Py_ssize_t size);
解释:新建一个参数列表(调试了下,发现其实是用链表实现的),size列表为长度的宽度

PyAPI_FUNC(Py_ssize_t)PyTuple_Size(PyObject *);
解释:获取该列表的大小

PyAPI_FUNC(PyObject )PyTuple_GetItem(PyObject , Py_ssize_t);
解释:获取该列表某位置的值

PyAPI_FUNC(int) PyTuple_SetItem(PyObject ,Py_ssize_t, PyObject );
解释:设置该列表此位置的值。如PyTuple_SetItem(pyParams,1,Py_BuildValue(“i”,2));设置第2个位置的值为2的整数。
备注:对应的列表和字典也有对应的操作

更多的接口调用以及数据类型转化,参照Python文档
这里写图片描述

Python 调用C

Python调用C有两种方式

使用ctypes模块,Python文档有详细示例
这里写图片描述

使用C为Python编写拓展模块
Python之所以如此强大,正是由于可以使用C\C++为其编写拓展模块,手动编写拓展模块的方式稍微有些繁琐,可借用SWIG自动实现,简洁快速。更多详细的SWIG用法,见其官方文档
官网下载 windows包并解压

使用vs创建空项目,并配置vs。右键当前项目,选择属性
这里写图片描述
现在使用C为Python创建一个叫user的拓展模块,该模块包含一个showHello函数:
分别创建三个文件
user.i
user.c
user_wrap.c

在user.i中添加如下代码

%module user
%inline %{
extern void showHello();
%}
1
2
3
4
user.c中添加

#include <stdio.h>

void showHello()
{
printf("hello Python!\n");

}
1
2
3
4
5
6
7
8
右键user.i 文件并选择属性
这里写图片描述
点击应用后如下图,完成配置
这里写图片描述
右键当前项目,选择属性,完成如下配置,确定
这里写图片描述
最后生成即可(选择工具栏 生成 –> 批生成)

创建测试代码调用C验证

import user
user.show()
1
2
在Linux下则无需如此麻烦的配置,可直接使用命令

On Unix the compilation of examples is done using the file Example/Makefile. This makefile performs a manual module compilation which is platform specific. Typically, the steps look like this (Linux):

% swig -python interface.i
% gcc -fpic -c interface_wrap.c -I/usr/local/include/python1.5
% gcc -shared interface_wrap.o $(OBJS) -o interfacemodule.so
% python
Python 1.5.2 (#3, Oct 9 1999, 22:09:34) [GCC 2.95.1 19990816 (release)] on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam

import interface
interface.blah(...)
1
2
3
4
5
6
7
8
9
10
11
此处.i文件为SWIG的接口文件,其中%module后面定义模块名,用%inline定义方法列表

%inline %{
包含导出的函数
%}
1
2
3
有了Python与C的交互基础,则还需要Android中的NDK开发基础,关于Android平台的jni调用,本文不在此处详解,可看看我的JNI方面博客,而此处我们需要使用Crystax NDK开发工具链,非官方NDK工具链,需自行下载。下一篇正式涉及Python for Android。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

不懂这几点就落后了:Android、Py

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

不懂这几点就落后了:Android、Py

Android 平台的Python:(本文以Python3为例,Python3是未来,大家都懂的) Python作为一个功能强大又语法简洁的语言,其应用已无需多言。要想在Android平台运行起Python,也有方案实现,其实质就是在And
2023-01-31

软考报完名后,千万别忘了这几个时间点!不然影响考试!

如今全国各省市都已发布关于2024年下半年的软考报名通知,大家务必按照当地软考办规定的时间报名。而软考并不是报完名就万事大吉了,因此,大家在报完名后,还千万不要忘了这几个时间点!不然影响考试!
软考报完名后,千万别忘了这几个时间点!不然影响考试!
2024-09-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录