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

简介:

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

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

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


假设有c++函数:

[cpp]  view plain copy
  1. void *thread_entry(void *args)  
  2. {  
  3.     while(1)  
  4.     {  
  5.         printf("thread running...\n");  
  6.         sleep(1);  
  7.     }  
  8.       
  9.       
  10. }  
  11.   
  12. void init()  
  13. {     
  14.     pthread_t thread;  
  15.     pthread_create(&thread,NULL,thread_entry,(void *)NULL);  
  16. }  

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

[cpp]  view plain copy
  1. public void Receive(char buffer[],int length){  
  2.         String msg = new String(buffer);  
  3.         msg = "received from jni callback:" + msg;  
  4.         Log.d("Test", msg);  
  5. }  


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

[cpp]  view plain copy
  1. //test.h  
  2. #include <pthread.h>  
  3. //function type for receiving data from native  
  4. typedef void (*ReceiveCallback)(unsigned char *buf, int len);  
  5.   
  6. /** Callback for creating a thread that can call into the Java framework code. 
  7.  *  This must be used to create any threads that report events up to the framework. 
  8.  */  
  9. typedef pthread_t (* CreateThreadCallback)(const char* name, void (*start)(void *), void* arg);  
  10.   
  11. typedef struct{  
  12.     ReceiveCallback recv_cb;  
  13.     CreateThreadCallback create_thread_cb;  
  14. }Callback;  

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

[cpp]  view plain copy
  1. //test.c  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <pthread.h>  
  5. #include <sys/wait.h>  
  6. #include <unistd.h>  
  7. #include "test.h"  
  8.   
  9. void *thread_entry(void *args)  
  10. {  
  11.     char *str = "i'm happy now";  
  12.     Callback cb = NULL;  
  13.     int len;  
  14.     if(args != NULL){  
  15.         cb = (Callback *)args;    
  16.     }  
  17.       
  18.     len = strlen(str);  
  19.     while(1)  
  20.     {  
  21.         printf("thread running...\n");  
  22.         //invoke callback method to java  
  23.         if(cb != NULL && cb->recv_cb != NULL){  
  24.             cb->recv_cb((unsigned char*)str, len);  
  25.         }  
  26.         sleep(1);  
  27.     }  
  28.       
  29.       
  30. }  
  31.   
  32. void init(Callback *cb)  
  33. {     
  34.     pthread_t thread;  
  35.     //pthread_create(&thread,NULL,thread_entry,(void *)NULL);  
  36.     if(cb != NULL && cb->create_thread_cb != NULL)  
  37.     {  
  38.         cb->create_thread_cb("thread",thread_entry,(void *)cb);  
  39.     }  
  40. }  


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

[cpp]  view plain copy
  1. //jni_test.c  
  2. #include <stdlib.h>  
  3. #include <malloc.h>  
  4. #include <jni.h>  
  5. #include <JNIHelp.h>  
  6. #include "android_runtime/AndroidRuntime.h"  
  7.   
  8. #include "test.h"  
  9. #define RADIO_PROVIDER_CLASS_NAME "com/tonny/Test"  
  10.   
  11.   
  12. using namespace android;  
  13.   
  14.   
  15. static jobject mCallbacksObj = NULL;  
  16. static jmethodID method_receive;  
  17.   
  18.   
  19.   
  20. static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {  
  21.     if (env->ExceptionCheck()) {  
  22.         LOGE("An exception was thrown by callback '%s'.", methodName);  
  23.         LOGE_EX(env);  
  24.         env->ExceptionClear();  
  25.     }  
  26. }  
  27.   
  28.   
  29.   
  30. static void receive_callback(unsigned char *buf, int len)  
  31. {  
  32.     int i;  
  33.     JNIEnv* env = AndroidRuntime::getJNIEnv();  
  34.     jcharArray array = env->NewCharArray(len);  
  35.     jchar *pArray ;  
  36.       
  37.     if(array == NULL){  
  38.         LOGE("receive_callback: NewCharArray error.");  
  39.         return;   
  40.     }  
  41.   
  42.     pArray = (jchar*)calloc(len, sizeof(jchar));  
  43.     if(pArray == NULL){  
  44.         LOGE("receive_callback: calloc error.");  
  45.         return;   
  46.     }  
  47.   
  48.     //copy buffer to jchar array  
  49.     for(i = 0; i < len; i++)  
  50.     {  
  51.         *(pArray + i) = *(buf + i);  
  52.     }  
  53.     //copy buffer to jcharArray  
  54.     env->SetCharArrayRegion(array,0,len,pArray);  
  55.     //invoke java callback method  
  56.     env->CallVoidMethod(mCallbacksObj, method_receive,array,len);  
  57.     //release resource  
  58.     env->DeleteLocalRef(array);  
  59.     free(pArray);  
  60.     pArray = NULL;  
  61.       
  62.     checkAndClearExceptionFromCallback(env, __FUNCTION__);  
  63. }  
  64.   
  65. static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)  
  66. {  
  67.     return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);  
  68. }  
  69.   
  70. static Callback mCallbacks = {  
  71.     receive_callback,  
  72.     create_thread_callback  
  73. };  
  74.   
  75.   
  76.   
  77. static void jni_class_init_native  
  78. (JNIEnv* env, jclass clazz)  
  79. {  
  80.     method_receive = env->GetMethodID(clazz, "Receive""([CI)V");  
  81. }  
  82.   
  83. static int jni_init  
  84. (JNIEnv *env, jobject obj)  
  85. {  
  86.   
  87.       
  88.     if (!mCallbacksObj)  
  89.         mCallbacksObj = env->NewGlobalRef(obj);  
  90.       
  91.     return init(&mCallbacks);  
  92. }  
  93.   
  94. static const JNINativeMethod gMethods[] = {    
  95.     { "class_init_native",          "()V",          (void *)jni_class_init_native },  
  96.     { "native_init",                "()I",          (void *)jni_init },  
  97. };    
  98.   
  99.   
  100.   
  101. static int registerMethods(JNIEnv* env) {    
  102.   
  103.   
  104.     const charconst kClassName = RADIO_PROVIDER_CLASS_NAME;  
  105.     jclass clazz;     
  106.     /* look up the class */    
  107.     clazz = env->FindClass(kClassName);    
  108.     if (clazz == NULL) {    
  109.         LOGE("Can't find class %s/n", kClassName);    
  110.         return -1;    
  111.     }    
  112.     /* register all the methods */    
  113.     if (env->RegisterNatives(clazz,gMethods,sizeof(gMethods)/sizeof(gMethods[0])) != JNI_OK)    
  114.     {    
  115.         LOGE("Failed registering methods for %s/n", kClassName);    
  116.         return -1;    
  117.     }    
  118.     /* fill out the rest of the ID cache */    
  119.     return 0;    
  120. }     
  121.   
  122.   
  123. jint JNI_OnLoad(JavaVM* vm, void* reserved) {   
  124.     JNIEnv* env = NULL;    
  125.     jint result = -1;    
  126.     LOGI("Radio JNI_OnLoad");    
  127.         if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {    
  128.         LOGE("ERROR: GetEnv failed/n");    
  129.         goto fail;    
  130.     }    
  131.   
  132.     if(env == NULL){  
  133.         goto fail;  
  134.     }  
  135.     if (registerMethods(env) != 0) {   
  136.         LOGE("ERROR: PlatformLibrary native registration failed/n");    
  137.         goto fail;    
  138.     }    
  139.     /* success -- return valid version number */        
  140.     result = JNI_VERSION_1_4;    
  141. fail:    
  142.     return result;    
  143. }   

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

[cpp]  view plain copy
  1. LOCAL_SHARED_LIBRARIES := liblog libcutils libandroid_runtime libnativehelper  

最后再实现Java中的Test类:

[java]  view plain copy
  1. //com.tonny.Test.java  
  2.   
  3. public class Test {  
  4.   
  5.     static{  
  6.         try {  
  7.             System.loadLibrary("test");  
  8.             class_init_native();  
  9.               
  10.         } catch(UnsatisfiedLinkError ule){  
  11.             System.err.println("WARNING: Could not load library libtest.so!");  
  12.         }  
  13.           
  14.     }  
  15.       
  16.       
  17.   
  18.     public int initialize() {  
  19.         return native_radio_init();  
  20.     }  
  21.   
  22.     public void Receive(char buffer[],int length){  
  23.         String msg = new String(buffer);  
  24.         msg = "received from jni callback" + msg;  
  25.         Log.d("Test", msg);  
  26.     }  
  27.       
  28.       
  29.       
  30.     protected  static native void class_init_native();  
  31.       
  32.     protected  native int native_init();  
  33.   
  34. }  
相关文章
|
2月前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
【7月更文挑战第28天】在Android开发中,确保UI流畅性至关重要。多线程与异步编程技术可将耗时操作移至后台,避免阻塞主线程。我们通常采用`Thread`类、`Handler`与`Looper`、`AsyncTask`及`ExecutorService`等进行多线程编程。
44 2
|
20天前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
68 1
|
5天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
24 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
7天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
24 10
|
5天前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
6天前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
1月前
|
调度 Android开发 开发者
【颠覆传统!】Kotlin协程魔法:解锁Android应用极速体验,带你领略多线程优化的无限魅力!
【8月更文挑战第12天】多线程对现代Android应用至关重要,能显著提升性能与体验。本文探讨Kotlin中的高效多线程实践。首先,理解主线程(UI线程)的角色,避免阻塞它。Kotlin协程作为轻量级线程,简化异步编程。示例展示了如何使用`kotlinx.coroutines`库创建协程,执行后台任务而不影响UI。此外,通过协程与Retrofit结合,实现了网络数据的异步加载,并安全地更新UI。协程不仅提高代码可读性,还能确保程序高效运行,不阻塞主线程,是构建高性能Android应用的关键。
37 4
|
1月前
|
算法 数据处理 开发工具
Android平台RTSP|RTMP播放器如何回调YUV或RGB数据
在开发Android平台上的RTSP或RTMP播放器时,开发者不仅追求低延迟播放,还希望获取解码后的视频数据(如YUV或RGB格式),以便进行视觉算法分析。使用大牛直播SDK中的SmartPlayer,可在确保播放流畅的同时,通过设置外部渲染器(`SmartPlayerSetExternalRender`)来高效地回调原始视频数据。例如,对于RGBA数据,需实现`NTExternalRender`接口,并重写相关方法以处理数据和尺寸变化。同样地,对于I420(YUV)数据,也需要相应地实现接口以满足需求。这种方式使得开发者能在不影响常规播放功能的情况下,进行定制化的视频处理任务。
|
18天前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享安卓与iOS开发中的线程管理比较
【8月更文挑战第30天】本文将探讨网络安全与信息安全的重要性,并分享关于网络安全漏洞、加密技术和安全意识的知识。我们将了解常见的网络攻击类型和防御策略,以及如何通过加密技术和提高安全意识来保护个人和组织的信息安全。
|
27天前
|
开发工具 Android开发
Android项目架构设计问题之SDK内部减少每次回调时的冗余判断逻辑如何解决
Android项目架构设计问题之SDK内部减少每次回调时的冗余判断逻辑如何解决
16 0