前端如何调用api接口得到JavaVM,JNIEnv接口

6695人阅读
JNI规范(10)
本章讲述如何把一个JVM嵌入到你的本地程序当中去。一个JVM可以看作就是一个本地库。本地程序可以链接这个库,然后通过“调用接口”(invocation&interface)来加载JVM。实际上,JDK中标准的启动器也就是一段简单的链接了JVM的C代码。启动器解析命令、加载JVM、并通过“调用接口”(invocation&interface)运行JAVA程序。7.1&创建JVM我们用下面这段C代码来加载一个JVM并调用Prog.main方法来演示如何使用调用接口。public&class&Prog&{&&&&&public&static&void&main(String[]&args)&{&&&&&&&&&&System.out.println(&Hello&World&&&+&args[0]);&&&&&}&}下面是启动器:#include&&jni.h&&&#define&PATH_SEPARATOR&';'&/*&define&it&to&be&':'&on&Solaris&*/&#define&USER_CLASSPATH&&.&&/*&where&Prog.class&is&*/&&main()&{&&&&&JNIEnv&*&&&&&JavaVM&*&&&&&jint&&&&&&jclass&&&&&&jmethodID&&&&&&jstring&&&&&&jclass&stringC&&&&&jobjectArray&&&#ifdef&JNI_VERSION_1_2&&&&&JavaVMInitArgs&vm_&&&&&JavaVMOption&options[1];&&&&&options[0].optionString&=&&&&&&&&&&-Djava.class.path=&&USER_CLASSPATH;&&&&&vm_args.version&=&0x;&&&&&vm_args.options&=&&&&&&vm_args.nOptions&=&1;&&&&&vm_args.ignoreUnrecognized&=&JNI_TRUE;&&&&&/*&Create&the&Java&VM&*/&&&&&res&=&JNI_CreateJavaVM(&jvm,&(void**)&env,&&vm_args);&#else&&&&&JDK1_1InitArgs&vm_&&&&&char&classpath[1024];&&&&&vm_args.version&=&0x;&&&&&JNI_GetDefaultJavaVMInitArgs(&vm_args);&&&&&/*&Append&USER_CLASSPATH&to&the&default&system&class&path&*/&&&&&sprintf(classpath,&&%s%c%s&,&&&&&&&&&&&&&vm_args.classpath,&PATH_SEPARATOR,&USER_CLASSPATH);&&&&&vm_args.classpath&=&&&&&&/*&Create&the&Java&VM&*/&&&&&res&=&JNI_CreateJavaVM(&jvm,&&env,&&vm_args);&#endif&/*&JNI_VERSION_1_2&*/&&&&&&if&(res&&&0)&{&&&&&&&&&fprintf(stderr,&&Can't&create&Java&VM\n&);&&&&&&&&&exit(1);&&&&&}&&&&&cls&=&(*env)-&FindClass(env,&&Prog&);&&&&&if&(cls&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&&mid&=&(*env)-&GetStaticMethodID(env,&cls,&&main&,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&([Ljava/lang/S)V&);&&&&&if&(mid&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&jstr&=&(*env)-&NewStringUTF(env,&&&from&C!&);&&&&&if&(jstr&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&stringClass&=&(*env)-&FindClass(env,&&java/lang/String&);&&&&&args&=&(*env)-&NewObjectArray(env,&1,&stringClass,&jstr);&&&&&if&(args&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&(*env)-&CallStaticVoidMethod(env,&cls,&mid,&args);&&destroy:&&&&&if&((*env)-&ExceptionOccurred(env))&{&&&&&&&&&(*env)-&ExceptionDescribe(env);&&&&&}&&&&&(*jvm)-&DestroyJavaVM(jvm);&}上面的代码有条件地编译一个初始化JDK1_1InitArgs这个structure。这个structure是JDK1.1下特有的,尽管JDK1.2也会支持,但JDK1.2引入了一个更通用的叫作JavaVMInitArgs的VM初始化structure。常量JNI_VERSION_1_2在JDK1.2下定义,JDK1.1下是没有的。当目标平台是1.1时,C代码首先调用JNI_GetDefaultJavaVMInitArgs来获得默认的VM设置。这个调用会返回heap&size、stack&size、默认类路径等信息,并把这些信息存放在参数vm_args中。然后我们把Prog.class所在的目录附加到vm_args.classpath中。当平台目标是1.2时,C代码创建了一个JavaVMInitArgs的structure。VM的初始化参数被存放在一个JavaVMOption数组中。设置完VM初始化structure后,C程序调用JNI_CreateJavaVM来加载和初始化JVM,传入的前两个参数:<span style="color:#、&接口指针jvm,指向新创建的JVM。<span style="color:#、&当前线程的JNIEnv接口指针env。本地代码通过env指针访问JNI函数。当函数JNI_CreateJavaVM函数成功返回时,当前本地线程已经把自己的控制权交给JVM。这时,它会就像一个本地方法一样运行。以后就可以通过JNI函数来启动Prog.main方法。接着,程序调用DestroyJavaVM函数来unloadJVM。不幸的是,在JDK1.1和JDK1.2中你不能unloadJVM,它会一直返回一个错误码。运行上面的程序,产生如下输出:Hello&World&from&C!<span style="color:#.2&把本地程序和JVM链接在一起通过调用接口,你可把invoke.c这样的程序和一个JVM链接到一起。怎么样链接JVM取决于本地程序是要和一个特定的VM一起工作,还是要和多个具体实现方式未知的不同VM一起工作。<span style="color:#.2.1&和一个己知的JVM链接到一起这种情况下,你可以把你的本地程序和实现了JVM的本地库链接在一起。编译链接成功后,你就可以运行得到的可执行文件。运行时,你可能会得到一个错误信息,比如“无法找到共享库或者动态链接库”,在Windows下,错误信息可能会指出无法发现动态链接库javai.dll(JDK1.1)或者jvm.dll(JDK1.2),这时,你需要把DLL文件加载到你的PATH环境变量中去。<span style="color:#.2.2&和未知的多个JVM链接到一起这种情况下,你就不能把本地程序直接和一个特定的库链接在一起了。比如,JDK1.1的库是javai.dll,而JDK1.2的库是jvm.dll。解决方案是根据本地程序的需要,用运行时动态链接来加载不同的VM库。例如,下面的win32代码,根据给定的VM库的路径找到JNI_CreateJavaVM函数的入口。LoadLibrary和GetProcAddress是Win32平台上用来动态链接的API。虽然LoadLibrary可以实现了JVM的本地库的名字(如“jvm”)或者路径(如“C:\\jdk1.2\\jre\\bin\\classic\\jvm.dll”)。最好把本地库的绝对路径传递给JNU_FindCreateJavaVM,让LoadLibrary去搜索jvm.dll,这样程序就不怕环境变量被改变了。<span style="color:#.3&附加本地线程假设,你有一个用C写的服务器这样的多线程程序。当HTTP请求进来的时候,服务器创建许多本地线程来并行的处理HTTP请求。为了让多个线程可以同时操作JVM,我们可能需要把一个JVM植入这个服务器。图7.1&把JVM嵌入WEB服务器服务器上的本地方法的生命周期一般会比JVM要短,因此我们需要一个方法把本地线程附加到一个已经在运行的JVM上面,然后在这个本地方法中进行JNI调用,最后在不打扰其它连接到JVM上的线程的情况下把这个本地线程和JVM分离。下面这个例子中,attach.c演示了怎么样使用调用接口(invocation&interface)把本地线程附加到VM上去,这段程序使用的是Win32线程API。/*&Note:&This&program&only&works&on&Win32&*/&#include&&windows.h&&#include&&jni.h&&JavaVM&*&/*&The&virtual&machine&instance&*/&&#define&PATH_SEPARATOR&';'&#define&USER_CLASSPATH&&.&&/*&where&Prog.class&is&*/&&void&thread_fun(void&*arg)&{&&&&&jint&&&&&&jclass&&&&&&jmethodID&&&&&&jstring&&&&&&jclass&stringC&&&&&jobjectArray&&&&&&JNIEnv&*&&&&&char&buf[100];&&&&&int&threadNum&=&(int)&&&&&/*&Pass&NULL&as&the&third&argument&*/&#ifdef&JNI_VERSION_1_2&&&&&res&=&(*jvm)-&AttachCurrentThread(jvm,&(void**)&env,&NULL);&#else&&&&&res&=&(*jvm)-&AttachCurrentThread(jvm,&&env,&NULL);&#endif&&&&&if&(res&&&0)&{&&&&&&&&fprintf(stderr,&&Attach&failed\n&);&&&&&&&&&&&&&}&&&&&cls&=&(*env)-&FindClass(env,&&Prog&);&&&&&if&(cls&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&mid&=&(*env)-&GetStaticMethodID(env,&cls,&&main&,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&([Ljava/lang/S)V&);&&&&&if&(mid&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&sprintf(buf,&&&from&Thread&%d&,&threadNum);&&&&&jstr&=&(*env)-&NewStringUTF(env,&buf);&&&&&if&(jstr&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&stringClass&=&(*env)-&FindClass(env,&&java/lang/String&);&&&&&args&=&(*env)-&NewObjectArray(env,&1,&stringClass,&jstr);&&&&&if&(args&==&NULL)&{&&&&&&&&&goto&&&&&&}&&&&&(*env)-&CallStaticVoidMethod(env,&cls,&mid,&args);&&&detach:&&&&&if&((*env)-&ExceptionOccurred(env))&{&&&&&&&&&(*env)-&ExceptionDescribe(env);&&&&&}&&&&&(*jvm)-&DetachCurrentThread(jvm);&}&&main()&{&&&&&JNIEnv&*&&&&&int&i;&&&&&jint&&&#ifdef&JNI_VERSION_1_2&&&&&JavaVMInitArgs&vm_&&&&&JavaVMOption&options[1];&&&&&options[0].optionString&=&&&&&&&&&&-Djava.class.path=&&USER_CLASSPATH;&&&&&vm_args.version&=&0x;&&&&&vm_args.options&=&&&&&&vm_args.nOptions&=&1;&&&&&vm_args.ignoreUnrecognized&=&TRUE;&&&&&/*&Create&the&Java&VM&*/&&&&&res&=&JNI_CreateJavaVM(&jvm,&(void**)&env,&&vm_args);&#else&&&&&JDK1_1InitArgs&vm_&&&&&char&classpath[1024];&&&&&vm_args.version&=&0x;&&&&&JNI_GetDefaultJavaVMInitArgs(&vm_args);&&&&&/*&Append&USER_CLASSPATH&to&the&default&system&class&path&*/&&&&&sprintf(classpath,&&%s%c%s&,&&&&&&&&&&&&&vm_args.classpath,&PATH_SEPARATOR,&USER_CLASSPATH);&&&&&vm_args.classpath&=&&&&&&/*&Create&the&Java&VM&*/&&&&&res&=&JNI_CreateJavaVM(&jvm,&&env,&&vm_args);&#endif&/*&JNI_VERSION_1_2&*/&&if&(res&&&0)&{&&&&&&&&&fprintf(stderr,&&Can't&create&Java&VM\n&);&&&&&&&&&exit(1);&&&&&}&&&&&for&(i&=&0;&i&&&5;&i++)&&&&&&&&&/*&We&pass&the&thread&number&to&every&thread&*/&&&&&&&&&_beginthread(thread_fun,&0,&(void&*)i);&&&&&Sleep(1000);&/*&wait&for&threads&to&start&*/&&&&&(*jvm)-&DestroyJavaVM(jvm);&}上面这段attach.c代码是invoke.c的一个变形。与在主线程中调用Prog.main不同,本地代码开启了五个线程。开启线程完成以后,它就会等待1秒钟让线程可以运行完毕,然后调用DestroyJavaVM来销毁JVM。而每一个线程都会把自己附加到JVM上面,然后调用Prog.main方法,最后断开与JVM的连接。JNI_AttachCurrentThread的第三个参数需要传入NULL。JDK1.2引入了JNI_ThreadAttachArgs这个structure。它允许你向你要附加的线程传递特定的信息,如线程组等。JNI_ThreadAttachArgs这个structure的详细描述在13.2节里面,作为JNI_AttachCurrentThread的规范的一部分被提到。当程序运行函数DetachCurrentThread时,它释放属于当前线程的所有局部引用。运行程序,输出如下:Hello&World&from&thread&1&Hello&World&from&thread&0&Hello&World&from&thread&4&Hello&World&from&thread&2&Hello&World&from&thread&3上面这些输出根据不同的线程调试策略,可能会出现不同的顺序。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1568860次
积分:14760
积分:14760
排名:第765名
原创:167篇
译文:37篇
评论:450条
农场老马,与CSDN博客同步更新
基于ffmpeg的mp4视频文件压缩工具DettachCurrentThread使用情况 - sq2108的专栏 - CSDN博客
DettachCurrentThread使用情况
android framework
在android项目中,需要用到jni相关的东西,在java应用层需要通过jni调用底层C&#43;&#43;实现的native接口,同时在底层C&#43;&#43;也需要调用java实现的接口,以此来实现异步通信。
& & 我们知道JavaVM是进程相关的,每一个加载native动态库,都会生成一个JavaVM的实例对象,但JNIEnv是与线程相关的,每一个单独的线程都要生成一个JNIEnv的对象实例。
JavaVM接口
& & 第一种方式,在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。一般都在这个时候保存一个static的JavaVM *jvm,这个jvm指针在进程内是可以放心共享的。
第二种方式,在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。
两种情况下,都可以用全局变量,比如JavaVM* g_jvm来保存获得的指针以便在任意上下文中使用。
Android系统是利用第二种方式Invocation interface来创建JVM的。
JNIEnv接口
& & JNI开发最常见的错误就是滥用了JNIEnv接口。需要强调的是JNIEnv是跟线程相关的。sdk文档中强调了do not cache JNIEnv*,要用的时候在不同线程中再通过JavaVM *jvm的方法来获取与当前线程相关的JNIEnv*。
在native method中,JNIEnv作为第一个参数传入。那么在JNIEnv不作为参数传入的时候,该如何获得它?JNI提供了两个函数:(*jvm)-&AttachCurrentThread(jvm, (void**)&env, NULL)和(*jvm)-&GetEnv(jvm, (void**)env, JNI_VERSION_1_2)。两个函数都利用JavaVM接口获得JNIEnv接口,上面已经讲到如何获得JavaVM接口。当创建的线程需要获取JNIEnv*的时候,最好在刚创建的时候调用一次AttachCurrentThread,最好还是不要缓存这个JNIEnv*,每次需要的时候通过JavaVM*获取,不要忘记线程结束的时候执行DettachCurrentThread。
& & 在我的项目中,我在JNIEXPORT jint JNICALL NI_OnLoad(JavaVM *ajvm, void *reserved)&br /&
LOGI(TAG,&jni onload, jvm is %p&,jvm);
return JNI_VERSION_1_2;
}中保存JavaVM唯一的示例对象。
在java调用C&#43;&#43;native接口时,会传入JNIEnv* env和jobject this两个参数,env为与java线程相关的JNIEnv对象。
在C&#43;&#43;调用java接口时,因为线程是在C&#43;&#43;里面新创建的,就不能使用之前Java传入的JNIEnv对象,因为是不同的线程,就应创建不同的JNIEnv对象。在C&#43;&#43;调用java接口,就需要用到AttachCurrentThread这个函数来获取与当前线程相关的JNIEnv对象,调用这个函数将当前线程链接到JavaVM中,该线程就可以使用jni的api来完成调用,一旦本线程结束,就需要调用DettachCurrentThread来释放本线程与JavaVM的链接,如果不调用,则会产生内存泄露。
& & 在项目中,有这样一种情况:Android应用层开启一个线程————————调用底层native接口——————底层native接口回调android应用层接口
& & 在这种情况下,实质上还是在java线程里面,并没有新的C&#43;&#43;线程生成,这个JNIEnv对象实质上是通过第二步(调用底层native接口)可以传入进来的,倘若按照正常的流程先AttachCurrentThread和DettachCurrentThread来操作,反而会出错,一般是段错误。
所以如果是在C&#43;&#43;线程的函数里调用java函数,是一定要先AttachCurrentThread和DettachCurrentThread,倘若是在java线程,先调用native,native里面再调java函数,是不能DettachCurrentThread的。
还有一种情况,C&#43;&#43;调用java函数,java函数里面又生成了新的java线程,貌&#20284;也不能DettachCurrentThread,也会报段错误,这个至于是什么原因,目前我也不是很清楚,我猜测是因为在java函数里面又生成了新的子线程。
我的热门文章
即使是一小步也想与你分享Posts - 46,
Articles - 0,
Comments - 5
技术路,一步一个脚印
11:49 by RayLee, ... 阅读,
在系列(1)中讲到,Java与native code的操作有两种方式:(1)在Java中加载动态链接库(2)使用JNI Invocation inteface,在native code中创建JVM
JavaVM接口
第一种方式,在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。
第二种方式,在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。
两种情况下,都可以用全局变量,比如JavaVM* g_jvm来保存获得的指针以便在任意上下文中使用。
Android系统是利用第二种方式Invocation interface来创建JVM的。
JNIEnv接口
JNI开发最常见的错误就是滥用了JNIEnv接口。需要强调的是JNIEnv是跟线程相关的。
在native method中,JNIEnv作为第一个参数传入。那么在JNIEnv不作为参数传入的时候,该如何获得它?JNI提供了两个函数:(*jvm)-&AttachCurrentThread(jvm, (void**)&env, NULL)和(*jvm)-&GetEnv(jvm, (void**)&env, JNI_VERSION_1_2)。两个函数都利用JavaVM接口获得JNIEnv接口,上面已经讲到如何获得JavaVM接口。
JNI规范也说明,可以将获得JNIEnv封装成一个函数。
JNIEnv* JNU_GetEnv()
(*g_jvm)-&GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_2);继续研究NDK&&&&&&&&&&&&&&&&&&&&&
我在阿里云服务器上搭建了Android ndk的开发平台,并且借助这一平台研究了NDK的内部细节。
NDK提供了Android本地编程的接口,让你可以开发高效的依赖库,提高程序的速度,像是图像处理类和游戏类必然是少不了这个东西的,此外对于嵌入式开发者来说,有的时候需要绕过虚拟机直接跟内核通信那么就需要这个东西了。之前看到学长做的项目说,需要在Android上做一个音乐接口,但是翻遍了Android的api发现都实现不了,这个时候其实用ndk去调用底层的库,就可以跟内核的数据流建立连接,就可以做更多地事情。这样我们就可以利用Android兼容性能的同时,塑造更加高效的计算,或者是更加庞大的库。
jni.h提供了一系列的C接口来让我们在虚拟机和native lib之间辗转腾挪。language-agnostic这个词到底怎么翻译我也不好独断。
jni.h provides a thin C++ wrapper around the C API; the underlying calls are language-agnostic.基础调用是语言无关的。
elfARM的可执行文件的格式是ELF格式文件,那么我们先看一下jni的函数库是什么样子的。当然了这里的函数库,指的是本地的二进制镜像。
readelf查看ELF文件格式:
ELF文件格式:研究这种东西有助于我们理解更加深奥的开发模型;
ProgramHeaders:
ABI(application binary interface):
数据类型的大小、布局和对齐;
调用约定(控制着函数的参数如何传送以及如何接受返回值),例如,是所有的参数都通过栈传递,还是部分参数通过寄存器传递;哪个寄存器用于哪个函数参数;通过栈传递的第一个函数参数是最先push到栈上还是最后;
的编码和一个应用如何向操作系统进行系统调用;
以及在一个完整的操作系统ABI中,的格式、程序库等等。
假如我们编写了一个待编译的文件hello-jni.c
那么编译命令是:Jni:ndk-build hello-jni.c,编译生成了libhello-jni.so。
这样我们就给Android程序定制了c语言实现的库
android-arch这里可以看到当前对arm支持的一些信息,通过ldd libhell-jni.so命令可以查看当前库的依赖。
编写ndk的项目配置文件,本文中的项目是最简单的hello程序:
android.mk
Generate libhello-jni.so
ABI也是很重要的概念,前面已经解释了。
编写第二个配置文件:Application.mk
编写平台:Target all platform such as arm-v7\x86\ ...
Add a mudule:LOCAL_LDLIBS += -lsth
这里是具体c文件编写的规则:
JNI函数声明:
JNIEXPORT &return& JNICALL Java_&package&_&class&_&function&(JNIEnv* env,jobject,&Args&)
JNIEXPORT void JNICALL Java_com_Cartoonifier_CartoonifierView_ShowPreview(
JNIEnv* env,jobject ,jint width,jint height,jbyteArray yuv,jintArray bgra){ }
native程序中频繁使用JNIEnv*和JavaVM*。而C和C++代码使用JNIEnv*和JavaVM*这两个指针的做法是有区别的,网上大部分代码都使用C++,基本上找不到关于C和C++在这个问题上的详细叙述。
使用JNIEnv* env要这样&&&&& (*env)-&方法名(env,参数列表)
使用JavaVM* vm要这样&&&&&& (*vm)-&方法名(vm,参数列表)
使用JNIEnv* env要这样& &&& env-&方法名(参数列表)
使用JavaVM* vm要这样&&&&&& vm-&方法名(参数列表)
我们来看一下和java连接的数据结构:
JNIEnv struct:
&这个函数很有趣,体现了虚拟机平台的思想,需要什么类就去找什么类。我见过一种热更新Android系统的方案,那样不需要你去重启系统,利用的就是这个原理,去更改系统的find class的配置文件,然后用继承的思维,加入更新进去的库文件,并且向下兼容,就可以实现热更新。java虚拟机是一种我并不喜欢的思维,他试图用一个模型来解决所有问题。当你要解决问题的时候只需要加一个新类进去。
本地方法不能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。
Java通过JNI机制调用c/c++写的native程序。c/c++开发的native程序需要遵循一定的JNI规范,下面的例子就是一个JNI函数声明:
JNIEXPORT jint JNICALL Java_jnitest_MyTest_test
&& (JNIEnv * env, jobject obj, jint arg0);
JVM负责从Java Stack转入C/C++ Native Stack。当Java进入JNI调用,除了函数本身的参数(arg0),会多出两个参数:JNIEnv指针和jobject指针。
JNIEnv指针是JVM创建的,用于Native的c/c++方法操纵Java执行栈中的数据,比如Java Class, Java Method等。
首先,JNI对于JNIEnv的使用, 提供了两种语法: c语法以及c++语法,如下:
jsize len = (*env)-&GetArrayLength(env,array);
jsize len =env-&GetArrayLength(array);
(注:由于C语言并不支持对象的概念,所以C语法中需要把env作为第一个参数传入,类似于C++的隐式参数this指针).
另外: JNIEnv有几个设计的原则:
第一、JNIEnv指针被设计成了Thread Local Storage(TLS)变量,也就是说每一个Thread, JNIEnv变量都有独立的Copy。你不能把Thead#1使用的JNIEnv传给Thread#2使用。
第二、JNIEnv中定义了一组函数指针,c/c++ Native程序是通过这些函数指针操纵Java数据。这样设计的好处是:你的c/c++ 程序不需要依赖任何函数库,或者DLL。由于JVM可能由不同的厂商实现,不同厂商有自己不同的JNI实现,如果要求这些厂商暴露约定好的一些头文件和库,这不是灵活的设计。
而且使用函数指针表的另外一个好处是: JVM可以根据启动参数动态替换JNI实现。
需要强调的是JNIEnv是跟线程相关的。
&这些都是数据接口;
调用JavaVM接口:
第一种方式,在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。
第二种方式,在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。两种情况下,都可以用全局变量,比如JavaVM* g_jvm来保存获得的指针以便在任意上下文中使用。Android系统是利用第二种方式Invocation interface来创建JVM的。
JavaVM *jvm=0;
JNIEnv *env=(JNIEnv *)p;
env-&GetJavaVM(&jvm);
调用JNIEnv接口:
在native method中,JNIEnv作为第一个参数传入。那么在JNIEnv不作为参数传入的时候,该如何获得它
JNI提供了两个函数:
& (*jvm)-&AttachCurrentThread(jvm, (void**)&env, NULL)
& (*jvm)-&GetEnv(jvm, (void**)&env, JNI_VERSION_1_2)
两个函数都利用JavaVM接口获得JNIEnv接口,上面已经讲到如何获得JavaVM接口。JNI规范也说明,可以将获得JNIEnv封装成一个函数。
JNIEnv* JNU_GetEnv()
&&&&JNIEnv*
&&&&(*g_jvm)-&GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_2);//C风格
//jint result = g_jvm-&GetEnv((void **) &env,JNI_VERSION_1_2);
&&&&return&
JNI_VERSION
下面是来自stackoverflow的一段代码:
#include &jni.h&
#include &iostream&
#include &pthread.h&
JNIEnv* getEnv(JavaVM *jvm)
&&&&&&& JNIEnv *env = 0;
&&&&&&& jint result = jvm-&GetEnv((void **) &env, JNI_VERSION_1_6);//C++
&&&&&&& if (result != JNI_OK)
    /*虚拟机的线程技术值得深入研究*/
&&&&&&&&&&& result = jvm-&AttachCurrentThread((void **) &env, NULL);
//struct JNIInvokeInterface
&&&&&&&&&&& if (result != JNI_OK)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& cout && "Failed to attach current thread " && pthread_self() &&
&&&&&&&&&&& }
&&&&&&&&&&& else
&&&&&&&&&&& {
&&&&&&&&&&&&&&& cout && "Successfully attached native thread " && pthread_self() &&
&&&&&&&&&&& }
&&&&&&&&&&& // ...and register for detach when thread exits
&&&&&&&&&&& int result = pthread_setspecific(key, (void *) env);
&&&&&&&&&&& if (result != 0)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& cout && "Problem registering for detach" &&
&&&&&&&&&&& }
&&&&&&&&&&& else
&&&&&&&&&&& {
&&&&&&&&&&&&&&& cout && "Successfully registered for detach" &&
&&&&&&&&&&& }
static pthread_key_
static pthread_once_t key_
&接下来看一下ndk的c函数库,我相信你看到了很多你想要的东西,就连编写高性能服务器的poll.h都出现了。elf可以查看动态链接库的格式。这些函数都太底层了,大部分可能直接和内核通信。安全性更加重要。
NDK platform/android-19/*
NDK.inlcude\c\c++
JNIEnv使用方法:
参数声明 ...jintArray bgra,jbyteArray yuv...
/*......*/
jbyte* _yuv=env-&GetByteArrayElements(yuv,0);//在JVM中获取图像数据
jint* _bgra=env-&GetIntArrayElements(bgra,0);
/*......*/
env-&ReleaseIntArrayElement(bgra,_bgra,0);
env-&ReleaseByteArrayElements(yuv,_yuv,0);
问题是:从这里看到指向的是像素缓冲区,也就是java的对象数据,那么就不是copy,那么多线程调用TLS是怎么实现的?指针发生了竞争怎么办?还是就是拷贝,如果是内存拷贝又是怎么改变原实例的数据的。
&下面的这个东西是多语言编译器,试图实现多种语言的混合编程。ndk是需要存在的,因为我们开发者有这种需要。不然我们真的就找不到办法去实现windows phone的那种性能了。即使Android虚拟机一直在优化。
SWIG (Simplified Wrapper and Interface Generator)
Tagline: SWIG is a compiler that integrates C and C++ with languages
&&&&&&&& including Perl, Python, Tcl,
Ruby, PHP, Java, C#, D, Go, Lua,
&&&&&&&& Octave, R, Scheme (Guile,
MzScheme/Racket, CHICKEN), Scilab,
&&&&&&&& Ocaml, Modula-3, Common Lisp
(CLISP, Allegro CL, CFFI, UFFI)
&&&&&&&& and Pike. SWIG can also export
its parse tree into XML and
&&&&&&&& Lisp s-expressions.
阅读(...) 评论()

我要回帖

更多关于 如何调用接口 的文章

 

随机推荐