EasyDarwin安卓直播之EasyPusher NDK開發:JNI回撥函式的實現
阿新 • • 發佈:2019-01-26
最近在做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 |