前面介绍了jni的基本概念和如何使用jni技术达到 Java 调用 C 的目的。
但是没有说 C 如何通过 jni 来调用 java 的方法,也就是如何实现回调。实现回调的作用很大,例如我们使用了 upnp 本地库,当库接收到消息便需要往 java 端传递。又例如使用了 C 实现了播放器,但是进度显示需要使用到 java。这些时候就需要用到回调了。
##实现
先看看主要部分
:::java
static void callback_handler(char *s) {
int status;
JNIEnv *env;
bool isAttached = false;
status = gJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4);
if(status < 0) {
LOGE("callback_handler: failed to get JNI environment, "
"assuming native thread");
status = gJavaVM->AttachCurrentThread(&env, NULL);
if(status < 0) {
LOGE("callback_handler: failed to attach "
"current thread");
return;
}
isAttached = true;
}
/* Construct a Java string */
jstring js = env->NewStringUTF(s);
jclass interfaceClass = env->GetObjectClass(gInterfaceObject);
if(!interfaceClass) {
LOGE("callback_handler: failed to get class reference");
if(isAttached) gJavaVM->DetachCurrentThread();
return;
}
/* Find the callBack method ID */
jmethodID method = env->GetStaticMethodID(
interfaceClass, "callBack", "(Ljava/lang/String;)V");
if(!method) {
LOGE("callback_handler: failed to get method ID");
if(isAttached) gJavaVM->DetachCurrentThread();
return;
}
env->CallStaticVoidMethod(interfaceClass, method, js);
if(isAttached) gJavaVM->DetachCurrentThread();
}
假设在 java 工程中有一个叫 callBack 的静态方法,接收 string 类型的参数。我们就可以通过调用例子中的方法实现。
:::java
jmethodID method = env->GetStaticMethodID(
interfaceClass, "callBack", "(Ljava/lang/String;)V");
但是在这个主要的方法里面还有很多东西需要注意。
首先是 gJavaVM
,看到 g 就知道是个全局变量指针。这个指针指向java虚拟机,这个程序运行时是不变的,所以我们可以使用全局变量保存它。而我们需要在 JNI_OnLoad
这个方法中获得这个指针。
第二个需要注意的变量是 env
,函数首先尝试使用 GetEnv 方法获取 env ,失败后再尝试使用 AttachCurrentThread 获得。这样做是因为 env 跟线程相关,每个线程拥有独立的 env 。
只要能拿到 env ,一切就变得简单了。但是还有一个变量没有提到,就是 gInterfaceObject
,这是个很重要的变量,其保存了其保存了 JNIExampleInterface 对象的指针。我们的 callBack 方法就是在这个对象中。在这个例子中,我们一开始就实例化了这个对象,并把对象保存到全局变量 gInterfaceObject 中。
:::java
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
......
initClassHelper(env, kInterfacePath, &gInterfaceObject);
......
}
void initClassHelper(JNIEnv *env, const char *path, jobject *objptr) {
jclass cls = env->FindClass(path);
if(!cls) {
LOGE("initClassHelper: failed to get %s class reference", path);
return;
}
jmethodID constr = env->GetMethodID(cls, "<init>", "()V");
if(!constr) {
LOGE("initClassHelper: failed to get %s constructor", path);
return;
}
jobject obj = env->NewObject(cls, constr);
if(!obj) {
LOGE("initClassHelper: failed to create a %s object", path);
return;
}
(*objptr) = env->NewGlobalRef(obj);
}
initClassHelper 方法首先获得类的构造函数,接着通过 NewObject 实例化该类。
源代码可访问 github : https://github.com/gavinlin/JNICallbackExample