如何在native代码里面回调java中的方法

8 篇文章 0 订阅
8 篇文章 0 订阅

            现在的Android应用,越来越多开始使用JNI调用了,把底层的复杂运算交给C/C++来完成,然后通过JNI来完成java与C/C++的连接。

            在我开发一个这样的应用的过程中,遇到了一个回调的问题。除了在java层调用C的实现,有时候也需要在C层调用java的方法。比如,在C层实现的事件处理器需要在事件发生时,上抛事件,告诉java层。

            这个过程和java调用C不一样,java中的调用是声明了native关键字的方法,在C层去实现他的方式来调用,他可以获得JNI interface 的指针(就是JNIEnv),可以获得虚拟机的上下文环境。而C中调用java中的方法是没有办法获得这些东西,必须通过java虚拟机来获得。Java提供了满足这种需求的API(Invocation API:http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html)。

 

 

             Invocation API允许应用区加载Java虚拟机到任意一个native应用,比如:

             

#include <jni.h>       /* where everything is defined */
    ...
    JavaVM *jvm;       /* denotes a Java VM */
    JNIEnv *env;       /* pointer to native method interface */
    JDK1_1InitArgs vm_args; /* JDK 1.1 VM initialization arguments */
    vm_args.version = 0x00010001; /* New in 1.1.2: VM version */
    /* Get the default initialization arguments and set the class 
     * path */
    JNI_GetDefaultJavaVMInitArgs(&vm_args);
    vm_args.classpath = ...;
    /* load and initialize a Java VM, return a JNI interface 
     * pointer in env */
    JNI_CreateJavaVM(&jvm, &env, &vm_args);
    /* invoke the Main.test method using the JNI */
    jclass cls = env->FindClass("Main");
    jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
    env->CallStaticVoidMethod(cls, mid, 100);
    /* We are done. */
    jvm->DestroyJavaVM();

    这是一个C++中获得虚拟机的example,是在JDK 1.1中实现的,现在已经跑不通了,经供参考,但是里面得到虚拟机原理是一样的。

 

      它用到了3个方法: Create VM,Attaching VM,Unloading VM.

       JNI_CreateJavaVM() 方法用来加载和初始化一个java虚拟机并且返回JNI接口的指针(JNIEnv), 这个线程被看做是主线程。

       JNIEnv是在当前线程中是合法的,如果其他的线程去访问java虚拟机,他必须调用一下AttachCurrentThread()方法,把自己附属到VM中才能获得JNIEnv。一旦调用成功,native的线程就像一个普通的java线程运行在native的线程中(native中的线程都是Linux线程,由内核调用执行)。native线程仍然连接到VM,直到它调用DetachCurrentThread()来分离。

       主线程不能从VM中分离自己,必须调用DestroyJavaVM()方法去卸载整个VM。

 

    有了这些概念,就很容易理解整个问题了。来看一下我们的主要代码调用:

 

    

//declare java VM and jobject
JavaVM* globalVM;
jobject globalObj;

//Get VM and JNIEnv at a native implemention method statement
JNIEXPORT jint JNICALL Java_com_ericsson_mstv_client_upnp_api_UPnPCtrlPointNative_ctrlPointStart(
		JNIEnv *env, jobject jObj) {
	globalObj = (*env)->NewGlobalRef(env, jObj);
	(*env)->GetJavaVM(env, &globalVM);
	return startCtrlPoint(uPnPCallback,NULL);
}

//event handler
void callback(....){
    JNIEnv *env;
    if(!globalVM || !globalObj){
           return;
     }
     int envState = (*globalVM)->GetEnv(globalVM, (void **) &env, JNI_VERSION_1_6);
     if (envState == JNI_EDETACHED) {
		envState = (*globalVM)->AttachCurrentThread(globalVM, &env, NULL);
		if (envState != 0) {
			//something log print
			return;
		}
	} else if (envState == JNI_EVERSION) {
		//something log print
		return;
	}
        //JNI data structure conversion
       ........
       (*globalVM)->DetachCurrentThread(globalVM);
}


JNIEXPORT jint JNICALL Java_com_ericsson_mstv_client_upnp_api_UPnPCtrlPointNative_ctrlPointStop(
		JNIEnv *env, jobject jObj) {
	(*env)->DeleteGlobalRef(env, globalObj);
	globalVM = NULL;
	return stopCtrlPoint();
}

 

 

   在这里用到了两个native方法,因为在在我们项目中的lifecycle的start和stop阶段调用,所以就把VM的一些逻辑放到里面来完成。其实Invocation API中提供了两个方法:jint JNI_OnLoad(JavaVM *vm, void *reserved) 和void JNI_OnUnload(JavaVM *vm, void *reserved),他们分别在程序的开始和结束的时候调用。我们可以把一些初始化和释放的工作在这两个方法中完成。

 

   是不是很有意思?

        

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值