《Java 本地接口规范》- 调用 API

调用 API


调用 API 允许软件厂商将 Java 虚拟机加载到任意的本地程序中。厂商可以交付支持 Java 的应用程序,而不必链接 Java 虚拟机源代码。

本章首先概述了调用 API。然后是所有调用 API 函数的引用页。

若要增强 Java 虚拟机的嵌入性,可以用几种方式来扩展 JDK 1.1.2 中的调用 API。


概述

以下代码示例说明了如何使用调用 API 中的函数。在本例中,C++ 代码创建 Java 虚拟机并且调用名为 Main.test 的静态方法。为清楚起见,我们略去了错误检查。

        #include <jni.h>       /* 其中定义了所有的事项 */
 
        ...
 
        JavaVM *jvm;       /* 表示 Java 虚拟机*/
        JNIEnv *env;       /* 指向本地方法接口的指针 */
 
        JDK1_1InitArgs vm_args; /* JDK 1.1 虚拟机初始化参数 */
 
        vm_args.version = 0x00010001; /* 1.1.2 中新增的:虚拟机版本 */
        /* 获得缺省的初始化参数并且设置类
         * 路径 */
        JNI_GetDefaultJavaVMInitArgs(&vm_args);
        vm_args.classpath = ...;
 
        /* 加载并初始化 Java 虚拟机,返回 env 中的
         * JNI 接口指针 */
        JNI_CreateJavaVM(&jvm, &env, &vm_args);
 
        /* 用 JNI 调用 Main.test 方法 */
        jclass cls = env->FindClass("Main");
        jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
        env->CallStaticVoidMethod(cls, mid, 100);
 
        /* 结束。*/
        jvm->DestroyJavaVM();

本例使用了 API 中的三个函数。调用 API 允许本地应用程序用 JNI 接口指针来访问虚拟机特性。其设计类似于 Netscape 的 JRI 嵌入式接口。

 

创建虚拟机

JNI_CreateJavaVM() 函数加载并初始化Java 虚拟机,然后将指针返回到 JNI 接口指针。调用 JNI_CreateJavaVM() 的线程被看作主线程

 

连接虚拟机

JNI 接口指针 (JNIEnv) 仅在当前线程中有效。如果另一个线程需要访问 Java 虚拟机,则该线程首先必须调用AttachCurrentThread() 以将自身连接到虚拟机并且获得 JNI 接口指针。连接到虚拟机之后,本地线程的工作方式就与在本地方法内运行的普通 Java 线程一样了。本地线程保持与虚拟机的连接,直到调用
DetachCurrentThread() 时才断开连接。

 

卸载虚拟机

主线程不能自己断开与虚拟机的连接。而是必须调用DestroyJavaVM() 来卸载整个虚拟机。

虚拟机等到主线程成为唯一的用户线程时才真正地卸载。用户线程包括 Java 线程和附加的本地线程。之所以存在这种限制是因为 Java 线程或附加的本地线程可能正占用着系统资源,例如锁,窗口等。虚拟机不能自动释放这些资源。卸载虚拟机时,通过将主线程限制为唯一的运行线程,使释放任意线程所占用系统资源的负担落到程序员身上。


初始化结构

不同的 Java 虚拟机实现可能会需要不同的初始化参数。很难提出适合于所有现有和将来的 Java 虚拟机的标准初始化结构。作为一种折衷方式,我们保留了第一个域 (version) 来识别初始化结构的内容。嵌入到 JDK 1.1.2 中的本地应用程序必须将版本域设置为
0x00010001。尽管其它实现可能会忽略某些由 JDK 所支持的初始化参数,我们仍然鼓励虚拟机实现使用与 JDK 一样的初始化结构。

0x800000000xFFFFFFFF 之间的版本号需保留,并且不为任何虚拟机实现所识别。

以下代码显示了初始化 JDK 1.1.2 中的 Java 虚拟机所用的结构。

    typedef struct JavaVMInitArgs {
       /* 前两个域在 JDK 1.1 中保留,并
          在 JDK 1.1.2 中正式引入。*/
       /* Java 虚拟机版本 */
        jint version;
 
       /* 系统属性。*/
        char **properties;
 
       /* 是否检查 Java 源文件与已编译的类文件
        *之间的新旧关系。*/
        jint checkSource;
 
       /* Java 创建的线程的最大本地堆栈大小。*/
        jint nativeStackSize;
 
       /* 最大 Java 堆栈大小。*/
        jint javaStackSize;
 
       /* 初始堆大小。*/
        jint minHeapSize;
 
       /* 最大堆大小。*/
        jint maxHeapSize;
 
       /* 控制是否校验 Java 字节码:
        * 0 无,1 远程加载的代码,2 所有代码。*/
        jint verifyMode;
 
       /* 类加载的本地目录路径。*/
        const char *classpath;
 
       /* 重定向所有虚拟机消息的函数的钩子。*/
        jint (*vfprintf)(FILE *fp, const char *format,
                         va_list args);
 
       /* 虚拟机退出钩子。*/
        void (*exit)(jint code);
 
       /* 虚拟机放弃钩子。*/
        void (*abort)();
 
       /* 是否启用类 GC。*/
        jint enableClassGC;
 
       /* GC 消息是否出现。*/
        jint enableVerboseGC;
 
       /* 是否允许异步 GC。*/
        jint disableAsyncGC;
 
       /* 三个保留的域。*/
        jint reserved0;
        jint reserved1;
        jint reserved2;
    } JDK1_1InitArgs;

在 JDK 1.1.2 中,初始化结构提供了钩子,这样在虚拟机终止时,本地应用程序可以重定向虚拟机消息并获得控制权。

当本地线程与JDK 1.1.2 中的 Java 虚拟机连接时,以下结构将作为参数进行传递。实际上,本地线程与 JDK 1.1.2 连接时不需要任何参数。JDK1_1AttachArgs 结构仅由 C 编译器的填充槽组成,而 C 编译器不允许空结构。

    typedef struct JDK1_1AttachArgs {
       /*
        * JDK 1.1 不需要任何参数来附加
        * 本地线程。此处填充的作用是为了满足不允许空结构的 C 
        * 编译器的要求。
        */
        void *__padding;
    } JDK1_1AttachArgs;

调用 API 函数

JavaVM 类型是指向调用 API 函数表的指针。以下代码示例显示了这种函数表。

    typedef const struct JNIInvokeInterface *JavaVM;
 
    const struct JNIInvokeInterface ... = {
        NULL,
        NULL,
        NULL,
 
        DestroyJavaVM,
        AttachCurrentThread,
        DetachCurrentThread,
    };

注意,JNI_GetDefaultJavaVMInitArgs()、JNI_GetCreatedJavaVMs() 和JNI_CreateJavaVM()这三个调用 API 函数不是 JavaVM 函数表的一部分。不必先有
JavaVM 结构,就可以使用这些函数。

 

JNI_GetDefaultJavaVMInitArgs

jintJNI_GetDefaultJavaVMInitArgs(void *vm_args);

返回 Java 虚拟机的缺省配置。在调用该函数之前,平台相关代码必须将 vm_args->version 域设置为它所期望虚拟机支持的 JNI 版本。在 JDK 1.1.2 中,必须将
vm_args->version 设置为 0x00010001。(JDK1.1 不要求平台相关代码设置版本域。为了向后兼容性,如果没有设置版本域,则 JDK 1.1.2 假定所请求的版本为 0x00010001。JDK 的未来版本将要求把版本域设置为适当的值。) 该函数返回后,将把vm_args->version 设置为虚拟机支持的实际 JNI 版本。

参数:

vm_args:指向 VM-specific initialization(特定于虚拟机的初始化)结构的指针,缺省参数填入该结构。

返回值:

如果所请求的版本得到支持,则返回“0”;如果所请求的版本未得到支持,则返回负数。

 

JNI_GetCreatedJavaVMs

jintJNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen,
jsize *nVMs);

返回所有已创建的Java 虚拟机。将指向虚拟机的指针依据其创建顺序写入 vmBuf 缓冲区。最多写入 bufLen 项。在 *nVMs 中返回所创建虚拟机的总数。

JDK 1.1 不支持在单个进程中创建多个虚拟机。

参数:

vmBuf:指向将放置虚拟机结构的缓冲区的指针。

bufLen:缓冲区的长度。

nVMs:指向整数的指针。

返回值:

成功时返回“0”;失败则返回负数。

 

JNI_CreateJavaVM

jintJNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env,
void *vm_args);

加载并初始化Java 虚拟机。当前线程成为主线程。将env 参数设置为主线程的 JNI 接口指针。

JDK 1.1.2 不支持在单个进程中创建多个虚拟机。必须将 vm_args 中的版本域设置为 0x00010001

参数:

p_vm:指向位置(其中放置所得到的虚拟机结构)的指针。

p_env:指向位置(其中放置主线程的 JNI 接口指针)的指针。

vm_args: Java 虚拟机初始化参数。

返回值:

成功时返回“0”;失败则返回负数。

 

DestroyJavaVM

jintDestroyJavaVM(JavaVM *vm);

卸载 Java 虚拟机并回收资源。只有主线程能够卸载虚拟机。调用 DestroyJavaVM() 时,主线程必须是唯一的剩余用户线程。

参数:

vm:将销毁的 Java 虚拟机。

返回值:

成功时返回“0”;失败则返回负数。

JDK 1.1.2 不支持卸载虚拟机。

 

AttachCurrentThread

jintAttachCurrentThread(JavaVM *vm, JNIEnv **p_env,
void *thr_args);

将当前线程连接到 Java 虚拟机。在 JNIEnv 参数中返回 JNI 接口指针。

试图连接已经连接的线程将不执行任何操作。

本地线程不能同时连接到两个 Java 虚拟机上。

参数:

vm:当前线程所要连接到的虚拟机。

p_env:指向位置(其中放置当前线程的 JNI 接口指针)的指针。

thr_args:特定于虚拟机的线程连接参数。

返回值:

成功时返回“0”;失败则返回负数。

 

DetachCurrentThread

jintDetachCurrentThread(JavaVM *vm);

断开当前线程与 Java 虚拟机之间的连接。释放该线程占用的所有 Java 监视程序。通知所有等待该线程终止的 Java 线程。

主线程(即创建 Java 虚拟机的线程)不能断开与虚拟机之间的连接。作为替代,主线程必须调用 JNI_DestroyJavaVM() 来卸载整个虚拟机。

参数:

vm:当前线程将断开连接的虚拟机。

返回值:

成功时返回“0”;失败则返回负数。


 

时间: 2024-08-04 00:44:23

《Java 本地接口规范》- 调用 API的相关文章

《Java 本地接口规范》- JNI 函数(三)

字符串操作   NewString jstringNewString(JNIEnv *env, const jchar *unicodeChars, jsize len); 利用 Unicode 字符数组构造新的 java.lang.String 对象. 参数: env:JNI 接口指针. unicodeChars:指向 Unicode 字符串的指针. len:Unicode 字符串的长度. 返回值: Java 字符串对象.如果无法构造该字符串,则为 NULL. 抛出: OutOfMemoryE

《Java 本地接口规范》- JNI 函数(二)

对象操作   AllocObject jobjectAllocObject(JNIEnv *env, jclass clazz); 分配新 Java 对象而不调用该对象的任何构造函数.返回该对象的引用. clazz 参数务必不要引用数组类. 参数: env:JNI 接口指针. clazz:Java 类对象. 返回值: 返回 Java 对象.如果无法构造该对象,则返回NULL. 抛出: InstantiationException:如果该类为一个接口或抽象类. OutOfMemoryError:如

《Java 本地接口规范》- JNI 函数(一)

JNI 函数 本章为 JNI 函数提供参考信息.其中列出了全部 JNI 函数,同时也给出了 JNI 函数表的准确布局. 注意:"必须"一词用于约束 JNI 编程人员.例如,当说明某个 JNI 函数必须接收非空对象时,就应确保不要向该 JNI 函数传递 NULL.这时,JNI 实现将无需在该 JNI 函数中执行 NULL 指针检查. 本章的部分资料改编自 Netscape 的 JRI 文档. 该参考资料按用法对函数进行组织.参考部分按下列函数区域进行组织: 版本信息 类操作 异常 全局及

《Java 本地接口规范》-JNI 的类型和数据结构

JNI 的类型和数据结构 本章讨论 JNI 如何将 Java 类型映射到本地 C 类型. 基本类型 表 3-1 描述Java 基本类型及其与计算机相关的本地等效类型. 表 3-1 基本类型和本地等效类型 Java 类型 本地类型 说明 boolean jboolean 无符号,8 位 byte jbyte 无符号,8 位 char jchar 无符号,16 位 short jshort 有符号,16 位 int jint 有符号,32 位 long jlong 有符号,64 位 float jf

基于JAVA每月运势api调用代码实例_java

本文实例为大家分享了JAVA每月运势api调用代码,供大家参考,具体内容如下 import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.

Java规则引擎与其API应用详解

详解 本文对Java规则引擎与其API(JSR-94)及相关实现做了较详细的介绍,对其体系结构和API应用有较详尽的描述,并指出Java规则引擎,规则语言,JSR-94的相互关系,以及JSR-94的不足之处和展望 本文对Java规则引擎与其API(JSR-94)及相关实现做了较详细的介绍,对其体系结构和API应用有较详尽的描述,并指出Java规则引擎,规则语言,JSR-94的相互关系,以及JSR-94的不足之处和展望 复杂企业级项目的开发以及其中随外部条件不断变化的业务规则(business l

java-RMI本地client调用远程服务器上的server报错

问题描述 RMI本地client调用远程服务器上的server报错 代码这篇文章上的.Java RMI之HelloWorld篇 我把ihello,helloimpl,server放在了服务器上,client放在了本机电脑的eclipse中 保证远程服务器上的server正常运行,运行client报错 Caused by: java.lang.ClassNotFoundException: IHello (no security manager: RMI class loader disabled

Java通过JNI调用C语言的方法

JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能. 解决JAVA对本地操作的一种方法就是JNI. JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式).通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法. 简单介绍及应用如下: 一.JAVA中所需要做的工作 在JAVA程序中,首先需要在类中声明所调

Java通过JNI调用C++程序

JNI是Java Native Interface的缩写,中文为JAVA本地调用.使用JNI可以很方便的用我们的Java程序调用C/C++程序.很多时候,某些功能用Java无法 实现,比如说涉及到底层驱动的一些功能,这时候我们就可以利用JNI来调用C或者C++程序来实现,这就是JNI的强大之处.但是JNI也有它的缺点,使 用java与本地已编译的代码交互,通常会丧失平台可移植性. 下面是一个JNI例子,调用C++输出"hello world":   第一步:创建Java类,在里面定义一