前言
前面我们已经在Android Studio中创建支持C/C++的项目了,并能调用native方法,返回字符串,展示在界面中,如果对此不了解的话,可以查看之前的博客:Android Studio NDK开发(二):Welcome to JNI
下面我将介绍native函数访问Java中的属性
Java基本数据类型与JNI的映射关系
Java类型<-->JNI类型<-->C类型
JNI基本数据类型(左边是Java,右边是JNI)
boolean jboolean;
byte jbyte;
char jchar;
short jshort;
int jint;
long jlong;
float jfloat;
double jdouble;
void void
JNI引用数据类型(左边是Java,右边是JNI)
String jstring
Object jobject
//基本数据类型的数组
byte[] jByteArray
//对象数组
Object[](String[]) jobjectArray
由于在属性、方法访问当中需要用到签名,接下来我们来看看签名:
属性与方法的签名
属性的签名
方法的签名
方法的签名稍显麻烦,我们可以在Android Studio中配置javap -s -p,来生成某个类所有的属性、方法的签名。
在Android Studio中,选择file -> settings -> 输入tools -> 选择External Tools:
这里由于我已经添加了javap -s -p,所以这里已经有了相关配置,可不必理睬。现在我们来进行配置,点击第三步,出现:
配置后:
点击ok,这样我们就可以查看某个方法的签名了,选择该方法所属类,比如MainActivity,点击Tools -> External Tools -> javap -s -p
可以看到生成了该类所有的签名(如果不行,可以Rebuild Project或者Make Project,再执行javap -s -p命令):
访问Java非静态属性
我们需要做的是native函数访问Java的非静态属性,并改变Java中非静态属性的值。
首先在MainActivity(可自己起名字,也可以是Java类)中定义一个非静态属性key,也就是native函数需要访问的Java属性
public String key = "john";
在MainActivity中定义一个native方法
//访问属性
public native void accessField();
由于需要判断更改后的值是否一样,所以在MainActivity中的onCreate()中调用并打印
System.out.println("修改前:" + key);
this.accessField();
System.out.println("修改后:" + key);
当然基本的配置,比如System.loadLibray,CMakeLists的配置是少不了的,有不清楚的,可以查看我的NDK开发系列的Android NDK开发(一)、(二),这里就不做赘述了。
再来看看native函数的实现:
//C/C++访问Java的成员
//访问非静态属性
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessField(JNIEnv *env, jobject jobj) {
//得到jclass
jclass jcla = (*env)->GetObjectClass(env, jobj);
//得到jfieldID,最后一个参数是签名,String对应的签名是Ljava/lang/String;(注意最后的分号)
jfieldID jfID = (*env)->GetFieldID(env, jcla, "key", "Ljava/lang/String;");
//得到key属性的值jstring
jstring jstr = (*env)->GetObjectField(env, jobj, jfID);
//jstring转化为C中的char*
char* oriText = (*env)->GetStringUTFChars(env, jstr, NULL);
//拼接得到新的字符串text="China John"
char text[20] = "China ";
strcat(text, oriText);
//C中的char*转化为JNI中的jstring
jstring jstrMod = (*env)->NewStringUTF(env, text);
//修改key
(*env)->SetObjectField(env, jobj, jfID, jstrMod);
//只要使用了GetStringUTFChars,就需要释放
(*env)->ReleaseStringUTFChars(env, jstr, oriText);
}
具体JNI的api可看上面的注释,这里就不细说了,有一定的规律可寻,大家动手做做,相信问题不大。
打印输出:
修改前:john
修改后:China john
访问Java静态属性
首先定义一个Java静态属性
private static int count = 10;
再定义一个native方法
//访问静态属性
public native void accessStaticField();
在MainActivity onCreate中调用打印
System.out.println("修改前count:" + count);
accessStaticField();
System.out.println("修改后count:" + count);
native函数的实现
//访问静态属性
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessStaticField(JNIEnv *env, jobject jobj) {
//得到jclass
jclass jcla = (*env)->GetObjectClass(env, jobj);
//得到jfieldID
jfieldID jfid = (*env)->GetStaticFieldID(env, jcla, "count", "I");
//得到静态属性的值count
jint count = (*env)->GetStaticIntField(env, jcla, jfid);
//自增
count++;
//修改count的值
(*env)->SetStaticIntField(env, jcla, jfid, count);
}
项目地址
https://github.com/fsrmeng/MyJniCmake-Master
展望
本篇博客介绍了如何在native函数中访问Java的属性,下一篇博客我们将介绍如何在native函数中访问Java的方法,敬请期待!