版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013758734/article/details/51114175
最近在做EasyDarwin的EasyPusher手机直播项目开发时涉及到JNI回调,今日便研究了一下,跟native调用Java层的代码不同,此文说的是直接通过setCallback的方式去实现回调:
先看一下加载so库的代码,我就直接在Activity中使用了:
package org.easydarwin.hellojni;
import android.app.Activity;
import android.util.Log;
import android.os.Bundle;
public class HelloJni extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setValueSetCallback(new OnValueSetCallback() {
@Override
public void onValueSet(String value) {
Log.i("callback__", "callback__" + value);
}
});
setValue("1234");
setValue("12345");
setValue("123456");
}
/**
* 回调接口
*/
public interface OnValueSetCallback {
void onValueSet(String value);
}
/**
* 设置回调函数
*/
public native void setValueSetCallback(OnValueSetCallback callback);
public native void setValue(String value);
static {
System.loadLibrary("hello-jni");
}
}
上面的文件是直接在ndk的hello-jni demo的基础上修改的。OnValueSetCallback就是事件的回调接口,通过setValueSetCallback设置回调函数,onValueSet为具体回调处理方法,这里测试的时候是将通过setValue方法set的value在callback中打印出来,来证明回调事件调用成功了。
下面看具体的native层代码:
#include <pthread.h>
#include <jni.h>
#include <stdio.h>
#define _JNI_VERSION JNI_VERSION_1_4
#define THREAD_NAME "lib_hello_jni"
JNIEnv *jni_get_env(const char *name);
/*
* Pointer to the Java virtual machine
* Note: It's okay to use a static variable for the VM pointer since there
* can only be one instance of this shared library in a single VM
*/
static JavaVM *myVm;
static pthread_key_t jni_env_key;
static jobject callback_obj = NULL;
/*
* This function is called when a thread attached to the Java VM is canceled or
* exited
*/
static void jni_detach_thread(void *data) {
(*myVm)->DetachCurrentThread(myVm);
}
JNIEnv *jni_get_env(const char *name) {
JNIEnv *env;
env = pthread_getspecific(jni_env_key);
if (env == NULL) {
if ((*myVm)->GetEnv(myVm, (void **) &env, _JNI_VERSION) != JNI_OK) {
/* attach the thread to the Java VM */
JavaVMAttachArgs args;
jint result;
args.version = _JNI_VERSION;
args.name = name;
args.group = NULL;
if ((*myVm)->AttachCurrentThread(myVm, &env, &args) != JNI_OK)
return NULL;
/* Set the attached env to the thread-specific data area (TSD) */
if (pthread_setspecific(jni_env_key, env) != 0) {
(*myVm)->DetachCurrentThread(myVm);
return NULL;
}
}
}
return env;
}
/**
* java文件在执行System.loadLibrary("hello-jni"); 的时候会调用此方法
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
// Keep a reference on the Java VM.
myVm = vm;
if ((*vm)->GetEnv(vm, (void **) &env, _JNI_VERSION) != JNI_OK)
return -1;
/* Create a TSD area and setup a destroy callback when a thread that
* previously set the jni_env_key is canceled or exited */
if (pthread_key_create(&jni_env_key, jni_detach_thread) != 0)
return -1;
return _JNI_VERSION;
}
void Java_com_example_hellojni_HelloJni_setValueSetCallback(JNIEnv *env, jobject thiz,jobject callback) {
if (callback_obj != NULL)
(*env)->DeleteGlobalRef(env, callback_obj);
//callback就是java层传进来的回调事件,在这里赋值给一个全局变量
callback_obj = callback ? (*env)->NewGlobalRef(env, callback) : NULL;
}
//内部回调处理函数
void jni_callback(jstring value) {
JNIEnv *env;
if (!(env = jni_get_env(THREAD_NAME)))
return;
if (callback_obj == NULL) {
return;
}
//GetObjectClass是通过传入jni中的一个java的引用来获取该引用的类型。
//FindClass是通过传java中完整的类名来查找java的class,
jclass cls = (*env)->GetObjectClass(env, callback_obj);
//根据class以及方法名、参数信息获取medthodId
//onValueSet就是方法名称,(Ljava/lang/String;)V中的Ljava/lang/String;代表该方法的参数类型,V代表Void,是方法返回类型
//如果一个方法名称为abc,有两个int参数,返回值为String则应该写作:(*env)->GetMethodID(env, cls, "abc", "(II)Ljava/lang/String")
jmethodID methodId = (*env)->GetMethodID(env, cls, "onValueSet", "(Ljava/lang/String;)V");
//根据methodId执行这个方法,最后的value是参数,注意这个value必须为jstring类型的,否则报错
(*env)->CallVoidMethod(env, callback_obj, methodId, value);
//清除cls引用
(*env)->DeleteLocalRef(env, cls);
}
void Java_com_example_hellojni_HelloJni_setValue(JNIEnv *env, jobject thiz, jstring value) {
// char *tmp = (*env)->GetStringUTFChars(env, value, NULL);
jni_callback(value);
}
在这个例子中不设置JavaVM *myVm这个全局变量也可以,直接将setValue中的env变量传给jni_callback也可以,这里全局变量是应对异步操作的情况。
java类型对应的类型签名列表:
java类型 | 类型签名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | B |
类 | L全限定名;,比如String, 其签名为Ljava/lang/util/String; |
数组 | [类型签名, 比如 [B |