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

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

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

先來看一下Java代碼:數組

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片ide

  1. package com.jni.demo;  函數

  2. public class JNIDemo {  spa

  3.   

  4. public int number = 0;//定義一個屬性  .net

  5.   

  6. //定義一個本地方法  指針

  7. public native void sayHello();  code

  8. public static void main(String[] args){  對象

  9. //調用動態連接庫  

  10. System.loadLibrary("JNIDemo");  

  11. JNIDemo jniDemo = new JNIDemo();  

  12. jniDemo.sayHello();  

  13. System.out.print(jniDemo.number);  

  14. }  

  15. }  


在來看一下C++代碼:

[cpp] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. #include<iostream.h>  

  2. #include "com_jni_demo_JNIDemo.h"  

  3.   

  4. JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)  

  5. {  

  6. //獲取obj中對象的class對象  

  7. jclass clazz = env->GetObjectClass(obj);  

  8. //獲取Java中的number字段的id(最後一個參數是number的簽名)  

  9. jfieldID id_number = env->GetFieldID(clazz,"number","I");  

  10. //獲取number的值  

  11. jint number = env->GetIntField(obj,id_number);  

  12. //輸出到控制檯  

  13. cout<<number<<endl;  

  14. //修改number的值爲100,這裏要注意的是jint對應C++是long類型,因此後面要加一個L  

  15. env->SetIntField(obj,id_number,100L);  

  16. }  


編譯成功後,在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在CODE上查看代碼片派生到個人代碼片

  1. JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)  

  2. {  

  3. //獲取obj中對象的class對象  

  4. jclass clazz = env->GetObjectClass(obj);  

  5. //獲取Java中的max方法的id(最後一個參數是max方法的簽名)  

  6. jmethodID id_max = env->GetMethodID(clazz,"max","(DD)D");  

  7. //調用max方法  

  8. jdouble doubles = env->CallDoubleMethod(obj,id_max,1.2,3.4);  

  9. //輸出返回值  

  10. cout<<doubles<<endl;  

  11. }  


編譯成動態文件後到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在CODE上查看代碼片派生到個人代碼片

  1. package com.jni.demo;  

  2.   

  3. public class Father {  

  4.   

  5. public void function(){  

  6. System.out.println("Father:function");  

  7. }  

  8.   

  9. }  


在定義一個子類Child:繼承Father類,從寫父類中的function方法

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. package com.jni.demo;  

  2.   

  3. public class Child extends Father{  

  4.   

  5. @Override  

  6. public void function(){  

  7. System.out.println("Child:function");  

  8. }  

  9.   

  10. }  


在JNIDemo代碼:定義Father類型的屬性

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. package com.jni.demo;  

  2. public class JNIDemo {  

  3. public Father father = new Child();  

  4. //定義一個本地方法  

  5. public native void sayHello();  

  6. public static void main(String[] args){  

  7. //調用動態連接庫  

  8. System.loadLibrary("JNIDemo");  

  9. JNIDemo jniDemo = new JNIDemo();  

  10. jniDemo.sayHello();  

  11. }  

  12.   

  13. }  

在來看一下C++中的代碼:

[cpp] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. #include<iostream.h>  

  2. #include "com_jni_demo_JNIDemo.h"  

  3.   

  4. JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)  

  5. {  

  6. //獲取obj中對象的class對象  

  7. jclass clazz = env->GetObjectClass(obj);  

  8. //獲取Java中的father字段的id(最後一個參數是father字段的簽名)  

  9. jfieldID id_father = env->GetFieldID(clazz,"father","Lcom/jni/demo/Father;");  

  10. //獲取father字段的對象類型  

  11. jobject father = env->GetObjectField(obj,id_father);  

  12. //獲取father對象的class對象  

  13. jclass clazz_father = env->FindClass("com/jni/demo/Father");  

  14. //獲取father對象中的function方法的id  

  15. jmethodID id_father_function = env->GetMethodID(clazz_father,"function","()V");  

  16. //調用父類中的function方法(可是會執行子類的方法)  

  17. env->CallVoidMethod(father,id_father_function);  

  18. //調用父類中的function方法(執行就是父類中的function方法)  

  19. env->CallNonvirtualVoidMethod(father,clazz_father,id_father_function);  

  20.   

  21. }  

編譯成功.dll文件,回到Eclipse中運行結果以下:


Child:function是調用env->CallVoidMethod(...)方法的

Father:function是調用env->CallNonvirtualMethod(...)方法的

這樣就可以控制到底調用哪一個類的function方法了。

相關文章
相關標籤/搜索