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
Related
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.
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.
}
env->CallVoidMethod is returning java/lang/Class when using
env->GetObjectClass(aobject); //aobject was the argument sent by JNI to C++
aobject is a Java object that implements an interface.
jobject obj = env->GetObjectClass(aobject)
is supposed to return the Java object but instead is returning java/lang/Class
I encountered this error and had to ask around at work before I got a real answer.
The problem is that when you designate your native method as static, it supplies an instance of the jclass, not a jobject instance of that class, as it's called from a static context. (If you call getCanonicalName() on that jclass, it will return your class's name.)
If the native method needs to be static, then you should pass in the instance as an argument if you need it. Otherwise, just make it not static and you should be all fixed.
You haven't regenerated your .h/.c file since you removed 'static' so your JNI method signature doesn't match the Java one. You have an extra jclass in the argument list that is only there for static methods. So you're going to get some very strange execution.
The answer to my problem was described in "The Java Native Interface - Programmer's Guide and Specification" by Shen Liang.
"You can use Call< Type >Method family of functions to invoke interface methods as well. You must derive the method ID from the interface type"
Is JNI's "method signature" different if method is defined to return (static) interface ?
In my Java class I have this method:
public SharedPreferences.Editor getSharedPrefsEditor() {
return mActivity.getPreferences(Context.MODE_PRIVATE).edit();
}
SharedPreferences.Editor is a static interface in SharedPreferences.
In my C++ JNI code I do this:
// 'env' is the java environment that JNI passes to us
// 'jObject' is the one that JNI passes to us (along with env)
jclass javaCallerClass = env->GetObjectClass(jObject);
jmethodID methodId_getSharedPrefsEditor = env->GetMethodID(
javaCallerClass,
"getSharedPrefsEditor",
"()Landroid/content/SharedPreferences/Editor;");
For some odd reason, this doesn't work. It compiles, but at runtime I get this:
DEBUG/dalvikvm(19020): GetMethodID:
method not found:
Lcom/mangotaster/madballs/MyRenderer;.getSharedPrefsEditor:()Landroid/content/SharedPreferences/Editor;
I'm calling other methods in that class in pretty much the same way without any problems.
The only change seems to be the return value.
I did try to call the edit() function straight from JNI code, but got the same error - which makes me believe that my function signature "()Landroid/content/SharedPreferences/Editor;" is indeed wrong.
More info on the SharedPreferences class.
Nested/Inner classes don't use the standard namespace nomenclature in JNI signatures. The inner class is actually translated to a normal class at the same level as the outer class by the Java compiler with the name "Outer$Inner". I think you want "()Landroid/content/SharedPreferences$Editor;".
No need to guess about this, or ask on forums ;-) javap -s will tell you the correct JNI signature string for any Java method.
I'm currently using both C++ and Java in a project and I'd like to be able to send an object which is contained in C++ to my Java interface in order to modify it via a GUI and then send the modification back in C++.
So far I've been returning either nothing, an int or a boolean to Java via the JNI interface. This time I have to send an object through the interface. I have made similar class definition available both in C++ and in Java. I'd like to know how I'd go about creating the object so that I can use it in Java.
In C++ I have:
JNIEXPORT MyObject JNICALL Java_ca_X_Y_Z_C_1getMyObject(JNIEnv* env, jclass, jint number);
This function would get called by Java in order to get the object from the C++ side (the object is contained in a singleton, easily accessible).
On the Java end, I do a simple call to this method,
MyObject anObject = C_getMyObject(3);
which should return me the newly created object.
Java currently returns me a UnsatisfiedLinkError when I do the actual call. What is wrong?
Here's the solution I opted to use:
First, I would create a similar object in Java. Then, from C++ I would instanciate it and pass it all the values.
(C++)
clazz = env->FindClass("java/lang/Integer");
jmethodID method = env->GetMethodID(clazz, "<init>", "(I)V");
return env->NewObject(clazz, method, (jint)anInteger);
But then I realised this wasn't very portable and was a bit too complicated.
Instead, I decided to return a string that Java would parse and use to initialize the object on its side.
(JAVA)
String aString = "valuesoftheobject";
MyObject myObject(aString);
MyObject would have a constructor which takes a string. I believe the solution is simple and effective.
Another tool you should look at is SWIG. SWIG is a great tool for generate wrappers in other languages (such as Java, Python or C#) for existing C/C++ objects. It will generate automatic Java wrappers around C/C++ objects, and do all the heavy JNI lifting for you.
I use it extensively in Xuggler. To see an example, if you download the Xuggler source code there is a C++ object here:
csrc/com/xuggle/xuggler/IStreamCoder.h
I define a SWIG interface file here:
csrc/com/xuggle/xuggler/IStreamCoder.i
And when run through Swig it generates a Java object (which is stored here)
generate/java/com/xuggle/xuggler/IStreamCoder.java
We can then access that object from Java easily (well, I add some ref counting stuff, but that's pretty advanced). Hope that helps.
Art
If your MyObject class is defined in C++, you're not going to be able access its methods in Java. I'd try to define a Java wrapper class around your C object:
Java:
public C_Object() {
handle = createHandle();
}
private native long createHandle(); // or whatever pointer/handle type?
public void doStuff() {
_doStuff(handle);
}
private native void _doStuff(long handle);
If you can extrapolate a C api instead, you might try JNA.
Your UnsatisfiedLinkError may be the extra character in your function name as written above, or perhaps it can't handle the MyObject return value?