JNI: API的调用

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

    #include <jni.h>       /* where everything is defined */
    ...
    JavaVM *jvm;       /* denotes a Java VM */
    JNIEnv *env;       /* pointer to native method interface */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=/usr/lib/java";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm, &env, &vm_args);
    delete options;
    /* 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++调用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函数表的指针。下面的例子展示了该函数表:

typedef const struct JNIInvokeInterface *JavaVM;


const struct JNIInvokeInterface ... = {
    NULL,
    NULL,
    NULL,

    DestroyJavaVM,
    AttachCurrentThread,
    DetachCurrentThread,

    GetEnv,

    AttachCurrentThreadAsDaemon
};

注意上面的三个调用的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);

你可能感兴趣的:(JNI: API的调用)