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 native
- typedef 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 java
- if(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 array
- for(i = 0; i < len; i++)
- {
- *(pArray + i) = *(buf + i);
- }
- //copy buffer to jcharArray
- env->SetCharArrayRegion(array,0,len,pArray);
- //invoke java callback method
- env->CallVoidMethod(mCallbacksObj, method_receive,array,len);
- //release resource
- env->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.java
- public 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();
- }