Android和Python之間的不能說的小祕密

前言

看到這個標題,你們可能會認爲就是Android運行python腳本,或者用python寫app,這些用QPython和P4A就能夠實現了。我在想既然C能夠調用Python,那麼Android能不能經過JNI去調用C裏的方法,C再去調用Python方法,實現Android與Python交互呢?用最近很熱的一個概念來講JNI就是個殼。(本文假設你們有JNI開發基礎)java

想法

因爲需求很明確了,因此總體流程大概就是這樣。python

Android和Python之見的小祕密

交互流程android

爲何要用python

  1. 首先看下咱們爲何要在Android裏須要使用Python,我認爲主要有一下幾個優勢編程

Android和Python之見的小祕密

而Python只須要一句話就能夠print出來json

print ("hello world")app

2.上手快,按網友所說,只須要讀完Python API就能夠成爲大神,實際體驗確實如此,十分好上手,若是如今讓我推薦一個沒有學過編程的人學習一款腳本語言,我會推薦他學一下python。函數

3.前期開發效率高,正如前兩個優勢所說,代碼簡潔、上手快並且因爲屬於超高級語言,不少東西都封裝好了,決定了他前期開發效率很高。學習

4.可移植性強,因爲是解釋性語言,只須要有解釋器,他能夠運行在任何平臺。gradle

5.拓展性強,C/JAVA都有接口能夠調用到Python,Python也能夠調用到C,對Python進項拓展。優化

6.豐富的庫,因爲超高級語言,封裝了不少方法,並且好多大牛對其開發了庫。

固然還有幾個缺點必需要強調一下。

  1. 強制縮進,代碼簡潔是把雙刃劍,因爲縮進因此簡潔,而又因爲縮進致使沒法自動格式化代碼,並且代碼塊的分割都是靠縮進,這時可能會形成混亂。

  2. 運行速度相對較慢,固然這個對相對C這種接近底層的語言來講的,Python在運行時先解析,再運行,並且因爲高層語言相比底層語言都會慢那麼一點。

  3. 版本兼容性較差,這個體現最明顯的就是Python3和Python2,Python3不向下兼容

  4. 更多Python視頻、源碼、資料加羣683380553免費獲取

Python C

Python C是C語言調用Python的一組API,經過它咱們能夠調用到Python方法。

Python C開發步驟

  1. 引入頭文件Python.h;

  2. 初始化python(Py_Initialize();)

  3. 引入模塊(pModule = PyImport_Import("pythoncode");)

  4. 獲取模塊中的函數(PyObject_GetAttrString(pModule, "hello");

  5. 調用獲取的函數(PyEval_CallObject(pFunction, NULL);

  6. 釋放python(Py_Finalize();)

對應的代碼以下:

Android和Python之見的小祕密

固然,直接運行這段代碼會報錯,由於Python.h找不到還有相應的lib找不到,這裏強烈建議使用mac或者Linux開發!!!填坑效率會比Windows高好多。具體怎麼樣處理這裏先不說,若是實在須要,留言給我,我會另開一篇博文,畢竟這裏是講Android調用python的,而這個是在桌面環境下C調用Python的,並且百度也不少。

JNI Python C

當我成功使用C語言調用Python以後,我着手在JNI開發裏調用Python,Python文件放在assets中 。

可是在開發過程當中遇到了如下幾個問題:

  1. 頭文件找不到(Python.h)

  2. 沒有移動平臺的python.so

  3. 兼容性

  4. 找不到.py文件

接下來一個一個填坑。

頭文件找不到(Python.h)

在MK文件中添加引用,

 
include $(CLEAR_VARS)

這段代碼其實也把下一個問題解決了。

另外咱們剛項目開始的時候可能爲了開發方便,會在gradle中配置JNI資源文件夾路徑,但是這致使了run project的時候AS也會對其中的C文件進行語法檢查,這樣因爲沒有外部頭文件依賴,編譯不會經過,因此咱們須要在gradle中把JNI資源文件夾刪了,用[]代替

 
sourceSets.main {

當咱們編譯成功SO庫以後,C文件在運行中並不會被調用,而是調用編譯爲.so的文件中的方法。

沒有移動平臺的python.so

想要運行Python必需要有解釋器,Android自己沒有帶,因此咱們須要在程序中內嵌一個解釋器,但是苦於找不到合適的so庫,曾把P4A的python編譯了一次,但是版本兼容性差,可用性不高。直到找到了Crystax NDK,它在10.3以後已經開始支持python for Android了,並且這個NDK資源包還填了幾乎全部Android調用python的坑,包括第一個找不到頭文件的問題,兼容的問題。在MK文件中,咱們還須要加一段代碼,編譯crystax so庫。

 
include $(CLEAR_VARS)

兼容性

Android目前有7個常見平臺須要適配,其他的都沒問題,只有X86和X86_64的有問題,推測crystax NDK Windows還沒完善,由於mac下是能夠直接編譯的,因此有關編譯的東西最好用Linux和Mac,Windows下我刪了一個頭文件,就能夠運行了,沒有發現異常。具體哪一個我忘了,不過運行時報錯哪一個就去相應的文件裏把頭文件依賴刪了就行,就一個。

而後生成7個平臺的so庫只須要在Application.mk中添加如下代碼便可(APP_PLATFORM看我的調節):

 
APP_PLATFORM := android-19APP_ABI := armeabi-v7a armeabi mips mips64 arm64-v8a x86 x86_64

找不到.py文件

不知道什麼緣由,assets文件夾裏的py文件獲取不到,彷佛是不能識別asset路徑?求大神告知。解決方法就是把assets文件夾裏的文件複製到設備的data文件夾裏,再進行初始化。

Android和Python之見的小祕密

Android和Python之見的小祕密

JNI C代碼:

Android和Python之見的小祕密

Python方面就是個簡單的hello函數,返回「hello」字符串。

優化

當我把上述問題一一解決以後,終於見到以前寫的python代碼裏返回的hello語句了。可由此也出現了一個問題,當我調用Python方法的時候,必須先引入模塊,再引入方法,並且當咱們須要添加Python方法的時候,咱們還要去寫重複的調用方法,只是換個方法名,並且須要再次編譯各平臺so庫,我就想有沒有一種方法能夠只修改Python方法和java調用方法,而不去動C方法呢。

修改後的流程圖以下:

Android和Python之見的小祕密

優化後流程

Python端增長一個路由方法,再寫一個函數字典,把全部方法都加到字典裏,C裏調用的就是這個路由方法,java端調用的時候傳入json裏面包含了所需python方法,當json傳入python中路由方法以後,自動匹配到相應的方法,每次添加新的方法只須要在python中添加字典已經方法,java調用時傳入新的方法便可。

Python路由方法:

 
def router(args):

Python函數字典:

 
routes = { 'hello': hello, 'add'add'mul': mul,

JNI C調用python方法:

 
JNIEXPORT jstring JNICALL Java_com_jcmels_liba_pysayhello_PyBridge_call

java調用:

 
json.put("function""hello");

後記

到此,Android call Python就基本完成了,調用第三方庫的話只須要把ctype文件(Crystax文件夾中的sourcespython.5libs對應平臺modules_ctypes.so)放到assets文件夾中就能夠經過cdll.LoadLibrary來調用第三方庫了。

在此感謝joaoventura大神的指導!

相關文章
相關標籤/搜索