I asked this question the other day, but wasn't too specific, so I want to re-clarify.
I am creating an Android Application which uses an existing library in C using the NDK. The problem I have run into is that the C code uses a lot of things java doesn't ( function pointers as parameters is the big problem ).
Anyway, I was wondering if I could write functions in my Java code that the C code calls. Now from what I can tell, you can do it, so I would appreciate it if no one just answered 'Yes you can, LINK." I have been looking into it but its very over my head as to what actually needs to be done.
Can anyone try to explain the process? I know it involves creating a JVM in the C code; any information that will help a newbie get on his feet will be greatly appreciated.
Thanks
EDIT :
So, I don't know what to do for these three steps.
To call a specific Java function from C, you need to do the following:
Obtain the class reference using the FindClass(,,) method.
Obtain the method IDs of the functions of the class that you want to call using the
GetStaticMethodID and GetMethodID function calls.
Call the functions using CallStaticVoidMethod, CallStaticIntMethod, and CallStaticObjectMethod.
This isn't explained too much and I have literally no experience in C. Is FindClass a C method?
Every C function that is callable from Java via JNI has a first parameter of type JNIEnv*. On the C end, this is a pointer to a pointer to a structure with a bunch of pointers to functions. Those functions are your interface to the Java world. FindClass, GetMethodID and the rest are among them.
So when you want to call FindClass from the C side, here's how you do it:
void Java_com_mypackage_MyClass_MyMethod(JNIEnv *jniEnv, jobject thiz)
{
jclass *clazz = (*(*jniEnv)->FindClass)(jniEnv, "com/mypackage/SomeClass");
jmethodID MethodID = (*(*jniEnv)->GetStaticMethodID)(jniEnv, clazz, "SomeMethod", "(I)I");
int result = (*(*jniEnv)->CallStaticIntMethod)(jniEnv, clazz, MethodID, 18);
And so forth. The line dereferences the jniEnv parameter, gets a function pointer and calls the function through it. Class and method names are completely bogus, naturally. How would I know yours.
Note: I'm talking of function pointers here, but not in the same sense as you do; those are function pointers to functions that JNI provides, not to your functions.
The verbosity of call syntax has to do with the limitations of C; in C++, you can write instead
jclass *cl = jniEnv->FindClass("com/mypackage/SomeClass");
as C++ supports function table pointers of this sort natively via virtual functions.
You can probably take some shortcuts along the way. If you're calling methods in the same class as your C point of entry, and it happens to be static, your second parameter already is a class object pointer. If you have a this pointer to the object you want to invoke a method on, you can use GetObjectClass.
Related
I want to do some initialization job in library. Any clue would be great. thanks.
A constructor of a global/static C++ object might be a good place (but don't forget about extern "C"{} around JNI methods in the .cpp file). The constructor, however, doesn't get a JNIEnv pointer and can't do anything with the Java world.
Alternatively, introduce a static native method in the class that does the loadLibrary() call, and invoke this method right after the loadLibrary() call. This is probably simpler - no need to go C++.
I use regular std::map to map jobject's to c++ objects. The problem with this approach is that it may fail for other types of references, e.g. global references are actually different pointer than regular local references even if they reference the same object. The correct way to compare if two references reference the same object is:
env->IsSameObject(jobj1, jobj2);
So, my question is: what's the correct way to map jobject's to c++ objects? The obvious reply to wrap jobject into some c++ class that overloads operator== and calls IsSameObject isn't the reply that I'm looking for. I'd like to know if there is way to do it without going back and forth between JVM and native c/c++ for every compare operation.
EDIT: global reference is a jni global reference, and has nothing to do with c++ references.
EDIT2: I'd like to clarify what the problem is with this code:
std::map<jobject, void *> jobjs;
jobject obj1, obj2;
... some code that sets these obj1 and obj2 to some Java objects.
jobjs[obj1] = new CppPeer;
CppPeer * = jobjs[obj1]; //OK...
if(objs.find(obj2) == objs.end()){
assert(obj2 != obj1);
//Here's the problem: here a new c++ CppPeer
//created for obj2, but the problem is that
//even if obj1 != ob2 it doesn't mean that
//they actually reference different java objects
//On the next line error might happen
jobjs[obj2] = new CppPeer; //maybe not OK here...
}
The other problem with IsSameObject is that it makes things pretty nasty and messy. Not only now I'd need to keep a pointer to JVM, but also whenever I need to compare jobjects I'd need to attach thread etc to get pointer to JNIEnv to be able to check jobject
EDIT3: Be aware, that according to android docs you cannot even assume that two references reference the same object if they are equal. I don't know how it's possible, but there's the part from Android's JNI tips page:
One consequence of this is that you must not assume object references
are constant or unique in native code. The 32-bit value representing
an object may be different from one invocation of a method to the
next, and it's possible that two different objects could have the same
32-bit value on consecutive calls. Do not use jobject values as keys.
DISCLAIMER: it smells.
On Android, pointer and jint are of the same size. Consider adding an int field to your Java class to store a pointer to the native conterpart. When in a JNI method, typecast it back to a pointer.
To be on a slightly safer side, do a static assert on datatype matching:
{char a[sizeof(void*) == sizeof(jint) ? 1 : -1]};
If it's compiled on a system where it's not, you'll get a compilation error.
For slightly less smell, use a static/global map from int to your objects, use an ID generator, and instead of object pointer store relatively unique (within process lifetime) objects IDs within the Java object.
I am writing the C++/Native side of a text/VoIP program that utilizes JNI. Currently there are three functions on the C++ side that will likely be called by the Java code regularly. The frequency at which the functions are called will depend on how often the user sends/receives text messages and how active their friends are in changing their presence/status. Each of the three functions follow the pseudo code below and I am not sure if it would be wise (or even possible) to "globalize" anything within the functions.
JNIEnv *env;
if (jvm_->GetEnv((void**) &env, JNI_VERSION_1_6) < 0)
{
[print error and return]
}
jclass stringclass = env->FindClass("java/lang/String"); // Same for all 3
jstring passinfo = env->NewStringUTF([str-info-to-pass]); // Different for all 3
jclass cls = env->FindClass([directory to calling Java function class]); // Same for all 3
[check if cls found, print error if not found and return]
jmethodID methID = env->GetStatisMethodID([arguments for the function]); // Different for all 3
[check if methID found, print error if not found and return]
jobjectArray args = env->NewObjectArray([arguments to build argument structure being passed to function]);
[call Java function, passing right arguments]
I was thinking that I should/would be able to move the jclass stringclass, jclass cls and JNIEnv *env out of the functions and make them globals that were set during the JNI_OnLoad function call. Is this possible/advisable? Will these values be somehow different from the OnLoad function to these functions? Should I just instantiate them as globals and still set them each time in the function call? And, will doing any of these really increase performance in any noticeable amount?
Possible, yes. Wise, it depends. Define for yourself what is a "noticeable amount". What cadence of JNI calls you expect? I think you could see some measurable impact of caching starting at 10+ calls per second. Is that a case? Anyway, in my personal experience, nicely done caching cleans up the code, makes it shorter and more readable. So i will give some advice anyway.
Caching results of FindClass, yes absolutely. Generally anything reusable and "static", what you are looking up by a symbolic name - which introduces a penalty of symbolic lookup, contrary to simply using an already cached pointer. Liang JNI book talks also about field and method ids, so your GetStaticMethodID applies. jmethodID is simpler in the sense that the result of Get(Static)MethodID can be directly cached: it is valid for any further calls. Unlike FindClass which returns a local reference, so for valid caching (and to make jmethodID really persistent) you must create NewGlobalRef from it - and delete it when appropriate. A nice comprehensive example is found in the JNI_OnLoad tutorial. On the other hand, there is no point in caching JNIEnv, you get it again with every JNI call. Cached JNIEnv would be also invalid if you happen to call the JNI methods from another thread than the one from which you cached.
You should not worry about performance.
Currently I develop a voip client where i used native codec. This means that the JNI is called for every rtp packet (two times in every 20 msec because both encoding and decoding) also passing the JNIEnv with each call. This variable is just a pointer. Consumed CPU usage for JNI calls are below 1% on a low end device for me in these circumstances so you should not worry at all if your only need is texting.
When I run the code, I get an error "failed adding to JNI local ref table has 512 entries"
This is my code:
jstring pJNIData = pJNIEnv->NewStringUTF ( variables[0].GetStringValue() );
pJNIEnv->CallStaticVoidMethod ( pJNIActivityClass, pJNIMethodIDStartTime, pJNIData ) ;
pJNIEnv->DeleteLocalRef(pJNIData);
I have read several suggestions, but none of them work! In spite of the DeleteLocalRef, it fails to works. The function is used in a profiler that literally calls all the functions...
I have seen this when a JNI method called Java code (in my case, the method was not static). As I understand, unused local references are not automatically deleted when a Java method is called from JNI (I mean, until the top-level JNI function returns).
IIRC either there already was information about memory objects in the log, or I could add some logging; from that information I identified garbage items that I did not mention before. They were two arrays and a class, created in subsequent calls but not garbage-collected.
// in a function that calls a Java method from JNI
jbyteArray srcArray = env->NewByteArray(len);
jclass cls = env->FindClass("com/something/MyClass");
jmethodID mid = env->GetMethodID(cls, "mymethod", "([BI)[B");
jbyteArray resArray = (jbyteArray)env->CallObjectMethod(obj, mid, srcArray, XXXX);
...
env->DeleteLocalRef(cls);
env->DeleteLocalRef(resArray);
env->DeleteLocalRef(srcArray);
// no need to do anything with mid
Note that although these three local references were obtained differently, all of them were hanging around.
Useful link:
http://www.netmite.com/android/mydroid/dalvik/docs/jni-tips.html#local_vs_global_references
(or find the Dalvik VM docs dalvik/docs/jni-tips.html and locate the section "Local vs. Global References")
Every object that JNI returns is a "local reference". This means that it's valid for the duration of the current native method in the current thread. Even if the object itself continues to live on after the native method returns, the reference is not valid. This applies to all sub-classes of jobject, including jclass and jarray. [...] Note: method and field IDs are just 32-bit identifiers, not object references, and should not be passed to NewGlobalRef. The raw data pointers returned by functions like GetStringUTFChars and GetByteArrayElements are also not objects.
I thought I would chip in just in case anyone else runs into this issue. This is a weird case that kept me confused for hours!
Ok so I have an NDK app and the Java code being called is inside an apk that is loaded at runtime. I have no idea if the runtime loading effects this in any way but I thought I should mention it.
Now in a c++ method I use find class and getmethodid to get the constuctor to a HashMap and call it to get a new HashMap instance. I then populate the HashMap from the c++ side using jni calls. So far so good.
I then pass the HashMap to java code and, again, all is working as expected. Once the java code has returned I call DeleteLocalRef on the HashMap. No errors are thrown but the reference is not deleted.
This only came up when I finally ran over 512 local references (from multiple calls to this function) and the error dump showed that the last 10 items in the localref store were nearly all HashMaps. I would understand that the GC would not collect these references at the end of the method as I am make a multithreaded ndk app. However the DeleteLocalRef should have worked.
The Fix:
In the end I found that creating the HashMap from a jni call to a java method I wrote was fine, and the reference was then free'able. It seems crazy to have a java function that literally just returns a new HashMap but it worked so for now I am living with it :)
This question has been asked and answered in many posts like this!
But how can I call from c++ directly ? For this how can I get JNIEnv* and jobject ?
Is this possible ?
To get JNIEnv you can write global JNI_OnLoad function that will get called during loading of shared library. This function will get JavaVM pointer as argument. Using it you can get JNIEnv for current thread (or create new one if there was no JNIEnv previously).
As to where get jobject - if that is new object you are instantiating, then you use JNIEnv::NewObject method. Otherwise you need to pass object on which you want to call method from java side to C/C++.
You need to read the Invocation section of the JNI Specification.