JNI - how to use multiple Jni wrapper instances with different fields? - java

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
}

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.

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

Related

How do I apply JNI on a CLASS instead of just random functions brought together?

I would like to implement an entire Java class via JNI, but I have only been able to find information about implementing methods, not classes themselves. In every example I've found, the JNI part is always just a bunch of functions.
I have a C++ class that I want to port to Java. I tried following some tutorials but failed. It doesn't seem to work for classes.
I have run into numerous problems; just one example would be what to do with the constructor of the class.
Problems are Example: what do i do with the constructor of the class.
You can keep the pointer of the inner Native class in a Java class member with long type.
In order to call the constructor of native class in Java class constructor, you can define a separate native method which allocates a native object and keep the resulting pointer in your Java class long type member.
With the example below, you can see a simple Java wrapper class and its JNI implementation which illustrates how to call construct and call inner native pointer:
public class JavaClassName {
private long nHandle;
protected JavaClassName() {
nHandle = ConstructNativeHandle();
}
public native void SomeMethodJava();
}
and jni implementation:
JNIEXPORT long JNICALL Java_packageName_JavaClassName_ConstructNativeHandle
(JNIEnv *env, jobject obj)
{
NativeClassName * pNativeObject = new NativeClassName();
return (jlong)pNativeObject;
}
JNIEXPORT void JNICALL Java_packageName_JavaClassName_SomeMethodJava
(JNIEnv * env, jobject obj)
{
jclass c = env->GetObjectClass(obj);
// J is the type signature for long:
jfieldID fid_handle = env->GetFieldID(c, "nHandle", "J");
NativeClassName * nativeObject = (NativeClassName *) env->GetLongField(obj, fid_handle);
nativeObject->SomeMethodNative();
return;
}
The reason you have been unable to find the information you are looking for is that it does not exist. JNI is primarily a mechanism for implementing Java native methods in C or C++, and secondarily a mechanism for embedding Java in C or C++. To serve those purposes it provides means for native code to access and manipulate Java objects, but it was never intended to allow Java classes themselves to be implemented in native code, and it makes no provision for that.
You can create a Java class whose methods are all native, and you can use such a class to wrap your C++ class (instances will each retain a pointer to an instance of the native class, most likely as a Java long), but you cannot compile native code into a Java class.
There are tools that can help with this sort of thing. First on my list to consider would be SWIG.

NewGlobalRef/DeleteGlobalRef when returning object created in JNI

When returning reference to object created in JNI method to Java, should we return result of NewGlobalRef call on created object? And, respectively, later, when object no more required in Java, do we need to call JNI method to do DeleteGlobalRef on the reference passed from Java?
Or NewGlobalRef/DeleteGlobalRef required only to hold references to Java object inside JNI, between JNI methods calls, and if we simply return created object to Java we dont need to do NewGlobalRef/DeleteGlobalRef calls?
I've seen such thing in a large open source project:
extern "C" JNIEXPORT jobject JNICALL
Java_foo_bar_Baz_create(JNIEnv* env, jclass, jint size) {
void* buf = malloc(size);
jobject obj = env->NewDirectByteBuffer(buf, size);
return env->NewGlobalRef(obj);
}
extern "C" JNIEXPORT void JNICALL
Java_foo_bar_Baz_free(JNIEnv* env, jclass, jobject jbuffer) {
void* buf = env->GetDirectBufferAddress(jbuffer);
free(buf);
}
It return result of NewGlobalRef call on created NewDirectByteBuffer object, and later, when object no more required in Java, there is a call to JNI method that does not call DeleteGlobalRef, but just call free() on the native buffer address.
When returning reference to New[Type]Array, or other object created in JNI method to Java, should we return result of NewGlobalRef call on created object?
Only if you also want to retain that reference for use in future JNI calls without receiving it again as a JNI method parameter.
And respectively, later, when object no more required in Java, do we need to call JNI method to do DeleteGlobalRef on the reference passed from Java?
Only if you called NewGlobalRef() on it.
Or NewGlobalRef/DeleteGlobalRef required only to hold references to Java object inside JNI, between JNI methods calls
Absolutely correct.
and if we simply return created object to Java we dont need to do NewGlobalRef/DeleteGlobalRef calls?
Correct again.
I saw returning result of NewGlobalRef on created NewDirectByteBuffer object (real buffer allocated via malloc()), in large open source project. And later, when object no more required in Java, there is a call to JNI method, that don't do DeleteGlobalRef, but just call free() on the native buffer address obtained from GetDirectBufferAddress.
That would constitute a leak of the DirectByteBuffer object, but not of its native buffer.

How to expose Java enum to native code through JNI

I'm using the enum pattern to define a singleton in my application. I need to access methods of this singleton, but I can't seem to pass the singleton correctly.
I've tried passing the enum and creating a jobject as a globalRef out of it like this:
class FrameElapsedListener : public gkEngine::Listener{
public:
JNIEnv* env;
jobject entityManager;
FrameElapsedListener(JNIEnv* env, jobject entityManager) :
env(env),
entityManager(env->NewGlobalRef(entityManager)){}
~FrameElapsedListener(){
env->DeleteGlobalRef(entityManager);
}
void tick(gkScalar rate);
};
but I got a crash when using it that the "local ref" was out of scope like this:
jclass entityManagerClass = env->FindClass(entityManagerClassPath.c_str());
jmethodID entityManagerFrameElapsedMethodId = env->GetMethodID(entityManagerClass, "frameElapsed", "(F)V");
env->CallVoidMethod(entityManager, entityManagerFrameElapsedMethodId, rate); //crashes here
Now I'm trying to just get the singleton out of the enum, but I'm not sure how to grab enum values like that. Any advice?
You can't save a JNIEnv pointer for use in a different thread. You have to get a new one in the thread, by calling AttachCurrentThread().
I was able to solve my problem by changing my method of ensuring a singleton from an enum with a single enum value, to a class with a private constructor, private instance, and a getInstance() method. I had read in other places that the JNI can not use Java enums natively, but I thought since Enum is a class, I could just treat it as an object. Apparently, that's not possible.
I have wrote a library which using Java Enum in C++ native code,I wrote a helper class which is simliar to yours,here it is,https://github.com/bugparty/jni_wifi_manager

Passing array/struct from native code (C) to Java [duplicate]

I've got some C functions which I am calling through JNI which take a pointer to a structure, and some other functions which will allocate/free a pointer to the same type of structure so that it is a bit easier to deal with my wrapper. Surprisingly, the JNI documentation says very little about how to deal with C structures.
My C header file looks like so:
typedef struct _MyStruct {
float member;
} MyStruct;
MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);
The corresponding JNI C wrapper file contains:
JNIEXPORT jobject JNICALL
Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {
return createNewMyStruct();
}
JNIEXPORT void JNICALL
Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,
jint numObjects, jobject arguments) {
int *actualData = (*env)->GetIntArrayElements(env, data, NULL);
processData(actualData, numObjects, arguments);
(*env)->ReleaseIntArrayElements(env, data, actualData, NULL);
}
...and finally, the corresponding Java class:
public class MyJavaClass {
static { System.loadLibrary("MyJniLibrary"); }
private native MyStruct createNewMyStruct();
private native void processData(int[] data, int numObjects, MyStruct arguments);
private class MyStruct {
float member;
}
public void test() {
MyStruct foo = createNewMyStruct();
foo.member = 3.14159f;
int[] testData = new int[10];
processData(testData, 10, foo);
}
}
Unfortunately, this code crashes the JVM right after hitting createNewMyStruct(). I'm a bit new to JNI and have no idea what the problem could be.
Edit: I should note that the C code is very vanilla C, is well-tested and was ported from a working iPhone project. Also, this project is using the Android NDK framework, which lets you run native C code from an Android project from within JNI. However, I don't think that this is strictly an NDK issue... it seems like a JNI setup/initialization error on my part.
You need to create a Java class with the same members as C struct, and 'map' them in the C code via methods env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, and so on - in short, lots of manual labor, hopefully there already exist programs that do it automatically: JNAerator (http://code.google.com/p/jnaerator) and SWIG (http://www.swig.org/). Both have their pros and cons, the choice is up to you.
It's crashing because Java_com_myorg_MyJavaClass_createNewMyStruct is declared to return jobject, but is actually returning struct MyStruct. If you ran this with CheckJNI enabled, the VM would complain loudly and abort. Your processData() function is also going to be fairly upset about what it gets handed in arguments.
A jobject is an object on the managed heap. It can have extra stuff before or after the declared fields, and the fields don't have to be laid out in memory in any particular order. So you can't map a C struct on top of a Java class.
The most straightforward way to deal with this was identified in an earlier answer: manipulate the jobject with JNI functions. Allocate the objects from Java or with NewObject, Get/Set the object fields with appropriate calls.
There are various ways to "cheat" here. For example, you could include a byte[] in your Java object that holds sizeof(struct MyStruct) bytes and then use GetByteArrayElements to get a pointer to it. A bit ugly, especially if you want to access the fields from the Java side as well.
C structure is the collection of variables (some are function pointer). Pass to java is not a good idea. In general, it is the problem how to pass more complex type to java, like pointer.
In JNI book, to keep the pointer/structure in native and export manipulation to java is recommended. You can read some useful articles. The JavaTM Native Interface Programmer's Guide and Specification, I have read. 9.5 Peer Classes have a solution to deal with it.
Make the class on both the Java and C++ sides, just putting in the member variables. C++ structs are really just classes with public data members. If you are really in pure C, stop reading now.
Use your IDE(s) to make setters and getters for the member variables automatically.
Use javah to generate the C header file from the Java class.
Do some editing on the C++ side to make the setters and getters match the generated header file.
Put in the JNI code.
This is not an ideal solution, but it may save you a little time, and it will at least give you a skeleton that you can edit. This functionality could be added to an IDE, but without a big demand, it probably won't happen. Most IDEs don't even support mixed language projects, let alone having them talk to each other.

How to pass C structs back and forth to Java code in JNI?

I've got some C functions which I am calling through JNI which take a pointer to a structure, and some other functions which will allocate/free a pointer to the same type of structure so that it is a bit easier to deal with my wrapper. Surprisingly, the JNI documentation says very little about how to deal with C structures.
My C header file looks like so:
typedef struct _MyStruct {
float member;
} MyStruct;
MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);
The corresponding JNI C wrapper file contains:
JNIEXPORT jobject JNICALL
Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {
return createNewMyStruct();
}
JNIEXPORT void JNICALL
Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,
jint numObjects, jobject arguments) {
int *actualData = (*env)->GetIntArrayElements(env, data, NULL);
processData(actualData, numObjects, arguments);
(*env)->ReleaseIntArrayElements(env, data, actualData, NULL);
}
...and finally, the corresponding Java class:
public class MyJavaClass {
static { System.loadLibrary("MyJniLibrary"); }
private native MyStruct createNewMyStruct();
private native void processData(int[] data, int numObjects, MyStruct arguments);
private class MyStruct {
float member;
}
public void test() {
MyStruct foo = createNewMyStruct();
foo.member = 3.14159f;
int[] testData = new int[10];
processData(testData, 10, foo);
}
}
Unfortunately, this code crashes the JVM right after hitting createNewMyStruct(). I'm a bit new to JNI and have no idea what the problem could be.
Edit: I should note that the C code is very vanilla C, is well-tested and was ported from a working iPhone project. Also, this project is using the Android NDK framework, which lets you run native C code from an Android project from within JNI. However, I don't think that this is strictly an NDK issue... it seems like a JNI setup/initialization error on my part.
You need to create a Java class with the same members as C struct, and 'map' them in the C code via methods env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, and so on - in short, lots of manual labor, hopefully there already exist programs that do it automatically: JNAerator (http://code.google.com/p/jnaerator) and SWIG (http://www.swig.org/). Both have their pros and cons, the choice is up to you.
It's crashing because Java_com_myorg_MyJavaClass_createNewMyStruct is declared to return jobject, but is actually returning struct MyStruct. If you ran this with CheckJNI enabled, the VM would complain loudly and abort. Your processData() function is also going to be fairly upset about what it gets handed in arguments.
A jobject is an object on the managed heap. It can have extra stuff before or after the declared fields, and the fields don't have to be laid out in memory in any particular order. So you can't map a C struct on top of a Java class.
The most straightforward way to deal with this was identified in an earlier answer: manipulate the jobject with JNI functions. Allocate the objects from Java or with NewObject, Get/Set the object fields with appropriate calls.
There are various ways to "cheat" here. For example, you could include a byte[] in your Java object that holds sizeof(struct MyStruct) bytes and then use GetByteArrayElements to get a pointer to it. A bit ugly, especially if you want to access the fields from the Java side as well.
C structure is the collection of variables (some are function pointer). Pass to java is not a good idea. In general, it is the problem how to pass more complex type to java, like pointer.
In JNI book, to keep the pointer/structure in native and export manipulation to java is recommended. You can read some useful articles. The JavaTM Native Interface Programmer's Guide and Specification, I have read. 9.5 Peer Classes have a solution to deal with it.
Make the class on both the Java and C++ sides, just putting in the member variables. C++ structs are really just classes with public data members. If you are really in pure C, stop reading now.
Use your IDE(s) to make setters and getters for the member variables automatically.
Use javah to generate the C header file from the Java class.
Do some editing on the C++ side to make the setters and getters match the generated header file.
Put in the JNI code.
This is not an ideal solution, but it may save you a little time, and it will at least give you a skeleton that you can edit. This functionality could be added to an IDE, but without a big demand, it probably won't happen. Most IDEs don't even support mixed language projects, let alone having them talk to each other.

Categories