现在的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),他们分别在程序的开始和结束的时候调用。我们可以把一些初始化和释放的工作在这两个方法中完成。
是不是很有意思?
相关推荐
NI是Java Native Interface的缩写,是Java平台的重要特性,使得Java代码可以方便地与C/C++代码...本文主要给出一份示例代码(工程文件见附件),描述如何在Android的JNI层开启一个线程,并在线程中回调Java层的函数。
对接微信支付统一下单接口时,下单支付成功后的回调工具类,相关的回调工具类使用,大家可以关注我的博客进入查看,有关键代码解析,配套使用更佳
Android 本目录下的代码都是来自于我的...JniCallback 演示了如何从Native线程回调Java的函数,相关博文:Android开发实践:JNI层线程回调Java函数示例 JniBuffer 演示了各种从Java端到Native层的Buffer传递方法,相
文件直接拷贝到.net项目里即可使用,文件所需要的DLL通过NuGet程序包可直接安装,压缩包中包含三个个文件,在使用的时候直接调用WeChatPayMain的方法并传入相应参数即可(无特殊支付需求不用改动),其他两个文件...
如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 15、error和exception有什么区别? error 表示恢复不是...
在 java 语言中,Java 程序的基本单位是类,也就是说:一个 Java 程序是由多个类组成 的。定义一个类与定义一个数据类型是有区别的。在程序设计语言中,把定义数据类型的能 力作为一种很重要的能力来对待。在面向...
3. Java与C/C++的交互:详细阐述了如何在Java代码中调用C/C++编写的本地方法,以及在C/C++中回调Java方法的具体实现。 4. 本地代码的编译与部署:介绍了如何将C/C++代码编译成动态链接库(.so文件),并将其部署到...
双向调用-JavaScript调用本机回调,反之亦然 跨越本机边界的真正对象可移植性-不仅是原始类型,而且没有需要同步的“冻结”类型 以本机友好的方式公开的异步Javascript方法(例如Promises) 完全向您的代码使用者...
如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 18、error和exception有什么区别? error 表示恢复...
只完成与支付相关的功能(支付、回调、查询、退款),没有其它业务逻辑相关模块,可直接接入生产环境 不同功能拆分成不同的服务,使得不同的支付服务更容易根据并发进行集群(可插拔) 使用了spring-boot2.0.0中新...
可以在方法中更改该值,但当控制传递回调用过程时,不会保留更改的值。通过使用方法参数关键字,可以更改这种行为。如果没有ref,out则默认为值传递,虽然可以在方法中修改这个参数的值,但是修改后的值不会还会到调用...
加abstract修饰符,并把普通方法的方法体(也就是方法后花括号括起来的部分)全部去掉,并在方法后增加分号 即可。 抽象类的特征 •抽象类的特征:有得有失,得到了新能力,可以拥有抽象方法;失去了...
4.4.5 避免在经常调用的方法、循环中创建Java对象 4.4.6 缓存经常使用的对象 4.4.7 尽量不要使用finalize方法 4.4.8 考虑使用SoftReference 4.5 小结 第5课 表达式中的陷阱 5.1 关于字符串的陷阱 5.1.1 JVM...
cordova自定义插件,实现toast,jar包的调用(利于对第三方jar包调用的理解),native与js的交互,实现android与js的信息回调。
目前市场业务中在产品以及其他项目的认证和检测方面存在诸多不便,用户需要实地考察并频繁与检测单位沟通,填写繁琐的纸质检测报告、当面送递样品,对于检测环节中存在的问题难以及时交互并处理。市场上相应的检测...