My application is an Android app with some native code.
The Java code starts a thread via new Thread(new Runnable), in the native code, I attach that thread to the JVM via AttachCurrentThread.
The native function which is called in that thread is an endless loop which will listen for simple events(boolean variables) and call another native function which by using the proper JNIEnv pointer and jobject will fill 2 java variables on the Java side and call a void method to display the data. The reason for that loop is that the overhead to calling the function normally(has a lot of allocation and destruction to do) is most likely huge.
The only problem is that while I can renew a JNIEnv pointer, I have no idea how to do it for a jobject.
And by jobject I mean the jobject which is passed to the native code via the function call like so
jint Java_com_example_example_MainActivity_NativeFunc( JNIEnv* env,
jobject obj)
{
return;
}
The Java code starts a thread via new Thread(new Runnable), in the native code, I attach that thread to the JVM via AttachCurrentThread.
You don't have to attach the JVM to a thread that was created with Java code, it is already attached.
If I understood your question correctly you get a jobject once and want to access that object later in other native methods. To accomplish that, you can use NewGlobalRef:
jobject myGlobalReference = env->NewGlobalRef(obj);
You can store that jobject in a global C/C++ variable and use it later. It will stay valid until the JVM dies or you delete the reference with DeleteGlobalRef. Keep in mind that the garbage collection won't collect the referenced object if all Java references to it are gone as long as you don't delete your global reference!
Related
I have a global object in native code which need to call Java code in it's constructor. Normally to get JavaVM pointer, I get the in JNI_OnLoad and cache it.
But the global object constructor is get called before JNI_OnLoad. And since you cannot really call JNI_GetCreatedJavaVMs or JNI_CreateJavaVM from Android native code.
Does anyone know how to get a JavaVM pointer before JNI_OnLoad get called?
Your help is appreciated.
For the sake of clarity, let's assume that you have libziron.so that has a global object with constructor that needs JavaVM* vm.
Build another library, lib1.so, which will have only
JavaVM* g_vm;
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
g_vm = vm;
return JNI_VERSION_1_4;
}
In Java, you load lib1.so and after that libziron.so. In libziron.so, you can now access extern g_vm. Note that while libziron.so depends on lib1.so, you must load them manually, in the right order.
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'm using JNI to get the music library from Android with Qt. I call upon the following Java method (which is already implemented),
public String getArtists(Context context)
...
I need to be able to get the Context of the application in order for it to work.
If it helps, when I was using Java, the following code provided the correct context.
MainActivity.this
Could anybody be of assistance in this problem?
Many thanks!
It will depend where you are making the call. Ideally you will cache the MainActivity pointer in C++.
One way to cache a pointer to use in a later JNI call is to add a native function in java such as native void onCreateNative() to you MainActivity class. In C++ you'll implement the method and cache the "thiz" pointer:
JNIEXPORT void JNICALL com_package_MainActivity_onCreateNative(JNIEnv *env, jobject thiz)
{
gCachedActivity = env->NewGlobalRef(thiz);
}
Now you can use gCachedActivity where you would have used MainActivity.this
env->CallObjectMethod(obj, s_getArtistsGetArtistsMethodID, gCachedActivity);
Of course replacing obj with the object you are calling the method on.
Lastly onCreateNative() should be called in the onCreate() method of MainActivity.
In my android application I've got a callback from a native thread into Java code which needs to be synchronized with the main UI thread. The intention is that the UI thread display a list of options based on information returned from the native thread. Until the user selects an option the native thread needs to block. After the user selects an option the native thread reads the value and continues running.
I've tried to implement this solution using a ConditionVariable however I get a VM error with the comment indicating "Fatal spin-on-suspend, dumping threads".
It looks as if it's not possible to use a Java based synchronization object to synchronize these threads. The code works perfectly in the case where I've got two Java threads.
In general is there any way to use a Java based synchronization object to synchronize a Java and native thread, or does this need to be implemented using the NDK with a call from the Java thread into an NDK function that implements the synchronization?
The way to do this is not to use a Java based sync object but rather an NDK based sync object as follows:
static pthread_cond_t uiConditionVariable = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t uiConditionMutex = PTHREAD_MUTEX_INITIALIZER;
/**
* This function opens the condition variable which releases waiting threads.
*/
JNIEXPORT void JNICALL
Java_com_Xxxx_openConditionVariable(JNIEnv *env,jobject o)
{
pthread_mutex_lock(&uiConditionMutex);
pthread_cond_signal(&uiConditionVariable);
pthread_mutex_unlock(&uiConditionMutex);
}
/**
* This function blocks on the condition variable associated with the
*/
JNIEXPORT void JNICALL
Java_com_Xxxx_blockConditionVariable(JNIEnv *env,jobject o)
{
pthread_mutex_lock(&uiConditionMutex);
pthread_cond_wait(&uiConditionVariable,&uiConditionMutex);
pthread_mutex_unlock(&uiConditionMutex);
}
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.