问题
当我们需要保存C++对象的状态时,如何通过JNI来操作呢?
思路
当JAVA使用本地方法时,需要把该方法定义在一个类中,我们可以让该对象有一个long
类型的属性,去保存本地代码C++类对象的一个地址。当我们创建C++对象后,在本地(native)方法中可以通过JNI提供的方法去获取JAVA对象,并通过JNI提供的本地方法,去给JAVA对象的属性赋值(把本地对象的指针地址赋值给JAVA对象中的属性保存)。
实现
JAVA端的代码:
public class NativeUtils {
long mObj;
public native int trackerInit(String path);
public native String trackerDealFrame(String path);
}
JAVA测试代码:
public class Test{
public static void main(String args[]){
NativeUtils myNativeUtils=new NativeUtils();
myNativeUtils.trackerInit(...);
myNativeUtils.trackerDealFrame(...);
}
}
C++代码如下:
JNIEXPORT jint JNICALL Java_com_shen_jni_NativeUtils_trackerInit
(JNIEnv *env, jobject thiz, jstring path) {
MyTracker* myTracker = new MyTracker();
jclass clazz = (jclass)(*env).GetObjectClass(thiz);
jfieldID fid = (jfieldID)(*env).GetFieldID(clazz, "mObj", "J");
(*env).SetLongField(thiz, fid, (jlong)myTracker);
……
}
C++代码中,参数有三个,env为JNI提供的上下文环境;thiz可以看做是调用该本地方法的JAVA对象;path是自己定义的形参。前面两个参数是JNI生成的本地方法默认添加的。
使用JAVA测试时,我们首先创建了一个包含本地方法的对象myNativeUtils
,则上文所说的JNI提供的两个默认参数的第二个thiz
可以当做该对象的引用。
- 使用
jclass clazz = (jclass)(*env).GetObjectClass(thiz);
获取JAVA对象。 - 使用
jfieldID fid = (jfieldID)(*env).GetFieldID(clazz, "mObj", "J");
去获取JAVA对象变量的ID。其中,“clazz”是第一步获取的对象,“mObj”是JAVA对象中保持C++对象地址的变量名,”J”是JAVA中long
类型的签名。(有点像JAVA中的反射机制) - 使用
(*env).SetLongField(thiz, fid, (jlong)myTracker);
调用JNI提供的方法去给JAVA对象的成员变量进行赋值操作,即保存了C++的指针地址。其中,“thiz”为JAVA对象的引用,”fid”为成员变量的ID,第三个参数是赋值的具体数据(这里我们使用指针的地址,并强制转换成jlong类型)。
以上我们已经将C++对象的指针地址保存在了JAVA对象的成员变量中。
下面在本地代码中,我们通过读取JAVA对象的成员变量,去恢复C++对象的引用。因为C++对象在内存已经初始化,我们只需要一个指针就可以访问该内存,并且长度还是该对象的长度,故可以恢复C++对象,继续进行调用。
jclass objClazz = (jclass)env->GetObjectClass(thiz);//obj为对应的JAVA对象
jfieldID fid = env->GetFieldID(objClazz, "mObj", "J");
jlong p = env->GetLongField(thiz, fid);
MyTracker* myTracker = (MyTracker *)p;
- 使用
jclass objClazz = (jclass)env->GetObjectClass(thiz);
去获取JAVA对象。 - 使用
jfieldID fid = env->GetFieldID(objClazz, "mObj", "J");
去获取”mObj”的属性值,第一个参数是JAVA对象,第二个是相对应的long变量,第三个是long类型对应的签名。 jlong p = env->GetLongField(thiz, fid);
通过对象引用和成员变量ID去获取该值。MyTracker* myTracker = (MyTracker *)p;
使用地址转成指针,获取到该对象的指针。