Tuesday, April 15, 2014

Android JNI Call back


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...