上一篇說道JNIEnv中的方法的用法,這一篇咱們就來經過例子來看一下這些方法的使用:java
首先是第一個例子:在Java代碼中定義一個屬性,而後再C++代碼中將其設置成另外的值,而且輸出來ios
先來看一下Java代碼:數組
[java] view plaincopyide
package com.jni.demo; 函數
public class JNIDemo { spa
public int number = 0;//定義一個屬性 .net
//定義一個本地方法 指針
public native void sayHello(); code
public static void main(String[] args){ 對象
//調用動態連接庫
System.loadLibrary("JNIDemo");
JNIDemo jniDemo = new JNIDemo();
jniDemo.sayHello();
System.out.print(jniDemo.number);
}
}
在來看一下C++代碼:
[cpp] view plaincopy
#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++中的代碼:
[cpp] view plaincopy
JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
{
//獲取obj中對象的class對象
jclass clazz = env->GetObjectClass(obj);
//獲取Java中的max方法的id(最後一個參數是max方法的簽名)
jmethodID id_max = env->GetMethodID(clazz,"max","(DD)D");
//調用max方法
jdouble doubles = env->CallDoubleMethod(obj,id_max,1.2,3.4);
//輸出返回值
cout<<doubles<<endl;
}
編譯成動態文件後到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類:
[java] view plaincopy
package com.jni.demo;
public class Father {
public void function(){
System.out.println("Father:function");
}
}
在定義一個子類Child:繼承Father類,從寫父類中的function方法
[java] view plaincopy
package com.jni.demo;
public class Child extends Father{
@Override
public void function(){
System.out.println("Child:function");
}
}
在JNIDemo代碼:定義Father類型的屬性
[java] view plaincopy
package com.jni.demo;
public class JNIDemo {
public Father father = new Child();
//定義一個本地方法
public native void sayHello();
public static void main(String[] args){
//調用動態連接庫
System.loadLibrary("JNIDemo");
JNIDemo jniDemo = new JNIDemo();
jniDemo.sayHello();
}
}
在來看一下C++中的代碼:
[cpp] view plaincopy
#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中的father字段的id(最後一個參數是father字段的簽名)
jfieldID id_father = env->GetFieldID(clazz,"father","Lcom/jni/demo/Father;");
//獲取father字段的對象類型
jobject father = env->GetObjectField(obj,id_father);
//獲取father對象的class對象
jclass clazz_father = env->FindClass("com/jni/demo/Father");
//獲取father對象中的function方法的id
jmethodID id_father_function = env->GetMethodID(clazz_father,"function","()V");
//調用父類中的function方法(可是會執行子類的方法)
env->CallVoidMethod(father,id_father_function);
//調用父類中的function方法(執行就是父類中的function方法)
env->CallNonvirtualVoidMethod(father,clazz_father,id_father_function);
}
編譯成功.dll文件,回到Eclipse中運行結果以下:
Child:function是調用env->CallVoidMethod(...)方法的
Father:function是調用env->CallNonvirtualMethod(...)方法的
這樣就可以控制到底調用哪一個類的function方法了。