JNI - how to use multiple Jni wrapper instances wi

2019-02-28 14:05发布

问题:

background

I have an android project that uses JNI (using NDK) to code in both Java and C/C++.

I've created a Jni java wrapper on the java side that will do all the Jni oprerations by itself, while no other java class can access the jni operations directly other than this wrapper.

the problem

problem is , i wish to create multiple instances of this wrapper, while the Jni part should have an instance per Jni wrapper.

this is a problem, since the Jni part holds the same fields for all instances.

the question

How can i solve this problem, so that for each java instance of the jni wrapper, there will be an instance on the jni part?

I was thinking, maybe I could put all of the fields into a C++ class, and have an init() function that will return a new instance of it for the CTOR of the JniWrapper, and from then, for each JNI function that needs fields, it will get this class as a parameter . maybe it could be a pointer as shown on this link.

sadly, i have no idea how to do it.

can anyone please help?

sample

here's a sample code that I hope will make things clearer for those who didn't understand the problem:

java part:

public class JniWrapper
  {
  static
    {
    System.loadLibrary("JniTest");
    }

  private native void foo(Bitmap bitmap);
  }

jni part:

...
// 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
  }

回答1:

You need to have C++ classes on the JNI side, and you need to associate an instance of the C++ class with each instance of your JNI wrapper class. You will need to add native methods to new and delete the C++ class instances, and you will need a bullet-proof way of ensuring that the delete-calling method gets called every time an instance of your JNI wrapper class is released, e.g. via a close() method, finally{} blocks, or even a finalize() method: this is one case where its usage is legitimate. You need to store a pointer to the C++ instance in each Java instance, e.g. as a Java long, and you need to get hold of that on the C++ side and cast it to a C++ class instance to get hold of the per-instance data.



回答2:

I've found a possible solution (link here), to either use a jlong or jobject to be a handle (or pointer, if you wish) to the object that was created on the JNI side.

people said it's better to use a jobject as ByteBuffer instead of jlong for better compatibility.

the solution is :

Java side:

private native ByteBuffer init();
private native void foo(ByteBuffer handle);

JNI side:

/**a class to hold the fields*/
class FieldsHolder
  {
  ... //private fields, for each instance
  }

creating the JNI object and sending to java side:

JNIEXPORT jobject JNICALL ...init(JNIEnv * env, jobject obj)
  {
  FieldsHolder* myClass= new FieldsHolder();
  ... //prepare fields of the class
  return env->NewDirectByteBuffer(myClass, 0);
  }

re-using the JNI object :

JNIEXPORT void JNICALL ...foo(JNIEnv * env, jobject obj, jobject handle)
  {
  FieldsHolder* myClass= (FieldsHolder*) env->GetDirectBufferAddress(handle);
  //now we can access the fields again.
  }