I am facing crash with
JNI WARNING : 0x44f81e80 is not a valid JNI reference, in Ldalvik/system/NativeStart;. run()v (GetObjectClass)
I found some post saying use NewWeakGlobalRef instead NewGlobalRef. But I am looking for validation in my code. All I want to know is is there any JNI api using which I can check whether given jobject is valid JNI reference or not? Something like unsigned
char isValid = (env)IsValidJNIRef(myjobject);
Using that isValid's value I can put some condition to handle system.
Thanks.
You can check the ref type of a jobject referred to by the obj argument using:
jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);
Which returns one of the following enumerated values defined as a jobjectRefType:
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3
Note that you can't use this on deleted refs - it is not specified what value the GetObjectRefType will return for a deleted ref.
See: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#objreftype
Related
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).
I am trying to use the android AccountManager from qt c++ code. To add a account, I want to create an instance of android.accounts.Account, I am trying to do this with this code:
jstring jUsername = QAndroidJniObject::fromString(username).object<jstring>();
jstring jPassword = QAndroidJniObject::fromString(password).object<jstring>();
jstring jType = QAndroidJniObject::fromString(type).object<jstring>();
qDebug()<<"Creating";
QAndroidJniObject accountObject("android.accounts.Account","(Ljava/lang/String;Ljava/lang/String;)V",jUsername,jType);
qDebug()<<"Inserting";
The code segfaults at the line, where the accountObject is created ("Creating" is printed, "Inserting" not):
JNI ERROR (app bug): accessed deleted global reference 0x100e46
JNI ERROR (app bug): accessed deleted global reference 0xe46
I read this occurs, if I call a method with a wrong signature, but the signature is right (see here).
By the looks of it, the way you're creating your strings are causing your problem.
jstring jUsername = QAndroidJniObject::fromString(username).object<jstring>();
What this does is create an anonymous temporary QAndroidJniObject (returned by fromString), which you then extract the wrapped jobject from (and cast it to a jstring). By the time execution of that statement finishes the lifetime of that QAndroidJniObject is over, and the reference it held to the wrapped jobject will be released.
You could change your code to somethine like this:
auto qjUsername = QAndroidJniObject::fromString(username);
auto jUsername = qjUsername.object<jstring>();
Or to:
jstring jUsername = env->NewLocalRef(QAndroidJniObject::fromString(username).object<jstring>());
Assuming that you have a way of getting the JNIEnv*.
If you create a new reference you should probably also delete it with DeleteLocalRef when you don't need it anymore.
I am curious why the jni programming guide said
The JNI provides a slightly cleaner interface for C++ programmers. The
jni.h file contains a set of inline C++ functions so that the native
method programmer can simply write:
jclass cls = env->FindClass("java/lang/String");
instead of:
jclass cls = (*env)->FindClass(env, "java/lang/String");
The extra level of indirection on env and the env argument to
FindClass are hidden from the programmer. The C++ compiler simply
expands out the C++ member function calls to their C counterparts;
therefore, the resulting code is exactly the same.
What is the difference between c and c++ pointers?
The C++ pointer is a pointer to a class object. The C pointer is a pointer to a function. They are two different things.
Because the C++ object already knows the environment, you do not need to pass the environment again.
The C pointer is a pointer to a struct, and in that struct you call the function. The function has no state, so you need to pass the state (in this case, the same env struct, so it can find the variables in this struct.).
In jni.h in the JDK you see that the C++ function is:
jclass FindClass(const char *name) {
return functions->FindClass(this, name);
}
And that functions is a pointer inside the object that points to the env. So the C++ version internally expands to the C version.
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
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.