1. 程式人生 > >Java中JNI的使用詳解第三篇:JNIEnv型別中方法的使用

Java中JNI的使用詳解第三篇:JNIEnv型別中方法的使用

上一篇說道JNIEnv中的方法的用法,這一篇我們就來通過例子來看一下這些方法的使用:

首先是第一個例子:在Java程式碼中定義一個屬性,然後再C++程式碼中將其設定成另外的值,並且輸出來

先來看一下Java程式碼:

 

在來看一下C++程式碼:

#include<iostream.h>
#include "com_jni_demo_JNIDemo.h"
 
JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
{
//獲取obj中物件的class物件
jclass clazz = env->GetObjectClass(obj);
//獲取Java中的number欄位的id(最後一個引數是number的簽名)
jfieldID id_number = env->GetFieldID(clazz,"number","I");
//獲取number的值
jint number = env->GetIntField(obj,id_number);
//輸出到控制檯
cout<<number<<endl;
//修改number的值為100,這裡要注意的是jint對應C++是long型別,所以後面要加一個L
env->SetIntField(obj,id_number,100L);
}

編譯成功後,在Eclipse執行後的結果:

第一個0是在C++程式碼中的cout<<number<<endl;

第二個100是在Java中的System.out.println(jniDemo.number);

 

JNIEnv提供了眾多的Call<Type>Method和CallStatic<Type>Method,還有CallNonvirtual<Type>Method函式,需要通過GetMethodID取得相應方法的jmethodID來傳入到上述函式的引數中

呼叫示例方法的三種形式:

Call<Type>Method(jobject obj,jmethodID id,....);

Call<Type>Method(jobject obj,jmethodID id,va_list lst);

Call<Type>Method(jobject obj,jmethodID id,jvalue* v);

第一種是最常用的方式

第二種是當呼叫這個函式的時候有一個指向引數表的va_list變數時使用的(很少使用)

第三種是當呼叫這個函式的時候有一個指向jvalue或jvalue陣列的指標時用的

說明:

jvalue在jni.h標頭檔案中定義是一個union聯合體,在C/C++中,我們知道union是可以存放不同型別的值,但是當你給其中一個型別賦值之後,這個union就是這種型別了,比如你給jvalue中的s賦值的話,jvalue就變成了jshort型別了,所以我們可以定義一個jvalue陣列(這樣就可以包含多種型別的引數了)傳遞到方法中。

假如現在Java中有這樣的一個方法:

boolean function(int a,double b,char c)

{

    ........

}

(1) 在C++中使用第一種方式呼叫function方法:

env->CallBooleanMethod(obj , id_function , 10L, 3.4 , L'a')

obj是方法funtion的物件

id_function是方法function的id;可以通過GetMethodID()方法獲取

然後就是對應的引數,這個和Java中的可變引數類似,對於最後一個char型別的引數L'a',為什麼前面要加一個L,原因是Java中的字元時Unicode雙位元組的,而C++中的字元時單位元組的,所以要變成寬字元,前面加一個L

(2) 在C++中使用第三種法師呼叫function方法:

jvalue* args = new jvalue[3];//定義jvalue陣列

args[0].i = 10L;//i是jvalue中的jint值

args[1].d = 3.44;

args[2].c = L'a';

env->CallBooleanMethod(obj, id_function, args);

delete[] args;//是否指標堆記憶體

例子:C++中呼叫Java中的方法:

Java程式碼:

public double max(double value1,double value2){
return value1>value2 ? value1:value2;
}

這時候用javap獲取max方法的簽名:

max方法的簽名是(DD)D

在C++中的程式碼:

 

編譯成動態檔案後到Eclipse中執行sayHello方法,執行結果如下:

成功的輸出了最大值

 

JNIEnv中有一個特殊的方法:CallNonvirtual<Type>Method方法

首先來了解一下上面呼叫的function是子類的function方法,這個我們都知道,但是在C++中就不一樣了:

這段C++程式碼中執行的是父類的function方法,那如果想執行子類的function方法怎麼辦呢?那就需要將父類的function方法定義成virtual虛擬函式:

所以說C++和Java對於繼承後執行的是父類的還是子類的方法是有區別的,在Java中所有的方法都是virtual的,所以總是呼叫子類的方法,所以CallNonVirtual<Type>Method這個方法就出來了,這個方法就可以幫助我們呼叫Java中的父類的方法:

在JNI中定義的CallNonvirtual<Type>Method就能夠實現子類物件呼叫父類方法的功能,如果想要呼叫一個物件的父類方法,而不是子類的方法的話,就可以使用CallNonvirtual<Type>Method了,要使用它,首先要獲得父類及其要呼叫的父類方法的jmethodID,然後傳入到這個函式就能通過子類物件呼叫被覆寫的父類的方法了

例子:在Java中定義Father類:

在JNIDemo程式碼:定義Father型別的屬性

 

在來看一下C++中的程式碼:

 

編譯成功.dll檔案,回到Eclipse中執行結果如下:

Child:function是呼叫env->CallVoidMethod(...)方法的

Father:function是呼叫env->CallNonvirtualMethod(...)方法的

這樣就能夠控制到底呼叫哪個類的function方法了。