如何在android的jni线程中实现回调

来源:互联网 发布:linux系统启动命令 编辑:程序博客网 时间:2024/04/29 21:44

JNI回调是指在c/c++代码中调用java函数,当在c/c++的线程中执行回调函数时,会导致回调失败。

其中一种在Android系统的解决方案是:

把c/c++中所有线程的创建,由pthread_create函数替换为由Java层的创建线程的函数AndroidRuntime::createJavaThread。


假设有c++函数:

void *thread_entry(void *args){while(1){printf("thread running...\n");sleep(1);}}void init(){pthread_t thread;pthread_create(&thread,NULL,thread_entry,(void *)NULL);}

init()函数创建一个线程,需要在该线程中调用java类Test的回调函数Receive:

public void Receive(char buffer[],int length){String msg = new String(buffer);msg = "received from jni callback:" + msg;Log.d("Test", msg);}


首先在c++中定义回调函数指针:

//test.h#include <pthread.h>//function type for receiving data from nativetypedef void (*ReceiveCallback)(unsigned char *buf, int len);/** Callback for creating a thread that can call into the Java framework code. *  This must be used to create any threads that report events up to the framework. */typedef pthread_t (* CreateThreadCallback)(const char* name, void (*start)(void *), void* arg);typedef struct{ReceiveCallback recv_cb;CreateThreadCallback create_thread_cb;}Callback;

再修改c++中的init和thread_entry函数:

//test.c#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <sys/wait.h>#include <unistd.h>#include "test.h"void *thread_entry(void *args){char *str = "i'm happy now";Callback cb = NULL;int len;if(args != NULL){cb = (Callback *)args;}len = strlen(str);while(1){printf("thread running...\n");//invoke callback method to javaif(cb != NULL && cb->recv_cb != NULL){cb->recv_cb((unsigned char*)str, len);}sleep(1);}}void init(Callback *cb){pthread_t thread;//pthread_create(&thread,NULL,thread_entry,(void *)NULL);if(cb != NULL && cb->create_thread_cb != NULL){cb->create_thread_cb("thread",thread_entry,(void *)cb);}}


然后在jni中实现回调函数,以及其他实现:

//jni_test.c#include <stdlib.h>#include <malloc.h>#include <jni.h>#include <JNIHelp.h>#include "android_runtime/AndroidRuntime.h"#include "test.h"#define RADIO_PROVIDER_CLASS_NAME "com/tonny/Test"using namespace android;static jobject mCallbacksObj = NULL;static jmethodID method_receive;static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {    if (env->ExceptionCheck()) {        LOGE("An exception was thrown by callback '%s'.", methodName);        LOGE_EX(env);        env->ExceptionClear();    }}static void receive_callback(unsigned char *buf, int len){int i;    JNIEnv* env = AndroidRuntime::getJNIEnv();jcharArray array = env->NewCharArray(len);jchar *pArray ;if(array == NULL){LOGE("receive_callback: NewCharArray error.");return; }pArray = (jchar*)calloc(len, sizeof(jchar));if(pArray == NULL){LOGE("receive_callback: calloc error.");return; }//copy buffer to jchar arrayfor(i = 0; i < len; i++){*(pArray + i) = *(buf + i);}//copy buffer to jcharArrayenv->SetCharArrayRegion(array,0,len,pArray);//invoke java callback method    env->CallVoidMethod(mCallbacksObj, method_receive,array,len);//release resourceenv->DeleteLocalRef(array);free(pArray);pArray = NULL;    checkAndClearExceptionFromCallback(env, __FUNCTION__);}static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg){    return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);}static Callback mCallbacks = {receive_callback,create_thread_callback};static void jni_class_init_native(JNIEnv* env, jclass clazz){method_receive = env->GetMethodID(clazz, "Receive", "([CI)V");}static int jni_init(JNIEnv *env, jobject obj){if (!mCallbacksObj)mCallbacksObj = env->NewGlobalRef(obj);return init(&mCallbacks);}static const JNINativeMethod gMethods[] = {  { "class_init_native","()V",(void *)jni_class_init_native },    { "native_init","()I",(void *)jni_init },};  static int registerMethods(JNIEnv* env) {  const char* const kClassName = RADIO_PROVIDER_CLASS_NAME;    jclass clazz;     /* look up the class */      clazz = env->FindClass(kClassName);      if (clazz == NULL) {          LOGE("Can't find class %s/n", kClassName);          return -1;      }  /* register all the methods */      if (env->RegisterNatives(clazz,gMethods,sizeof(gMethods)/sizeof(gMethods[0])) != JNI_OK)      {          LOGE("Failed registering methods for %s/n", kClassName);          return -1;      }  /* fill out the rest of the ID cache */      return 0;  }   jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL;  jint result = -1;  LOGI("Radio JNI_OnLoad");      if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {      LOGE("ERROR: GetEnv failed/n");      goto fail;  }  if(env == NULL){goto fail;}if (registerMethods(env) != 0) {     LOGE("ERROR: PlatformLibrary native registration failed/n");      goto fail;  }  /* success -- return valid version number */      result = JNI_VERSION_1_4;  fail:  return result;  } 

jni的Android.mk文件中共享库设置为:

LOCAL_SHARED_LIBRARIES := liblog libcutils libandroid_runtime libnativehelper

最后再实现Java中的Test类:

//com.tonny.Test.javapublic class Test {static{try {System.loadLibrary("test");class_init_native();} catch(UnsatisfiedLinkError ule){            System.err.println("WARNING: Could not load library libtest.so!");        }}public int initialize() {return native_radio_init();}public void Receive(char buffer[],int length){String msg = new String(buffer);msg = "received from jni callback" + msg;Log.d("Test", msg);}protected  static native void class_init_native();protected  native int native_init();}