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