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

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

}













  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值