JNI本地方法访问Java属性和方法
在JNI调用中,不仅仅Java可以调用本地方法,本地代码也可以调用Java中的方法和成员变量。在Java1.0中“原始的”Java到C的绑定中,程序员可以直接访问对象数据域。然而,直接方法要求虚拟机暴露他们的内部数据布局,基于这个原因,JNI要求程序员通过特殊的JNI函数来获取和设置数据以及调用java方法。
- 取得代表属性和方法的jfieldID和jmethodID
为了在C/C++中表示属性和方法,JNI在jni.h头文件中定义了jfieldID和jmethodID类型来分别代表Java对象的属性和方法。我们在访问或是设置Java属性的时候,首先就要先在本地代码取得代表该Java属性的jfieldID,然后才能在本地代码进行Java属性操作。同样的,我们需要调用Java对象方法时,也是需要取得代表该方法的jmethodID才能进行Java方法调用。
使用JNIEnv提供的JNI方法,我们就可以获得属性和方法相对应的jfieldID和jmethodID:
l GetFieldID :取得成员变量的id
l GetStaticFieldID :取得静态成员变量的id
l GetMethodID :取得方法的id
l GetStaticMethodID :取得静态方法的id
jfieldID GetFieldID(jclass clazz, const char *name,const char *sig)
jfieldID GetStaticFieldID(jclass clazz, const char*name, const char *sig)
jmethodID GetStaticMethodID(jclass clazz, const char*name, const char *sig)
jmethodID GetMethodID(jclass clazz, const char *name,constchar *sig)
第一个参数jclassclazz :
上一节讲到的jclass类型,相当于Java中的Class类,代表一个Java类,而这里面的代表的就是我们操作的Class类,我们要从这个类里面取的属性和方法的ID。
第二个参数constchar *name:
这是一个常量字符数组,代表我们要取得的方法名或者变量名。
第三个参数constchar *sig:
这也是一个常量字符数组,代表我们要取得的方法或变量的签名。(变量类型)
类型签名 | Java 类型 | 类型签名 | Java 类型 |
Z | boolean | V | void |
B | byte | Ljava/lang/String | String |
C | char | Ljava/lang/Object | Object |
S | short | F | float |
I | int | D | double |
J | long | L | Java对象以L开头,然后以“/”分隔包的完整类型, |
示例:String substr(String str, int idx, int count);
对应的类型签名(Ljava/lang/String;II)Ljava/lang/String
- 获取/设置 属性/静态属性
获取属性/静态属性的形式:
Get<Type>Field GetStatic<Type>Field。
设置属性/静态属性的形式:
Set<Type>Field SetStatic<Type>Field。
取得成员属性:
jobject GetObjectField(jobjectobj, jfieldID fieldID);
jboolean GetBooleanField(jobjectobj, jfieldID fieldID);
jbyte GetByteField(jobjectobj, jfieldID fieldID);
取得静态属性:
jobject GetStaticObjectField(jclassclazz, jfieldID fieldID);
jboolean GetStaticBooleanField(jclassclazz, jfieldID fieldID);
jbyte GetStaticByteField(jclassclazz, jfieldID fieldID);
Get方法的第一个参数代表要获取的属性所属对象或jclass对象,第二个参数即属性ID。
设置成员属性:
void SetObjectField(jobjectobj, jfieldID fieldID, jobject val);
void SetBooleanField(jobjectobj, jfieldID fieldID,jboolean val);
void SetByteField(jobjectobj, jfieldID fieldID, jbyte val);
设置静态属性:
void SetStaticObjectField(jobjectobj, jfieldID fieldID, jobject val);
void SetStaticBooleanField(jobjectobj, jfieldID fieldID,jboolean val);
void SetStaticByteField(jobjectobj, jfieldID fieldID, jbyte val);
Set方法的第一个参数代表要设置的属性所属的对象或jclass对象,第二个参数即属性ID,第三个参数代表要设置的值。
- 调用方法
取得了代表方法和静态方法的jmethodID,就可以用在JNIEnv中提供的方法来调用方法和静态方法。
调用普通方法:
Call<Type>Method(jobject obj, jmethodID methodID,...);
Call<Type>MethodV(jobject obj, jmethodID methodID,va_listargs);
Call<Type>tMethodA(jobject obj, jmethodID methodID,constjvalue *args);
调用静态方法:
CallStatic<Type>Method(jclass clazz, jmethodID methodID,...);
CallStatic<Type>MethodV(jclass clazz, jmethodID methodID,va_listargs);
CallStatic<Type>tMethodA(jclass clazz, jmethodID methodID,constjvalue *args);
上面的Type这个方法的返回值类型,如Int,Char,Byte等等。
第一个参数代表调用的这个方法所属于的对象,或者这个静态方法所属的类。
第二个参数代表jmethodID。
后面的参数,就代表这个方法的参数列表了。
本地代码部分:
//首先取得要调用的方法所在的类的Class对象,在C/C++中即jclass对象
jclass clazz_NativeTest=env->FindClass("cn/itcast/NativeTest");
//取得jmethodID
jmethodID id_show=env->GetMethodID(clazz_NativeTest,“show”,"(I)V");
//调用java函数
env->CallIntMethod(obj, id_show, i); //i类型为 jint