JNI – 如何使用具有不同字段的多个Jni包装器实例?
背景
我有一个使用JNI(使用NDK)在Java和C / C ++中编码的android项目。
我已经在java端创建了一个Jni java包装器,它将自己完成所有Jni操作,而没有其他java类可以直接访问除此包装器之外的jni操作。
问题
问题是,我希望创建这个包装器的多个实例,而Jni部分应该有一个每个Jni包装器的实例。
这是一个问题,因为Jni部分为所有实例保存相同的字段。
这个问题
我怎样才能解决这个问题,这样对于jni包装器的每个java实例,jni部分都会有一个实例?
我在想,也许我可以将所有字段放入C ++类,并且有一个init()函数,它将为JniWrapper的CTOR返回它的新实例,从那时起,对于需要字段的每个JNI函数,它会将此类作为参数。 也许它可能是这个链接上显示的指针。
可悲的是,我不知道该怎么做。
有人可以帮忙吗?
样品
这是一个示例代码,我希望这些代码能让那些不了解问题的人更清楚:
java部分:
public class JniWrapper { static { System.loadLibrary("JniTest"); } private native void foo(Bitmap bitmap); }
jni部分:
... // sadly, all of those fields are actually a global fields int _intField; float _floatField; //those are just sample fields. i would also like to store pointers and objects... JNIEXPORT void JNICALL ...foo(JNIEnv * env, jobject obj, jobject bitmap) { // do something with the fields, as if they all belong to the JniWrapper, // and no other instances of JniWrapper are allowed to change them }
我找到了一个可能的解决方案(链接在这里 ),要么使用jlong或jobject作为句柄(或指针,如果你愿意)到JNI端创建的对象。
人们说最好使用Jobject作为ByteBuffer而不是jlong以获得更好的兼容性。
解决方案是:
Java方面:
private native ByteBuffer init(); private native void foo(ByteBuffer handle);
JNI方面:
/**a class to hold the fields*/ class FieldsHolder { ... //private fields, for each instance }
创建JNI对象并发送到java端:
JNIEXPORT jobject JNICALL ...init(JNIEnv * env, jobject obj) { FieldsHolder* myClass= new FieldsHolder(); ... //prepare fields of the class return env->NewDirectByteBuffer(myClass, 0); }
重用JNI对象:
JNIEXPORT void JNICALL ...foo(JNIEnv * env, jobject obj, jobject handle) { FieldsHolder* myClass= (FieldsHolder*) env->GetDirectBufferAddress(handle); //now we can access the fields again. }
您需要在JNI端具有C ++类,并且需要将C ++类的实例与JNI包装类的每个实例相关联。 您需要向new
添加本机方法并delete
C ++类实例,并且需要一种防弹方法来确保每次释放JNI包装类的实例时都会调用delete
-calling方法,例如通过close()
方法, finally{}
块,甚至是finalize()
方法:这是一种使用它是合法的情况。 您需要在每个Java实例中存储指向C ++实例的指针,例如,作为Java long
,您需要在C ++端获取它并将其转换为C ++类实例以获取每个实例数据。