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解釋器。python
首先談一點,爲何要在Android平臺使用Python?Python擁有衆多強大的第三方庫和框架,在機器學習、大數據處理等諸多方面都有不俗的應用。另外,就語法而言,Python比Java更加簡潔,同時又功能強大,既可面向過程亦可面向對象,而不像Java同樣,是一種純粹的面嚮對象語言,哪怕打印一句話也須要先建立類。Python做爲一種腳本語言,能夠邊解釋邊執行,而不需編譯,另外Python中存在的元類,可使咱們動態的建立類,如此能夠在不須要從新編譯安裝apk的狀況下,動態的由遠程服務端爲Android項目添加功能。咱們還能夠將Python已有的一些東西移植到Android平臺,例如tornado、django等,總之玩法多多。linux
在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的交互是很是方便的,所以才能實現這一系列調用。面試
也爲你們推薦了技術教程:django
Android視頻編碼和直播推流windows
Android高手進階api
一天掌握Scrapy爬蟲框架網絡
【Python全棧】網絡爬蟲體驗課app
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。
文末彩蛋: 1.CSDN學院助學會員:僅需百元- 1300門課+600次下載特權,一次購買,整年無憂!點擊查看。
2.CSDN學院知識週刊:每週更新學院優惠課程活動及精品上新內容,點擊查看!
3.本期推薦教程:
課程名稱 課程連接 技術分類 一天掌握Scrapy爬蟲框架 edu.csdn.net/course/deta… 【Python全棧】網絡爬蟲體驗課 edu.csdn.net/course/deta… Django Web框架/Python最牛框架 edu.csdn.net/course/deta… edu.csdn.net/course/deta… Linux視頻教程 edu.csdn.net/course/deta… zabbix企業實戰應用 edu.csdn.net/course/deta… Linux 實用講解+實操+面試題 edu.csdn.net/course/deta… 跟着王進老師學Web前端開發第四季:JavaScript語法基礎 edu.csdn.net/course/deta… 華爲工程師 ,帶你實戰C++(2018版) edu.csdn.net/course/deta… 精通 S T L edu.csdn.net/course/deta…