In the above images shows all possible scenarios of Android JNI calls and its call backs.
Android UI mostly implemented in Java, then if needed there will be C++ implementation to perform low level operations. In this case JNI is used.
1. Java to C++ calls :
First UI will need to make some call to C++, for example start engine etc.
In Java:
1.1. Need to write a native method:
package demo;
class Service{
public static native doSomething();
}
1.2. Call this method to invoke/access the low level.
....
//Somewhere
Service.doSomething();
....
In C++:
1.2. Need to write the implementation for above native method:
JNIEXPORT jint JNICALL Java_demo_Service_doSomeThing(JNIEnv * env, jobject ){
..............
..............
}
2. C++ to Java Call
There may be some cases C++ Implementation may need to call back UI, for example inform the progress update.
This can happen from the same thread which the initial called made from Java or C++ may have created a new thread and may call from there.
In Java: Write a Observer to receive those call backs.
package demo;
class Service{
public static void onCallBack(){
............
//add implementation to handle call back
...............
}
}
Any callback from C++ to Java need to be implemented using given JNI refection APIs.
jclass jcls = env->FindClass("dem/Service");
jmethodID mid = env->GetStaticMethodID(jcls,"onCallBack","....");
env->CallStaticVoidMethod(jcls,mid);
This will perform what we want, but we will need some more extra implementation to make it work in caseof concurrent access from c++.
2.1 Calling from same thread.
In this case, the above listing will work without any issue. We can use the same JNIEnv which passed in Java_demo_Service_doSomeThing call above 1.2
2.2 Calling from other thread
In this case we cannot use the above env passed with Java_demo_Service_doSomeThing as it is invalid in other threads.
So We have to create a new JNIEnv using JavaVM, which is passed in JNI_OnLoad.
So,
2.2.1 Write a JNI_OnLoad in your native c++ code:
static JavaVM * s_Jvm;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
s_Jvm = vm;//Keep for future use.
return JNI_VERSION_1_6;
}
2.2.2 Attach Current Thread to create JNIEnv:
JNIEnv * env;
s_Jvm->AttachCurrentThread(&env,NULL);
2.2.3 If you try FindClass with this JNIEnv it will return NULL in Android since uses special Class Loader in Java and in New thread which created in C++ will use the
jclass jcls = env->FindClass("dem/Service");
2.2.4 So this should be done inside OnLoad method : (code are in bold) and assign to global reference to use in future.
jclass s_jcls;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
s_Jvm = vm;//Keep for future use.
s_jcls = env->NewGlobalRef(env->FindClass("dem/Service"));
return JNI_VERSION_1_6;
}
2.2.5 Now you can use this s_jcls to make any call backs
GetStaticMethodID
CallStaticObjectMethod
etc...