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方法了。