EasyDarwin安卓直播之EasyPusher NDK开发:JNI回调函数的实现

最近在做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 
#include 
#include 

#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

你可能感兴趣的:(Android)