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

如何在android的jni线程中实现回调
分类: C/C++ Android 2012-03-16 11:38 711人阅读 评论(2) 收藏 举报

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

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

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


假设有c++函数:

[cpp] view plaincopy

    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:

[cpp] view plaincopy

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


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

[cpp] view plaincopy

    //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函数:

[cpp] view plaincopy

    //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中实现回调函数,以及其他实现:
[cpp] view plaincopy

    //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文件中共享库设置为:

[cpp] view plaincopy

    LOCAL_SHARED_LIBRARIES := liblog libcutils libandroid_runtime libnativehelper 


最后再实现Java中的Test类:

[java] view plaincopy

    //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(); 
     
    } 

你可能感兴趣的:(android)