JNI: API的调用

12 篇文章 0 订阅
12 篇文章 0 订阅

什么都不说,我们先来看代码:

  1. #include <jni.h>       /* where everything is defined */  
  2. ...  
  3. JavaVM *jvm;       /* denotes a Java VM */  
  4. JNIEnv *env;       /* pointer to native method interface */  
  5. JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */  
  6. JavaVMOption* options = new JavaVMOption[1];  
  7. options[0].optionString = "-Djava.class.path=/usr/lib/java";  
  8. vm_args.version = JNI_VERSION_1_6;  
  9. vm_args.nOptions = 1;  
  10. vm_args.options = options;  
  11. vm_args.ignoreUnrecognized = false;  
  12. /* load and initialize a Java VM, return a JNI interface 
  13.  * pointer in env */  
  14. JNI_CreateJavaVM(&jvm, &env, &vm_args);  
  15. delete options;  
  16. /* invoke the Main.test method using the JNI */  
  17. jclass cls = env->FindClass("Main");  
  18. jmethodID mid = env->GetStaticMethodID(cls, "test""(I)V");  
  19. env->CallStaticVoidMethod(cls, mid, 100);  
  20. /* We are done. */  
  21. jvm->DestroyJavaVM();  

在不考虑语法正确的情况下,我们可以知道c++调用java的过程:

1.准备工作:定义一些JNI的变量——JavaVM、JNIEnv、JavaVMInitArgs以及JavaVMOption实例;

2. 创建VM: 通过JNI_CreateJavaVM() 函数加载以及初始化一个java虚拟机以及返回一个指向JNI接口的指针。注意:只能在main thread中调用JNI_CreateJavaVM()

3.Attaching to the VM:因为只能在main thread创建VM,所以JNI 接口指针,即JNIEnv只能在main thread中有效。为了在其他的线程也能使用VM:在其他的线程中调用 AttachCurrentThread(),从而获得JNI 接口指针。注意: (1)一旦attach了VM,该native 线程就像普通的java 线程一样运行在native方法中( Once attached to the VM, a native thread works just like an ordinary Java thread running inside a native method. )。该native 线程保持attach了VM,直到调用DetachCurrentThread()结束;(2)已经attach了VM的线程应该有足够的栈空间来执行相应的工作。每个线程的栈空间大小由操作系统决定。

4. Detaching to the VM:已经attach了VM的native 线程在退出时 必须调用DetachCurrentThread()。注意:如果thread的call stack有java方法,那么该thread不能detach VM。

5. 卸载VM:调用JNI_DestroyJavaVM()卸载VM。注意:在JDK/JRE 1.1,只有主线程才能卸载VM;在JDK/JRE 1.2及其之后,无此限制。

Library以及版本管理:

在JDK/JRE1.1中,一旦一个native library加载成功,那么所有的class loader都是可见的。因此不能的class loader中两个类可能链接相同的native方法,这样会导致两个问题:

1. 一个类可能错误地链接由另外一个class loader中相同类名加载的native库(A class may mistakenly link with native libraries loaded by a class with the same name in a different class loader.)。

2. native 方法能够容易的混合不同class loader的类。这破坏了class loader提供的命名空间分割,由此会导致类型安全问题。

而在JDK/JRE1.2之后,每个class loader管理自己的native library集合。相同的JNI native library只能加载在一个class loader中。如果不这么做就会扔出UnsatisfiedLinkError异常。例如当一个native library加载两个class loader, System.loadLibrary扔出 UnsatisfiedLinkError ,这样做的好处有:

1. 基于class loader的命名空间分隔保留在native library中。一个native library不会容易的混淆不同的class loader;

2. 除此之外,当native libraries相应的class loader在垃圾回收时, 可以安全的卸载native libraries。

为了便于版本控制以及资源管理,JDK/JRE1.2的JNI libraries可以选择使用下面两个函数:

1. jint JNI_OnLoad(JavaVM *vm, void *reserved);

当native library加载时,VM会调用JNI_Onload。JNI_Onload必须返回native library需要的JNI版本。

为了使用任何新的JNI函数,native library必须引出JNI_Onload函数,JNI_Onload函数返回JNI_VERSION_1_2。如果native library没有引出JNI_Onload函数那么VM假设该native library只需要JNI版本为JNI_VERSION_1_1。如果VM不能识别出由JNI_Onload返回的版本数据,那么native library将不能加载。

2. void JNI_OnUnload(JavaVM *vm, void *reserved);

当包含了native library的class loader在回收垃圾时,VM将会调用JNI_OnUnload。该函数可以用来做清理操作。

JNI的API函数调用:

JavaVM类型是一个指向API函数表的指针。下面的例子展示了该函数表:

  1. typedef const struct JNIInvokeInterface *JavaVM;  
  2.   
  3.   
  4. const struct JNIInvokeInterface ... = {  
  5.     NULL,  
  6.     NULL,  
  7.     NULL,  
  8.   
  9.     DestroyJavaVM,  
  10.     AttachCurrentThread,  
  11.     DetachCurrentThread,  
  12.   
  13.     GetEnv,  
  14.   
  15.     AttachCurrentThreadAsDaemon  
  16. };  

注意上面的三个调用的API函数:JNI_GetDefaultJavaVMInitArgs()JNI_GetCreatedJavaVMs(), 以及 JNI_CreateJavaVM(),他们不是JavaVM函数表的一部分。这三个函数可以在没有预先存在的JavaVM结构的情况下使用。


获得JavaVM的默认设置

JNI_GetDefaultJavaVMInitArgs jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);

调用该函数之前,native的代码必须设置JNI version的 vm_args->version,并且为期望支持的VM 版本号。如果请求的版本为支持的版本,则返回 JNI_OK ; 否则返回JNI error code (a negative number) 。

获得已经创建的所有JavaVM:

JNI_GetCreatedJavaVMs—— jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

vmBuf是返回的按照顺序创建并写入到vmBuf中所有VM的指针。注意:在JDK/JRE1.2之后不支持在单进程中创建多个VM。


创建JavaVM

JNI_CreateJavaVM——jint JNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env, void *vm_args);

加载并初始化JavaVM,并且当前线程变成主线程。

还有一些函数基本与上面函数的形式类似:

DestroyJavaVM——jint DestroyJavaVM(JavaVM *vm);


AttachCurrentThread

jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args);

AttachCurrentThreadAsDaemon

jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);

DetachCurrentThread

jint DetachCurrentThread(JavaVM *vm);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值