看到這個標題,你們可能會認爲就是Android運行python腳本,或者用python寫app,這些用QPython和P4A就能夠實現了。我在想既然C能夠調用Python,那麼Android能不能經過JNI去調用C裏的方法,C再去調用Python方法,實現Android與Python交互呢?用最近很熱的一個概念來講JNI就是個殼。(本文假設你們有JNI開發基礎)java
因爲需求很明確了,因此總體流程大概就是這樣。python
交互流程android
首先看下咱們爲何要在Android裏須要使用Python,我認爲主要有一下幾個優勢編程
而Python只須要一句話就能夠print出來json
print ("hello world")app
2.上手快,按網友所說,只須要讀完Python API就能夠成爲大神,實際體驗確實如此,十分好上手,若是如今讓我推薦一個沒有學過編程的人學習一款腳本語言,我會推薦他學一下python。函數
3.前期開發效率高,正如前兩個優勢所說,代碼簡潔、上手快並且因爲屬於超高級語言,不少東西都封裝好了,決定了他前期開發效率很高。學習
4.可移植性強,因爲是解釋性語言,只須要有解釋器,他能夠運行在任何平臺。gradle
5.拓展性強,C/JAVA都有接口能夠調用到Python,Python也能夠調用到C,對Python進項拓展。優化
6.豐富的庫,因爲超高級語言,封裝了不少方法,並且好多大牛對其開發了庫。
固然還有幾個缺點必需要強調一下。
強制縮進,代碼簡潔是把雙刃劍,因爲縮進因此簡潔,而又因爲縮進致使沒法自動格式化代碼,並且代碼塊的分割都是靠縮進,這時可能會形成混亂。
運行速度相對較慢,固然這個對相對C這種接近底層的語言來講的,Python在運行時先解析,再運行,並且因爲高層語言相比底層語言都會慢那麼一點。
版本兼容性較差,這個體現最明顯的就是Python3和Python2,Python3不向下兼容
更多Python視頻、源碼、資料加羣683380553免費獲取
Python C是C語言調用Python的一組API,經過它咱們能夠調用到Python方法。
Python C開發步驟
引入頭文件Python.h;
初始化python(Py_Initialize();)
引入模塊(pModule = PyImport_Import("pythoncode");)
獲取模塊中的函數(PyObject_GetAttrString(pModule, "hello");
)
調用獲取的函數(PyEval_CallObject(pFunction, NULL);
)
釋放python(Py_Finalize();)
對應的代碼以下:
固然,直接運行這段代碼會報錯,由於Python.h找不到還有相應的lib找不到,這裏強烈建議使用mac或者Linux開發!!!填坑效率會比Windows高好多。具體怎麼樣處理這裏先不說,若是實在須要,留言給我,我會另開一篇博文,畢竟這裏是講Android調用python的,而這個是在桌面環境下C調用Python的,並且百度也不少。
當我成功使用C語言調用Python以後,我着手在JNI開發裏調用Python,Python文件放在assets中 。
可是在開發過程當中遇到了如下幾個問題:
頭文件找不到(Python.h)
沒有移動平臺的python.so
兼容性
找不到.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文件夾裏,再進行初始化。
JNI C代碼:
Python方面就是個簡單的hello函數,返回「hello」字符串。
當我把上述問題一一解決以後,終於見到以前寫的python代碼裏返回的hello語句了。可由此也出現了一個問題,當我調用Python方法的時候,必須先引入模塊,再引入方法,並且當咱們須要添加Python方法的時候,咱們還要去寫重複的調用方法,只是換個方法名,並且須要再次編譯各平臺so庫,我就想有沒有一種方法能夠只修改Python方法和java調用方法,而不去動C方法呢。
修改後的流程圖以下:
優化後流程
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大神的指導!