前面已经实现了APP->xxxManager->xxxManagerService->jni的函数调用,这篇博文就来实现jni->xxxManagerService的回调。
使用环境
我先说一下我的应用环境吧。我在有一个对设备节点进行轮询的线程,当能读到消息的时候就调用jni中的一个回调函数。回调函数会调用java中的函数来传递消息。
函数定义
JNI中回调函数的声明如下:
get_msg_cb(message *evtmsg)
它的参数是一个结构体,我在回调函数里将解析这个结构体,转化成int数组。再调用下面的Java函数:
private void recvMsgFromNative(int[] msg)
JNIEnv的对象
但凡做过JNI开发,都会知道,在调用JNI函数的时候,一个JNIEnv指针是必须具备的。在从Java调用的JNI函数的参数列表里面,第一个参数就是一个JNIEnv指针。但是对于我的get_msg_cb()函数,是从底层回调的,没有这个JNIEnv指针,该怎么办呢?
在JNI中,JNIEnv指针是与线程一一对应的,也就是说,每个线程都有一个JNIEnv,在线程一里不能使用线程二的JNIEnv。所以在register_android_server_iflytek_radiomanagerservice里面把JNIEnv保存在一个全局变量中,然后在get_msg_cb()函数里面使用,是不行的。但是,有一个对象却是全局的,那就是JavaVM。我们可以将JavaVM保存在全局变量中,然后通过JavaVM在get_msg_cb()里面获取JNIEnv指针。
static JavaVM * gJavaVM;
int register_android_server_radiomanagerservice(JNIEnv* env) {
// 此处省略很多行
// 获取JavaVM指针,保存在全局变量里面。
env->GetJavaVM(&gJavaVM);
return 0;
}
获取对象和函数的引用
Java中的非static函数是属于某个对象的,要想调用这个函数,就必须拥有这个对象的引用。我在Java里面定义了一个nativeInit()函数,并按照上一篇博文的方法映射到jni中的nativeInit()函数。在RadioManagerService()的构造函数里调用nativeInit()函数。在NativeInit()函数里面,通过JNIEnv和jobject获取了RadioManagerService对象的实例并保存在了全局变量中。同时还获取了recvMsgFromNative()函数的ID并保存在全局变量中。代码如下:
static jobject gRadioManagerServiceObj;
static jclass gRadioManagerServiceClass;
static void nativeInit(JNIEnv* env, jobject obj) {
ALOGD("nativeInit() is called");
mcu_rpc_init();
// 获取UartManagerService对象的实例
gRadioManagerServiceObj = env->NewGlobalRef(obj);
// Callbacks
FIND_CLASS(gRadioManagerServiceClass,
"com/android/server/RadioManagerService");
GET_METHOD_ID(gRadioManagerServiceClassInfo.recvMsgFromNative,
gRadioManagerServiceClass, "recvMsgFromNative", "([I)V");
// 注册回调
mcu_rpc_set_callback(get_msg_cb);
}
JNI函数的实现
下面就是JNI函数的实现了。这里直接给出代码:
void get_msg_cb(message *evtmsg) {
ALOGD("get_msg_cb() is called");
int i;
// 通过JavaVM获取JNIEnv指针
JNIEnv* env;
gJavaVM->AttachCurrentThread(&env, NULL);
if (env == NULL) {
ALOGD("JNIEnv is null");
return;
}
// 从message结构体获取数组
jintArray returnArray = getArrayFromMsg(env, evtmsg);
// 调用Java中的函数
if (gRadioManagerServiceObj != NULL) {
env->CallVoidMethod(gRadioManagerServiceObj,
gRadioManagerServiceClassInfo.recvMsgFromNative, returnArray);
}
// 这里的returnArray是在getArrayFromMsg()函数中通过env->NewIntArray()新建出来的,所以必须delete掉,以释放空间,不然会有内存泄露
env->DeleteLocalRef(returnArray);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}