前情提要
在前面 , 我们已经熟悉了JNI的开发流程 , .h头文件的分析 , 生成头文件javah命令 , 以及java类型在C语言中的表现形式 , 值得注意的是 , java中的所有引用类型都是jobject类型 , native生成的函数 , 以Java_全类名_方法名表示,包名的.以_表示 。
概述
在开篇的时候 ,我们就使用java的native方法调用过C函数 , 返回了一个String类型的字符串 , 使用(*Env)->NewStringUTF(Env, "Jni C String");函数 , 我们将字符指针转换成jstring , java类型的字符串返回给了我们的java层 。今天我们来学习 , 使用C语言来调用Java的字段与方法 。
part 1 : C 函数访问java字段
一 , 定义Java 的String类型字段与修改字段的native方法
// 使用C语言修改java字段
public String name = "zeno" ;
// C语言修改java String 类型字段本地方法
public native void accessJavaStringField() ;
// 调用
HelloJni jni = new HelloJni() ;
System.out.println("修改前 name 的值:"+jni.name);
//C语言修改java字段本地方法
jni.accessJavaStringField();
System.out.println("修改后 name 的值:"+jni.name);
二 , 在C语言头文件中定义native方法的实现函数 , 并实现
// com.zeno.jni_HelloJNI.h
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStringField
(JNIEnv *, jobject);
// Hello_JNI.c
/*C语言访问java String类型字段*/
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStringField
(JNIEnv *env, jobject jobj) {
// 得到jclass
jclass jcls = (*env)->GetObjectClass(env, jobj);
// 得到字段ID
jfieldID jfID = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;");
// 得到字段的值
jstring jstr = (*env)->GetObjectField(env, jobj, jfID);
// 将jstring类型转换成字符指针
char* cstr = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE);
//printf("is vaule:%s\n", cstr);
// 拼接字符
char text[30] = " xiaojiu and ";
strcat(text, cstr);
//printf("modify value %s\n", text);
// 将字符指针转换成jstring类型
jstring new_str = (*env)->NewStringUTF(env, text);
// 将jstring类型的变量 , 设置到java 字段中
(*env)->SetObjectField(env, jobj, jfID, new_str);
}
三 , 输出
修改前 name 的值:zeno
修改后 name 的值: xiaojiu and zeno
四 , 分析
首先来分析C函数:
JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStringField
(JNIEnv *env, jobject jobj)
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromC
(JNIEnv *Env, jclass jclazz)
我们可以看出两处不同 , 一处是返回值类型 , 一处是函数参数类型 ,返回值类型没什么可讲的 , 在分析.h头文件的时候 , 已经详细讲述了 。那 , 这两个函数参数类型jobject与jclass有什么区别呢 ? 这两个类型表示 , Java的native函数 , 是成员方法还是类方法 , 成员方法需要对象.方法名 , 类方法则类名.方法名 , 可以在main方法里面直接使用 。
接下来是:
// 得到jclass jclass就好比java的.class对象
jclass jcls = (*env)->GetObjectClass(env, jobj);
为什么要得到jclass呢 ?
因为 ,我们要获取字段ID , 在JNI中 , 获取java字段与方法都需要签名。而签名是在类加载的时候完成 , 所以在获取字段ID的时候需要传入jclass 。
// 得到字段ID
jfieldID jfID = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;");<