1. 程式人生 > >Android平臺調用c++

Android平臺調用c++

jni

小白:java代碼中,為什麽不能直接寫c或c++代碼?而oc代碼卻可以直接寫!

小程:你可以寫,只是編譯不過而已。

小白:那不就是不能寫嘛!

不管是編譯到jvm,還是dalvik上運行,java的編譯器都不會直接生成執行文件,而只是生成字節碼(表現為class文件,或者是經過轉換的dex文件),這跟c/c++編譯器的做法大為不同,而很可能因為這個原因,使得java中不能參雜c/c++代碼,而objective-c就可以混編c/c++,因為它的編譯器直接生成執行文件。

那java語言就不能使用c++代碼了嗎?有問題,就有解決辦法,辦法是使用JNI。

JNI(java native interface,即java本地接口)的出現,可以解決java與c++交互的問題,並且不僅限於c++。

小白:可是,我的android程序,為什麽要調用c++的代碼呢?

小程:事出有因,比如已經實現某個功能的是c++代碼而非java代碼,比如要進行大量的數據運算而c++的執行性能遠在java之上,比如要調用一些驅動類的接口,比如...

本文演示java如何調用c++的代碼,即jni的使用。

jni層,可以分為jni上層,跟jni下層。

jni上層用java實現,jni下層用c/c++實現。

大概的調用關系是這樣的:
技術分享圖片

小程接下具體介紹jni上下層的實現,並介紹如何生成so庫(so庫代表了c++實現的功能)並調用它。

(1)實現jni上層

隨便找一個java類(或者新建一個),然後在裏面聲明一個native方法,就實現了jni上層。

比如,創建一個Android Studio項目,並且有這樣的native方法聲明:
技術分享圖片

至此,就已經建立了jni上層。

(2)實現jni下層

java代碼調用jni上層的native函數時,會調用到c++的什麽函數?這裏需要有映射的關系。

建立起這個映射,有兩個辦法。

辦法一:使用簽名

對於native方法,使用javah,可以抽取出對應的簽名,然後c++實現這個簽名的函數即可。

可以這樣生成簽名:
技術分享圖片

這個.h文件的內容是這樣的:
技術分享圖片

那一長串就是對應的簽名,c++代碼實現這個函數,這樣java在調用jni上層聲明的native函數時,就會調用到這裏面來。顯然,一旦建立了映射,jni上層與下層就不能再改名字(除非重新簽名)。

小白:一大串的,還不給改名,大不人性化了。

小程:要改名就新增接口吧,或者,使用第二個辦法。

辦法二:使用RegisterNatives

在jni層加載時,調用RegisterNatives。

這種辦法,沒有使用javah生成固定的簽名,而是使用RegisterNatives來建立navtive方法與具體的實現的關聯,具體實現可隨意命名。

在JNI_onLoad函數內,觸發RegisterNatives的調用:
技術分享圖片
技術分享圖片

在解決掉jni上下層的映射問題後,就是natvie方法的具體實現了。這裏介紹如何生成動態鏈接so庫。

在項目的根目錄中,創建一個jni目錄,在裏面寫c/c++代碼與編譯腳本,目錄結構是這樣的:
技術分享圖片

impl.c的內容:
技術分享圖片

腳本Android.mk的內容:
技術分享圖片

執行ndk-build,生成so庫:
技術分享圖片
技術分享圖片

可以用readelf查看so庫:
技術分享圖片

這裏涉及到編譯腳本(Android.mk)的規則、smalis語言特征(jni的數據類型等),還有ndk-build的使用等知識,小程不展開了,讀者可以關註“廣州小程”微信公眾號,並獲取後續的更新。

(3)調用jni層

在java層調用native方法,比如:
技術分享圖片

執行這個程序,可以看到輸出:
技術分享圖片

到此,如何在Android平臺調用c++的實現就介紹完畢了。


總結一下,本文介紹了如何在Android平臺調用c++的實現,演示了jni層的創建與編譯出so庫,並且調用了so庫。讀者可以把這部分知識應用到實際的Android項目中。

Android平臺調用c++