im currently trying to integrate a c library to my android project.
The extern function accepts a Array<Double> for example
val doubleArray= arrayOf(0.0)
MyClass().myFunction(doubleArray)
in the native part of the application, im trying to access it like this:
Java_com_mypackage_MyClass_myFunction(JNIEnv *env, jobject thiz,jdoubleArray myArray) {
jdouble *body_ = (*env)->GetDoubleArrayElements(env, myArray, 0);
}
This is the way, how the documentation tells me to access my array values. But when im doing this, i'm getting a SIGABRT.
What am i missing here? Would appreciate when you can point me the direction :)
An Array<Double> is a java.lang.Double[] in JVM terms, ie an array of objects.
You meant to create a DoubleArray, which is a double[] (the primitive).
Related
To learn basic practice presence am calling string to my application. I know its too basic but am new to native languages. Like:
jstring Java_com_idiasoft_LoginActivity_NdkdealerName(JNIEnv* env, jobject javaThis)
{
return (*env)->NewStringUTF(env, "User Name");
}
Here I can call this string in particular class. But I wanna make dynamic call. like two or more classes. where am uses the same string. How can I do this?
Couldn't find a perfect solution regarding NDK but I think creating a global class in Java part could help you in this situation.
Just define that particular native function in your global class and use it anywhere.
If there is no need to instantiate the class you can also make it static and use it anywhere in your application.
Hope this helps.
I have those two pieces of code, the first is:
JNIEXPORT jlongArray* JNICALL Java_com_home_overlay_activity_MainActivity_ProcessPointer(JNIEnv* env, jobject) {
jlongArray blobs_arr;
return &blobs_arr;
}
and the second is:
JNIEXPORT jlongArray JNICALL Java_com_home_overlay_activity_MainActivity_Process(JNIEnv* env, jobject) {
jlongArray blobs_arr;
return blobs_arr;
}
all I want is to return long array to the java code.
The first runs okay while the second not, is there any issue here with returning a long array this way ??
There are no pointers in Java, so I think that if the first snippet of code actually works, it will not produce what you want at all. It probably returns the memory address of the C jlongArray.
As for the second piece of code, I can't see any problem with it except that it returns an uninitialized object, maybe NULL, maybe some random memory garbage, which probably causes unexpected behavior on the Java side. Maybe you should try initializing it to NULL in the C part, or try making your code snippet more realistic by actually filing the array so you can test the code behavior in real conditions.
My application was not reading the jni.h but working .. which is weird, after I set the NDKROOT variable it all worked correctly
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 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.
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.