Android Studio開發之 JNI 篇

前言

Android上層應用使用java開發,不過java并不適合密集型運算,比如圖片處理等,遇到密集型運算,一般使用c/c++完成。

Java虛擬機支持調用c/c++代碼,即JNI(Java Native Interface),它提供了若干的API實現了Java和其他語言的通信。為方便android平臺上使用JNI技術,提供了NDK開發包,可以將NDK理解為對JNI的進一步封裝,方便開發使用罷了。

JNI開發方式有多種,可以在Android 源碼中開發,也可以利用其它工具,但都比較煩瑣或者要下載很多東西,Android Studio也支持JNI開發,使用起來也比較方便,本文主要講述下如何使用Android Studio進行JNI開發。

NDK設置

NDK需要下載,一共有兩種方式,建議從Android Studio中下載。

  • 從Android Studio中打開SDK Manager,進入如下界面并且勾選NDK選項。
  • 點擊應用,安裝完后重啟Android Studio即可。

也可以從官網下載,然后在Android Studio中設置,這種方式不再講述。

JNI開發

本章中以高斯模糊圖像處理為示例,學習如何進行JNI開發。

1、新建一個Android工程,注意Android Studio對包名的處理,它的默認處理非常地別扭,如果不喜歡這種包名命名方式,可以點擊 Edit 進行更改。

2、將工程以Project視圖顯示,方便查找具體文件。

3、在項目gradle.properties文件中加上以下代碼,表示我們要使用NDK進行開發。

  android.useDeprecatedNdk=true

4、查看項目local.properties中是否有加入ndk和sdk的路徑,如果沒有需要補充。

  ndk.dir=D\:\\android-sdk\\ndk-bundle
  sdk.dir=D\:\\android-sdk

5、在app文件夾下的build.gradle的defaultConfig里加入如下代碼

  ndk {
        moduleName "ImageBlur"       //生成的so文件名字,調用C程序的代碼中會用到該名字
        abiFilters "armeabi", "armeabi-v7a", "x86" //輸出指定三種平臺下的so庫
        ldLibs "log", "jnigraphics", "android"    //jni中需要用到的其它庫
    }

6、定義native方法

7、生成h文件,打開Android Studio提供的命令行工具Terminal,輸入以下指令。

  cd app/src/main/java
  javah -jni 包名+類名

本例中報錯,“無法確定Bitmap的簽名”,根據網上搜索結果,需要指出 android.jar 文件的位置才行,于是按如下方法生成 h 文件。

  javah -classpath C:\PROGRA~2\Android\android-sdk\platforms\android-8\android.jar;. com.test.JniTest

8、建立 JNI 文件夾,復制生成的 h 文件到 JNI 文件夾中來。 選擇File->New->Folder->JNI Folder

注意:在彈出創建 JNI 文件夾的對話框中勾選 Change Folder Location,并在下面輸入文件夾名,如下圖所示。

一般來說JNI相關文件放在 src/main/jni 之中。

9、新建c文件,實現對應接口,在java代碼中完成 JNI 接口調用。

方法訪問

通常使用JNI,都是在java代碼中訪問c或c++代碼,但是JNI還提供這樣的能力,可以在本地代碼中訪問java代碼。

JNI方法中都會存在JNIEnv變量,它是本地函數的第一個參數,其本質指向了一個函數列表的指針。

調用Java方法一共有兩個步驟,先找到這個java類的class對象,再得到java類具體方法的method,類似于反射,最后調用:

  char* classname = "wjy/geridge/com/testndk/jni/JniUtils";  
  jclass dpclazz = (*env)->FindClass(env, classname);  
  //參數介紹 : 第二個參數是Class對象, 第三個參數是方法名,第四個參數是方法的簽名, 獲取到調用的method  
  jmethodID methodID = (*env)->GetMethodID(env, dpclazz,"add", "(II)I");  

JNI識別Java方法 : JNI依靠函數名 和 方法簽名 識別方法, 函數名是不能唯一識別一個方法的, 因為方法可以重載, 類型簽名代表了 參數 和 返回值;

Java類型 與 類型簽名對照表 : 注意 boolean 與 long 不是大寫首字母, 分別是 Z 與 J, 類是L全限定類名, 數組是[元素類型簽名;

-- 類的簽名規則 :L + 全限定名 + ;三部分, 全限定類名以 / 分割;

Java類型 類型簽名

boolean Z
byte B
char C
short S
int I
long J
float F
double D
L全限定類名
數組 [元素類型簽名

-- 簽名規則 : (參數1類型簽名參數2類型簽名參數3類型簽名參數N類型簽名...)返回值類型簽名, 注意參數列表中沒有任何間隔;

如. long function(int n, String str, int[] arr);
該方法的簽名 :(ILjava/lang/String;[I)J
上面的例子中兩個參數都是int類型返回值也是int所以是:(II)I
例子中調用了GetMethodID方法去獲取add方法的唯一標識,如果add是個靜態方法呢?

  jmethodID methodID = (*env)->GetStaticMethodID(env, dpclazz,"add", "(II)I");

可以看到獲取靜態方法需要調用GetStaticMethodID方法,參數與GetMethodID相同

已經獲取了methodId了,接下來就要調用具體方法了。

普通方法 : CallTypeMethod , 其中的Type隨著返回值類型的不同而改變;
參數介紹 : ① JNIEnv指針 ②調用該native方法的對象 ③方法的methodID ④⑤... 后面是可變參數

同樣的如果是調用靜態方法應該使用對應的CallStaticTypeMethod, 其中的Type隨著返回值類型不同而改變;
上面的方法都在jni.h中聲明(android-sdk-windows\ndk-bundle\platforms\android-24\arch-arm\usr\include\jni.h)

結語

在gradle構建的過程中有可能出現這樣或那樣的異常,查看gradle構建日志,即可知道具體異常,而查看gradle構建日志按鈕比較隱蔽。

比如說,使用c文件或c++文件,往往會有一些不同,使用c++文件可能編譯報錯,此時則需要打開gradle console查看具體原因。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容