Oracle官网JNI简介和接口函数分析

第一章 概述

本章主要介绍JNI(Java Native Interface),JNI是一种本地编程接口。它允许运行在JAVA虚拟机中的JAVA代码和用其他编程语言,诸如C语言、C++、汇编,写的应用和库之间的交互操作。

JNI的最大优势在于没有强加任何限制在JAVA虚拟机的下层实现上,因此,JAVA虚拟机供应商能够提供JNI的支持而不影响虚拟机的其他部分,程序员只需写出一个版本的本地应用和库,就可使之运行在一切支持JNI的JAVA虚拟机上。

本章包含了以下的要点:

? JNI概述

? 目标

? JNI方法

? JNI编程

? 变化

JNI概述

当你能够完全的用JAVA写出一个应用,也有一种情况,那就是只用JAVA无法满足应用的要求。这种无法完全用JAVA完成应用的情况出现的时候,程序员会用JNI写出JAVA本地方法来处理。

下面列举几个用JAVA本地方法的例子:

? 标准JAVA类库不支持应用所需的平台依赖特性

? 你已经有一个其他语言实现好的库,想让它能够通过JNI在JAVA代码中使用

? 你想在诸如汇编的低级语言中实现一部分时效性的代码

通过JNI的编程,你能够实现如下功能:

? 创建,检查,更新JAVA对象(包括数组和字符串)

? 调用JAVA方法

? 捕捉或者抛出异常

? 加载类和获取类的信息

? 执行运行时类型检查

你也能使用JNI通过调用API来让任意的本地应用嵌入到JAVA虚拟机中,这允许程序员轻松地让他们现有的程序在JAVA中可用,而不需要连接虚拟机的源代码。

历史背景

不同供应商的虚拟机提供不同的本地接口代码。不同的接口迫使程序员在一个给定的平台上产生,维持,分配多个版本的本地方法库。我们简略地查看一下这些本地方法接口,例如:

? JDK1.0本地方法接口

? 网景公司的JRI(JAVA运行时接口)

? 微软的RNI(原始本地接口)和JAVA/COM接口

JDK1.0本地方法接口

JDK1.0有本地方法接口,但是有两个重大的使这个接口不能被其他JAVA虚拟机采用的问题.

首先,本地代码作为C结构体的成员来访问JAVA对象的域。然而,JAVA语言规格书没有定义怎么在内存中放置对象。如果一个JAVA虚拟机在内存中以不同的方式放置对象,那么程序员必须重新编译本地方法库。

其次,JDK1.0的本地方法接口依靠保守的垃圾回收器。例如,无限制的使用unhand宏,将使得适当地去扫描本地栈。

JAVA运行时接口

网景公司提出了JAVA运行时接口(JRI),这是一个通用的JAVA虚拟机提供服务的接口。JRI设计时运用了嵌入式的思想--它使得不需要考虑JAVA虚拟机下层实现细节的条件。JRI处理大范围的问题,包括了本地方法、调试、反射机制、嵌入(调用)等。

原始本地接口和JAVA/COM接口

微软的JAVA虚拟机支持两种本地方法接口。低层次地,提供了有效的原始本地接口(RNI),RNI提供了对JDK本地方法接口的源码级后向兼容性的高度,尽管JDK有一个主要的不同点。本地代码必须使用RNI函数来明确地和垃圾回收器交互,而不是依靠垃圾回收器。

目标

我们相信一个统一的、深思熟虑的接口应该向每个人提供以下的益处:

? 每个虚拟机供应商能够支持大量的本地代码

? 工具制造者不必维护不同种类的本地方法接口

? 应用开发者能够只写一个版本的本地代码而能够在不同的虚拟机上运行

获取本地方法接口的最好途径是包含对JAVA虚拟机感兴趣的所有部分,因此,我们在设计一个统一的本地方法接口的JAVA授权商中组织了一系列的审议。毫无疑问地,这场关于标准化的本地接口审议必须满足下面的要求:

? 二进制兼容性 - 最基本目的是实现一个给定的平台上所有的JAVA虚拟机二进制兼容的本地方法库。开发者只需要就给定的平台维护一个版本的本地方法库。

? 效益 - 为了支持时效性的代码,本地方法接口必须只征收很少的费用。所有众所周知的满足虚拟机独立性的技术背负着很大的费用,所以总得在效益和虚拟机独立性上取得折中。

? 功能性 - 接口必须暴露出足够多的JAVA虚拟机内部来让本地方法完成有用的任务。

JNI方法

我们希望采用一个现有的方法作为标准接口,因为这将给在不同的虚拟机中学过多种接口的程序员最小的负担。不幸的是,没有这样的可以完全满足我们要求的解决方法。

网景公司的JRI是最接近我们预想的可移植本地方法接口,被用作我们设计的出发点。熟悉JRI的读者会注意到在API命名约定,方法和域ID的使用,局部和全局引用的使用等的相似点。尽管尽了我们最大的努力,然而,JNI和JRI不是二进制兼容的,即使虚拟机能够支持JRI和JNI。

微软的RNI是JDK1.0的改进,因为它解决了本地方法工作在保守垃圾回收器上的问题。然而,RNI作为虚拟机独立性的本地方法接口是不合适的。和JDK一样,RNI作为C结构体的去访问JAVA对象,导致了两个问题:

? RNI向本地代码暴露了内部JAVA对象的布局。

? 作为C结构体直接访问JAVA对象,使得不能高效地合并先进垃圾回收算法需要的”写屏障”

作为二进制标准,COM确保了通过不同虚拟机的二进制兼容性。调用COM方法要求只有一个小开销的间接调用,COM对象是在解决版本问题上动态链接库的一个重大提高。

然而,使用COM作为标准JAVA本地方法接口在一些方面有限制:

? 首先,JAVA/COM接口缺乏某些希望的函数,诸如访问私有域和引起通用的异常。

? 其次,JAVA/COM接口自动为JAVA对象提供标准的 IUnknown 和 IDispatch COM 接口,因此本地代码能够访问公有的域和方法。然而,IDispatch 接口不能处理重载的JAVA方法并且在匹配方法名是大小写敏感度的。此外,所有的通过IDispatch 接口暴露的JAVA方法被封装来执行动态的类型检测和强制转换。这是因为IDispatch 接口是用弱类型语言(例如Basic)的思想设计的。

? 再次,COM设计为允许软件组件(包括完全的应用程序)一起工作,而不是处理单独底层函数。我们相信将所有的JAVA类或者底层的本地方法作为软件组件式不合适的。

? 最后,COM的直接采用为缺乏UNIX平台的支持而受限制。

尽管JAVA对象不作为COM对象暴露给本地代码,JNI接口它本身是和COM二进制兼容的,JNI使用和COM相同的跳转表结构和调用约定。这意味着,一旦跨平台支持的COM可用,JNI就能作为JAVA虚拟机的COM接口。

JNI不是唯一的被给定JAVA虚拟机支持的本地方法接口。一个标准的接口使得想加载本地代码库到JAVA虚拟机的程序员受益。在一些情况下,程序员可能不得不使用底层的,虚拟机特性的接口来获得最高的效率,在另一些情况下,程序员可能使用上层的接口来构建软件组件。确实,当JAVA环境和组件软件技术更加成熟,本地方法将逐渐地失去重要性。

JNI编程

本地方法程序员应该用JNI编程。用JNI编程把你从诸如终端用户可能运行的供应商虚拟机等未知中隔离出来,通过遵从JNI标准,你将会给本地库最好的机会来在给定的JAVA虚拟机中运行。

如果你实现了JAVA虚拟机,你应该实现JNI。JNI经过长期试验来确保不强加任何的开销和限制在虚拟机的实现上,包括对象表示,垃圾回收方案等。

第二章 设计综述

本章聚焦在JNI的主要设计议题,这一章节的大部分设计议题都和本地方法有关,调用API的设计将在第五章介绍

JNI接口函数和指针

本地代码通过调用JNI函数来获取JAVA虚拟机的特性。JNI函数是通过接口指针来发挥作用的。接口指针是指向指针的指针。这种指针指向了一个指针数组,数组中的每一个指针指向一个接口函数。每个接口函数在数组中有一个既定的偏移量。图2-1 显示了接口指针的组织结构。

图2-1 接口 指针

JNI接口组织地就像一个C++虚函数表和COM接口,使用接口表而不是硬链接的函数实体的好处是,JNI的名字空间和本地代码分开了。虚拟机能够轻松地提供多种版本的JNI函数表,例如,虚拟机提供了两种JNI函数表:

· 第一种完成了非法参数检测,更适合调试

· 另一种完成最少的JNI规格要求的检测,因此更加高效

JNI接口指针仅在当前线程中有效,因此,本地方法不能经过接口指针从一个线程到另一个线程。实现了JNI的虚拟机能够分配和存储本地线程数据到JNI接口指针指向的区域。

JN接口指针作为参数被本地方法接收,虚拟机保证从相同的线程多次调用本地方法时,会传递相同的接口指针给本地方法。但是,一个本地方法可以从不同的线程中调用,因此会接收到不同的JNI接口指针。

编译,加载和链接本地方法

由于JAVA虚拟机是多线程的,所以本地库应该使用多线程相关的编译器来编译和链接。例如,用标志-mt指示用SUN Studio 编译器来编译C++代码,用GNU GCC编译器编译代码,将使用 -D_REENTRANT 或者-D_POSIX_C_SOURCE 来指示。更多信息参考本地编译器文档。

本地方法使用System.loadLibrary方法装载,在下面的例子中,类初始化中装载了一个平台特性的本地库,其中定义了一个test方法。

package pkg;

class Cls {

native double f(int i, String s);

static {

System.loadLibrary(“pkg_Cls”);

}

}

System.loadLibrary的参数是一个由程序员任意命名的库名,系统根据一个统一的,平台性的方法来库名到本地库名,例如,Solaris系统转换pkg_Cls到libpkg_Cls.so,而WIN32的系统转换pkg_Cls到pkg_Cls.dll。

程序员可以用一个单独的库来存储任意数量的类所需的本地方法,只要这些类用相同的类装载器装载。虚拟机内部提供了一系列已经装载好的本地库,供应商应该选择合适的库名来减小命名上的冲突。

本地库必须与虚拟机静态的连接,库与虚拟机镜像结合的方式是与实现相关的。

System.loadLibrary和等价的方法必须确保库被成功地加载。

一个和虚拟机结合的库L,被定为只有通过导出了JNI_OnLoad_L函数实现静态连接。

一个静态连接库L导出了 JNI_OnLoad_L 方法和JNI_OnLoad 方法,那么JNI_OnLoad方法将被忽略。

如果一个库L被静态地连接,那么第一次调用System.loadLibrary("L")或者等价的方法,JNI_OnLoad_L 方法将会按照JNI_OnLoad的规定引用相同地参数和返回预期值。

静态加载的库L将会禁止动态加载相同名字的库。

当包含静态链接库的类加载器被垃圾回收时,虚拟机将会调用 JNI_OnUnload_L函数,如果有这样的函数导出了。

如果静态链接导出了JNI_OnUnload_L函数和JNI_OnUnload函数,JNI_OnUnload函数将会被忽略。

程序员能够调用RegisterNatives() 函数来注册关联了类的本地方法, RegisterNatives() 对于静态连接函数非常有用 。

解析本地方法名

动态连接器根据名字解析条目,本地方法名是由下面这些部分串联组成的

· 前缀Java_

· 改编的完整类名

· 下划线分隔符

· 改编的方法名

· 对于重载的方法,用两个下划线隔开后面的参数标示

虚拟机检查方法名是否匹配属于本地库的方法。虚拟机第一次检查不带参数标示的短名称,再次检查带参数标示的长名称,程序员只需要在本地方法重载另一个本地方法时用到长名称。然而,本地方法名和非本地方法拥有相同的名称是允许的。非本地方法(JAVA方法)不在本地库中。

在下面的例子中,本地方g 不需要使用长名称连接,因为另一个方法g 不是本地方法,因此不在本地库中。

class Cls1 {

int g(int i);

native int g(double d);

}

我们采取了命名编码的方案来确保所有的Unicode字符翻译成有效的C函数名,使用下划线(_)来代替完全限定类名中的(/),因为名字和类型描述符不能以数字开头,我们可以用 _0,..._9来作为转义字符,如下表所示:


Unicode 字符翻译


转义字符


表示


_0XXXX


一个Unicode字符 XXXX. 注意小写用来表示非ASCII Unicode字符,例如, _0abcd和 _0ABCD是相反的。


_1


字符“_”


_2


签名中的字符 “;”


_3


签名中的字符 “[“

本地方法和接口API在给定的平台上都依据标准的库调用约定。例如,UNIX使用C调用约定,而WIN32系统使用 __stdcall 。

本地方法参数

JNI接口指针是给本地方法的第一个参数。JNI接口指针是JNIENV类型的。第二个参数由于方法动态或静态而不同。非静态的方法的第二个参数是对象的引用,静态方法的第二个参数是它JAVA类的引用。

剩下的参数是和普通的JAVA方法的参数一致。本地方法调用通过返回值传递它的结果给调用的函数。

下面的代码例子说明一个C函数实现的本地方法f,本地方法f定义如下:

package pkg;

class Cls {

native double f(int i, String s);

// ...

}

长编码名Java_pkg_Cls_f_ILjava_lang_String_2的C函数实现了本地方法f :

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (

JNIEnv *env,        /* interface pointer */

jobject obj,        /* "this" pointer */

jint i,             /* argument #1 */

jstring s)          /* argument #2 */

{

/* Obtain a C-copy of the Java string */

const char *str = (*env)->GetStringUTFChars(env, s, 0);

/* process the string */

...

/* Now we are done with str */

(*env)->ReleaseStringUTFChars(env, s, str);

return ...

}

注意的是我们总是使用接口指针env类操控JAVA对象。用C++ ,我们能够写出稍微清楚版本的代码,正如下面的代码例子所示:

extern "C" /* specify the C calling convention */

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (

JNIEnv *env,        /* interface pointer */

jobject obj,        /* "this" pointer */

jint i,             /* argument #1 */

jstring s)          /* argument #2 */

{

const char *str = env->GetStringUTFChars(s, 0);

// ...

env->ReleaseStringUTFChars(s, str);

// return ...

}

使用C++ ,额外的间接层和接口指针参数从源代码中消失了,然而,基本的机理还是和C一样的。在C++,JNI函数被定义成扩展成他们C的副本的内联成员函数。

引用JAVA对象

诸如整型、字符型的原始类型在JAVA和本地代码之间复制。任意的JAVA对象传递引用。虚拟机必须追踪所有的传到本地代码中的对象,因此这些对象不会被垃圾回收器释放掉。相反地,本地代码必须有一种方式去告诉虚拟机它不再需要这些对象,另外,垃圾回收器必须能够移动被本地代码指向的对象。

全局和局部引用

JNI根据本地代码将对象应用分为两种:局部引用和全局引用,局部应用在本地方法调用期间有效,在本地方法返回之后自动释放。全局引用一直保持有效,直到他们被明确释放。

对象作为局部引用传递到本地方法。JNI函数返回的所有JAVA对象都是局部引用。JNI允许程序员从局部引用创建全局引用。需要JAVA对象的JNI函数接收局部和全局引用。本地方法可能返回局部的和全局的引用给虚拟机。

大多数情况下,程序员需要依靠虚拟机在本地方法返回之后来释放所有的局部引用,然而,有时程序员需要明确地释放局部引用,例如,考虑到下面的情形:

· 本地方法访问一个大的JAVA对象,因此创建了JAVA对象的局部引用。本地方法在返回调用者之前执行着额外的运算,大JAVA对象的局部引用将会阻止垃圾回收器回收该对象,即使这个对象不在剩下的运算中使用。

· 本地方法创建了大量的局部引用,即使他们不在同一时间使用,由于虚拟机需要相应的空间来记录局部应用,创建太多的局部应用可能引起系统内存溢出,例如,本地嗲吗循环通过大批的对象,作为局部引用取回元素,在每次迭代操作一个元素。在每次迭代之后,程序员不再需要数组元素的局部引用。

JNI允许程序员手动地在本地方法中的删除任意处的局部引用,为了确保程序员能够手动地释放局部引用,JNI函数不允许创建额外的局部引用,除了作为返回值的引用。

局部应用只在创建他们的线程中有效,本地代码不能传递他们从一个线程到另一个线程。

实现局部引用

为了实现局部引用,JAVA虚拟机创建了一个从JAVA到本地方法的转变控制的注册机。注册机映射了JAVA对象的局部引用,并阻止对象被回收,所有的传递到本地方法的JAVA对象(包括作为JNI函数调用返回值的)自动添加到注册机。在本地方法返回之后,注册机将被删除,同时其中的条目将被垃圾回收。

有不同的实现注册机的方法,例如用表,链表,哈希表等。

注意局部引用不能通过适当地扫描本地栈来很好的实现,本地代码可能将局部引用存放在全局数据区或堆数据结构中。

访问JAVA对象

JNI提供了丰富的访问局部引用和全局引用的函数集。这意味着不论虚拟机内部怎么表示JAVA对象,只需要相同的本地方法实现。这就是JNI能够被大量的虚拟机实现的重要原因。

使用不透明引用的访问函数的费用高于直接访问C数据结构,我们相信,大多数情况下,JAVA程序员会使用本地方法来执行重要的任务而弱化接口的费用。

访问原始数组

对于大的包含很多像整型数组和字符串的原始数据类型的对象来说,费用是不能被接受的。(考虑到为了执行向量和矩阵计算的本地方法)迭代JAVA数组和通过函数调用取回每个元素师非常低效的。

一种解决方法介绍了概念“定位”,它是为了让本地方法使虚拟机限制数组的内容。本地方法接收一个直接指向元素的指针,然而,这种方法有两个含意:

· 垃圾回收器必须支持定位

· 虚拟机必须放置原始数组在连续的内存中。尽管这是大多数原始数组的自然实现,布尔类型的数组可以以压缩的和未压缩的形式实现,因此,依靠精确布局的布尔数组的本地代码不能被移植。

我们采用了一个折中的方式来克服上面的问题

首先,提供了一系列的函数在原始数组的部分和本地内存缓冲区之间进行复制,只在需要访问大数组的少量元素时使用。

其次,程序员能够使用另外系列的函数来取回固定版本的数组元素。记住这些函数可能要求JAVA虚拟机执行存储器的分配和拷贝。这些函数是否真正地复制了数组取决于虚拟机的实现,就像下面的例子:

· 如果垃圾回收器支持定位,数组的布局和本地方法需要的一样,那么就不需要复制。

· 否则,数组被复制到固定的存储块(例如,C中的堆区域),执行必要的格式转换,返回指向复制后的数组的指针。

最后,接口提供了通知虚拟机本地代码不再需要访问数组元素的函数,当你调用这些函数的时候,系统要么除去这个数组,要么调和原始数组和它的固定复制品并释放复制品。

JNI实现必须确保不同线程中的本地方法能够同时地访问相同的数组,注意,JNI不需要为了独占通过本地方法锁住原始数组,在不同的线程中同时去更新数组会导致不确定的结果。

访问域和方法

JNI 允许本地代码访问域和调用JAVA对象的方法。JNI通过符号名和类型标记来识别方法,查找域或者方法有两步。例如,调用cls类中f 方法,本地代码首先获取方法ID,就像下面:

jmethodID mid = env->GetMethodID(cls, “f”, “(ILjava/lang/String;)D”);

本地代码能够反复地使用这个方法ID而不需要再次去查方法,像下面:

jdouble result = env->CallDoubleMethod(obj, mid, 10, str);

域和方法的ID不能阻止虚拟机卸载已经生成方法ID类。当类被卸载后,方法ID和域ID将不可用,因此,如果长时间地使用一个方法ID或者域ID,本地代码必须保证:

· 保持一个基本类的存在的引用,或者

· 重新计算方法和域ID

JNI在内部实现不对域ID和方法ID强加任何限制。

记录编程错误

JNI不检查编程错误,像传递空指针,非法参数类型等。非法参数错误包括了用一个正常的JAVA对象代替一个JAVA类对象。JNI不检查编程错误的原因如下:

· 强制让JNI函数检查可能存在的错误条件会降低正常(没有错误)的本地方法的表现。

·在许多情况下,没有足够的运行时类型信息来进行这些检查。

大多数的C库函数防护编程错误。例如,printf()函数经常在接收到无效的地址引起一个运行时错误,而不是返回一个错误代码。强制C库函数检查所有的可能存在的错误可能导致这样的检查发生多次--一次在用户代码,再次在库中。

程序员不能传递非法的指针或者错误类型的参数给JNI函数。这样做将会导致任意的结果发生,包括系统的毁坏和虚拟机的崩溃。

JAVA异常

JNI允许本地方法提出任意的JAVA异常。本地代码可能处理先显著地JAVA异常,未经处理掉的异常将会被传回虚拟机中。

异常和错误代码

某些JNI函数用JAVA异常处理机制来记录错误情况,在大多数情况下,JNI函数用过返回错误代码和抛出异常来记录错误情况。错误代码通常是超出正常返回值范围的特殊返回值(例如NULL),因此,程序员能够:

· 快速检查上次JNI调用的返回值来判断是否有错误发生

· 调用函数 ExceptionOccurred()来获取包含了错误情况的详细描述的异常对象

有两种情形,程序员要检查异常,而不能首先检查错误代码:

· 调用JAVA方法的JNI函数返回JAVA方法的结果。程序员必须调用ExceptionOccurred()来检查在JAVA方法执行期间可能发生的异常。

· 有一些JNI数组访问函数不会返回错误代码,但是可能抛出异常ArrayIndexOutOfBoundsException或者ArrayStoreException。

在所有的其他情况下,非错误的返回值保证不抛出异常。

异步的异常

多线程的情况下,除了当前线程的其他线程可能会抛出异步的异常。异步的异常不会立即就影响当前线程的本地代码执行,直到:

· 本地代码调用了引发异步异常的JNI函数,或者

· 本地代码中使用ExceptionOccurred()来明确地检查同步的和异步的异常。

注意只有这些可能引发同步异常的JNI函数检查异步异常。

本地代码应该在适当的位置(比如在没有其他异常检查的密闭循环中)中插入ExceptionOccurred()来确保当前线程在合理的时间内响应异步异常。

异常处理

有两种方式来在本地代码中处理异常:

· 本地方法能够选择立刻返回,引起异常抛出到调用本地方法的JAVA代码中。

· 本地代码可以通过调用ExceptionClear()来清除异常,然后执行自己的异常处理代码。

在异常被引起之后,本地代码必须在进行其他JNI调用之前清除异常,当有挂起的异常时,可以安全调用的JNI函数有:

ExceptionOccurred()

ExceptionDescribe()

ExceptionClear()

ExceptionCheck()

ReleaseStringChars()

ReleaseStringUTFChars()

ReleaseStringCritical()

Release<Type>ArrayElements()

ReleasePrimitiveArrayCritical()

DeleteLocalRef()

DeleteGlobalRef()

DeleteWeakGlobalRef()

MonitorExit()

PushLocalFrame()

PopLocalFrame()

第三章 JNI类型和数据结构

本章讨论JNI怎么映射JAVA类型到C类型

本章涵盖了如下的主要内容:

· 原始类型

· 引用类型

· 域和方法ID

· 值类型

· 类型标示

· 改进的UTF-8字符串

原始类型

下面的表格描述了JAVA原始类型和与他们的机器相关的本地等价类型

原始类型和本地等价类型


Java Type


Native Type


Description


boolean


jboolean


unsigned 8 bits


byte


jbyte


signed 8 bits


char


jchar


unsigned 16 bits


short


jshort


signed 16 bits


int


jint


signed 32 bits


long


jlong


signed 64 bits


float


jfloat


32 bits


double


jdouble


64 bits


void


void


not applicable

下面的定义为了方便而提供的

#define JNI_FALSE  0

#define JNI_TRUE   1

jsize整型类型被用来描述基本的索引和大小。

typedef jint jsize;

引用类型

JNI包含了大量的相当于JAVA对象的引用类型,JNI引用类型是由下面的层级关系组织起来的:

· jobject

· jclass (java.lang.Class objects)

· jstring (java.lang.String objects)

· jarray (arrays)

· jobjectArray (object arrays)

· jbooleanArray (boolean arrays)

· jbyteArray (byte arrays)

· jcharArray (char arrays)

· jshortArray (short arrays)

· jintArray (int arrays)

· jlongArray (long arrays)

· jfloatArray (float arrays)

· jdoubleArray (double arrays)

· jthrowable (java.lang.Throwable objects)

在所有的C语言中,JNI引用类型被定义成和jobject一样,例如:

typedef jobject jclass;

在C++中,JNI介绍了一种虚拟的类来加强子类型的关系,例如:

class _jobject {};

class _jclass : public _jobject {};

// ...

typedef _jobject *jobject;

typedef _jclass *jclass;

域和方法ID

方法和域ID是规则的C指针类型:

struct _jfieldID;              /* opaque structure */

typedef struct _jfieldID *jfieldID;   /* field IDs */

struct _jmethodID;              /* opaque structure */

typedef struct _jmethodID *jmethodID; /* method IDs */

值类型

联合类型jvalue被用在参数数组中,它是如下定义的:

typedef union jvalue {

jboolean z;

jbyte    b;

jchar    c;

jshort   s;

jint     i;

jlong    j;

jfloat   f;

jdouble  d;

jobject  l;

} jvalue;

类型标示

JNI使用JAVA虚拟机的类型标示表示法,下面的表格显示了这些类型标示


JAVA虚拟类型标示


类型标示


JAVA类型


Z


boolean


B


byte


C


char


S


short


I


int


J


long


F


float


D


double


Lpkg/Cls ;


pkg.Cls (完整类名)


[ type


type[] (数组)


( 参数类型 )返回值类型


方法类型

举个例子,JAVA方法:

long f (int n, String s, int[] arr);

有如下的类型标示:

(ILjava/lang/String;[I)J

改进的UTF-8 字符串

JNI用改进的UTF-8字符串来代表可变的字符串类型。改进的UTF-8字符串是和JAVA虚拟机使用的一样。改进的UTF-8字符串编码是为了只包含非空ASCII字符的字符序列能够用每字符一字节来表示,但是所有的Unicode字符都能够表示。

所有的在范围从\u0001到\u007F中的字符都能够用一字节表示,如下:

0xxxxxxx

字节数据的七位给出了所代表的字符。

空字符(‘\u0000‘) 和字符从 ‘\u0080‘ 到 ‘\u07FF‘ 通过一对字节的x和y 表示:

x:110xxxxx

y:10yyyyyy

字节代表了值为的 ((x & 0x1f) << 6) + (y & 0x3f)字符。

范围在‘\u0800‘ 到 ‘\uFFFF‘ 的字符用3个字节x,y,z表示:

x:1110xxxx

y:10yyyyyy

z:10zzzzzz

所代表的字符的值为 ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f) 。

在U+FFFF之上的编码点(被称为辅助字符)的字符,通过分别编码他们UTF-16表示的两个代理码来表示,每个代理码由三个字节代表。这意味着,辅助字符由六个字节u,v,w,x,y,z来表示

u:11101101

v:1010vvvv

w:10wwwwww

x:11101101

y:1011yyyy

z:10zzzzzz

六个字节所代表的字符值为 ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f) 。

多字符的字节数以大端序存放在class文件中。

这种格式和标准UTF-8格式有两种不同。首先,空字符0是用两个字节而不是一个字节编码的,这意味着改进的UTF-8没有嵌入的空值。其次,只有一字节、两字节、三字节的标准UTF-8被使用了。JAVA虚拟机不能识别四字节格式的标准UTF-8;它用两次三字节的格式代替。

第四章 JNI函数

这章的作用是JNI函数的参考部分。提供了全部的JNI函数。也显示了JNI函数表准确的设计。

注意用了条目“必须”来描述对JNI编程者的限制。例如,当你看见某个JNI函数必须接收非空的对象,那么保证空值不能传给JNI函数将是你的责任。因此,JNI实现不需要在JNI函数中进行空指针的检查。

本章的这部分是由网景公司的JRI文档改编过来的。

这些参考资料是通过用处分组的,参考部分是由下面的功能区域组织起来的。

接口函数表 (Interface Function Table)

版本信息(Version Information)

GetVersion

Constants

类操作(Class Operations)

DefineClass

FindClass

GetSuperclass

IsAssignableFrom

异常(Exceptions)

Throw

ThrowNew

ExceptionOccurred

ExceptionDescribe

ExceptionClear

FatalError

ExceptionCheck

全局和局部引用(Global and Local References)

Global References

NewGlobalRef

DeleteGlobalRef

Local References

DeleteLocalRef

EnsureLocalCapacity

PushLocalFrame

PopLocalFrame

NewLocalRef

弱全局引用(Weak Global References)

NewWeakGlobalRef

DeleteWeakGlobalRef

对象操作(Object Operations)

AllocObject

NewObject, NewObjectA, NewObjectV

GetObjectClass

GetObjectRefType

IsInstanceOf

IsSameObject

获取域和对象(Accessing Fields of Objects)

GetFieldID

Get<type>Field Routines

Set<type>Field Routines

调用实例方法(Calling Instance Methods)

GetMethodID

Call<type>Method Routines, Call<type>MethodA Routines, Call<type>MethodV Routines

CallNonvirtual<type>Method Routines, CallNonvirtual<type>MethodA Routines, CallNonvirtual<type>MethodV Routines

获取静态域(Accessing Static Fields)

GetStaticFieldID

GetStatic<type>Field Routines

SetStatic<type>Field Routines

调用静态方法(Calling Static Methods)

GetStaticMethodID

CallStatic<type>Method Routines, CallStatic<type>MethodA Routines, CallStatic<type>MethodV Routines

字符串操作(String Operations)

NewString

GetStringLength

GetStringChars

ReleaseStringChars

NewStringUTF

GetStringUTFLength

GetStringUTFChars

ReleaseStringUTFChars

GetStringRegion

GetStringUTFRegion

GetStringCritical, ReleaseStringCritical

数组操作(Array Operations)

GetArrayLength

NewObjectArray

GetObjectArrayElement

SetObjectArrayElement

New<PrimitiveType>Array Routines

Get<PrimitiveType>ArrayElements Routines

Release<PrimitiveType>ArrayElements Routines

Get<PrimitiveType>ArrayRegion Routines

Set<PrimitiveType>ArrayRegion Routines

GetPrimitiveArrayCritical, ReleasePrimitiveArrayCritical

注册本地方法(Registering Native Methods)

RegisterNatives

UnregisterNatives

监控操作(Monitor Operations)

MonitorEnter

MonitorExit

网络接口支持(NIO Support)

NewDirectByteBuffer

GetDirectBufferAddress

GetDirectBufferCapacity

反射支持(Reflection Support)

FromReflectedMethod

FromReflectedField

ToReflectedMethod

ToReflectedField

JAVA虚拟机接口(Java VM Interface)

GetJavaVM

接口函数表

每个函数通过参数JNIEnv的固定偏移变得可用。JNIEnv类型是一个指向存储了所有JNI函数指针的结构体的指针,定义如下:

typedef const struct JNINativeInterface *JNIEnv;

虚拟机像下面显示的代码例子一样初始化函数表。注意最先的三个条目是为未来的接口兼容性而保留的。另外,我们保留了一些额外的空条目靠近函数表的开始,为此,举个例子,将来的一个和类相关的JNI操作能够被添加到FindClass后面,而不是表的最后面。

注意函数表能够在所有的JNI接口指针中共享

const struct JNINativeInterface ... = {

NULL,

NULL,

NULL,

NULL,

GetVersion,

DefineClass,

FindClass,

FromReflectedMethod,

FromReflectedField,

ToReflectedMethod,

GetSuperclass,

IsAssignableFrom,

ToReflectedField,

Throw,

ThrowNew,

ExceptionOccurred,

ExceptionDescribe,

ExceptionClear,

FatalError,

PushLocalFrame,

PopLocalFrame,

NewGlobalRef,

DeleteGlobalRef,

DeleteLocalRef,

IsSameObject,

NewLocalRef,

EnsureLocalCapacity,

AllocObject,

NewObject,

NewObjectV,

NewObjectA,

GetObjectClass,

IsInstanceOf,

GetMethodID,

CallObjectMethod,

CallObjectMethodV,

CallObjectMethodA,

CallBooleanMethod,

CallBooleanMethodV,

CallBooleanMethodA,

CallByteMethod,

CallByteMethodV,

CallByteMethodA,

CallCharMethod,

CallCharMethodV,

CallCharMethodA,

CallShortMethod,

CallShortMethodV,

CallShortMethodA,

CallIntMethod,

CallIntMethodV,

CallIntMethodA,

CallLongMethod,

CallLongMethodV,

CallLongMethodA,

CallFloatMethod,

CallFloatMethodV,

CallFloatMethodA,

CallDoubleMethod,

CallDoubleMethodV,

CallDoubleMethodA,

CallVoidMethod,

CallVoidMethodV,

CallVoidMethodA,

CallNonvirtualObjectMethod,

CallNonvirtualObjectMethodV,

CallNonvirtualObjectMethodA,

CallNonvirtualBooleanMethod,

CallNonvirtualBooleanMethodV,

CallNonvirtualBooleanMethodA,

CallNonvirtualByteMethod,

CallNonvirtualByteMethodV,

CallNonvirtualByteMethodA,

CallNonvirtualCharMethod,

CallNonvirtualCharMethodV,

CallNonvirtualCharMethodA,

CallNonvirtualShortMethod,

CallNonvirtualShortMethodV,

CallNonvirtualShortMethodA,

CallNonvirtualIntMethod,

CallNonvirtualIntMethodV,

CallNonvirtualIntMethodA,

CallNonvirtualLongMethod,

CallNonvirtualLongMethodV,

CallNonvirtualLongMethodA,

CallNonvirtualFloatMethod,

CallNonvirtualFloatMethodV,

CallNonvirtualFloatMethodA,

CallNonvirtualDoubleMethod,

CallNonvirtualDoubleMethodV,

CallNonvirtualDoubleMethodA,

CallNonvirtualVoidMethod,

CallNonvirtualVoidMethodV,

CallNonvirtualVoidMethodA,

GetFieldID,

GetObjectField,

GetBooleanField,

GetByteField,

GetCharField,

GetShortField,

GetIntField,

GetLongField,

GetFloatField,

GetDoubleField,

SetObjectField,

SetBooleanField,

SetByteField,

SetCharField,

SetShortField,

SetIntField,

SetLongField,

SetFloatField,

SetDoubleField,

GetStaticMethodID,

CallStaticObjectMethod,

CallStaticObjectMethodV,

CallStaticObjectMethodA,

CallStaticBooleanMethod,

CallStaticBooleanMethodV,

CallStaticBooleanMethodA,

CallStaticByteMethod,

CallStaticByteMethodV,

CallStaticByteMethodA,

CallStaticCharMethod,

CallStaticCharMethodV,

CallStaticCharMethodA,

CallStaticShortMethod,

CallStaticShortMethodV,

CallStaticShortMethodA,

CallStaticIntMethod,

CallStaticIntMethodV,

CallStaticIntMethodA,

CallStaticLongMethod,

CallStaticLongMethodV,

CallStaticLongMethodA,

CallStaticFloatMethod,

CallStaticFloatMethodV,

CallStaticFloatMethodA,

CallStaticDoubleMethod,

CallStaticDoubleMethodV,

CallStaticDoubleMethodA,

CallStaticVoidMethod,

CallStaticVoidMethodV,

CallStaticVoidMethodA,

GetStaticFieldID,

GetStaticObjectField,

GetStaticBooleanField,

GetStaticByteField,

GetStaticCharField,

GetStaticShortField,

GetStaticIntField,

GetStaticLongField,

GetStaticFloatField,

GetStaticDoubleField,

SetStaticObjectField,

SetStaticBooleanField,

SetStaticByteField,

SetStaticCharField,

SetStaticShortField,

SetStaticIntField,

SetStaticLongField,

SetStaticFloatField,

SetStaticDoubleField,

NewString,

GetStringLength,

GetStringChars,

ReleaseStringChars,

NewStringUTF,

GetStringUTFLength,

GetStringUTFChars,

ReleaseStringUTFChars,

GetArrayLength,

NewObjectArray,

GetObjectArrayElement,

SetObjectArrayElement,

NewBooleanArray,

NewByteArray,

NewCharArray,

NewShortArray,

NewIntArray,

NewLongArray,

NewFloatArray,

NewDoubleArray,

GetBooleanArrayElements,

GetByteArrayElements,

GetCharArrayElements,

GetShortArrayElements,

GetIntArrayElements,

GetLongArrayElements,

GetFloatArrayElements,

GetDoubleArrayElements,

ReleaseBooleanArrayElements,

ReleaseByteArrayElements,

ReleaseCharArrayElements,

ReleaseShortArrayElements,

ReleaseIntArrayElements,

ReleaseLongArrayElements,

ReleaseFloatArrayElements,

ReleaseDoubleArrayElements,

GetBooleanArrayRegion,

GetByteArrayRegion,

GetCharArrayRegion,

GetShortArrayRegion,

GetIntArrayRegion,

GetLongArrayRegion,

GetFloatArrayRegion,

GetDoubleArrayRegion,

SetBooleanArrayRegion,

SetByteArrayRegion,

SetCharArrayRegion,

SetShortArrayRegion,

SetIntArrayRegion,

SetLongArrayRegion,

SetFloatArrayRegion,

SetDoubleArrayRegion,

RegisterNatives,

UnregisterNatives,

MonitorEnter,

MonitorExit,

GetJavaVM,

GetStringRegion,

GetStringUTFRegion,

GetPrimitiveArrayCritical,

ReleasePrimitiveArrayCritical,

GetStringCritical,

ReleaseStringCritical,

NewWeakGlobalRef,

DeleteWeakGlobalRef,

ExceptionCheck,

NewDirectByteBuffer,

GetDirectBufferAddress,

GetDirectBufferCapacity,

GetObjectRefType

};

版本信息

GetVersion

jint GetVersion(JNIEnv *env);

返回本地方法接口的版本。

LINKAGE:

JNIEnv的接口函数表索引 4 。

PARAMETERS:

env: JNI接口指针.

RETURNS:

返回主版本号在高16位,次版本号在低16位。

在 JDK/JRE 1.1, GetVersion() 返回0x00010001.

在 JDK/JRE 1.2, GetVersion() 返回0x00010002.

在 JDK/JRE 1.4, GetVersion() 返回0x00010004.

在 JDK/JRE 1.6, GetVersion() 返回0x00010006.

常量 Constants

从 JDK/JRE 1.2:

#define JNI_VERSION_1_1 0x00010001

#define JNI_VERSION_1_2 0x00010002

/* 错误码 */

#define JNI_EDETACHED    (-2)              /* 线程脱离虚拟机*/

#define JNI_EVERSION     (-3)              /* JNI版本错误 */

从JDK/JRE 1.4:

#define JNI_VERSION_1_4 0x00010004

从JDK/JRE 1.6:

#define JNI_VERSION_1_6 0x00010006

类操作Class Operations

DefineClass

jclass DefineClass(JNIEnv *env, const char *name, jobject loader,

const jbyte *buf, jsize bufLen);

从原始类数据缓冲区装载一个类. DefineClass调用返回之后, 包含原始类数据的缓冲区不被虚拟机引用,如果需要的话可能会被丢弃.

LINKAGE:

JNIEnv的接口函数表索引 5。

PARAMETERS:

env: JNI接口指针.

name: 被定义的类或者接口的名称. 字符串用改进的UTF-8编码.

loader:分配给确定类的类装载器.

buf: 包含 .class 文件数据的缓冲区.

bufLen: 缓冲区长度.

RETURNS:

返回JAVA对象,或者发生错误返回NULL。

THROWS:

ClassFormatError: 如果类数据没有指定一个有效的类.

ClassCircularityError: 如果一个类或者接口是它自己的超类或者超接口.

OutOfMemoryError: 如果系统内存溢出.

SecurityException: 如果调用者尝试定义一个类在“java”包结构树下.

FindClass

jclass FindClass(JNIEnv *env, const char *name);

在 JDK 1.1发行版, 这个函数装在一个本地定义的类. 它在由CLASSPATH 环境变量指定的目录和zip文件中查找指定名称的类。

从 Java 2 SDK 1.2发行版, JAVA安全模型允许装载非系统类和调用本地方法. FindClass 查找和当前本地方法相关联的类加载器; 也就是, 声明本地方法的类的类加载器. 如果本地方法属于系统类,将没有相关的类加载器.否则,适当的类加载器将会装载和连接指定的类.

从 Java 2 SDK 1.2发行版,当通过调接口调用了FindClass , 将没有当前的本地方法和相关的类加载器. 这种情况下, ClassLoader.getSystemClassLoader的结果将被使用. 这是虚拟机为应用程序创建的类加载器, 它能在列出在 java.class.path 属性中查找类。

参数name 是一个完全限定的类名或者数组类型标示. 例如java.lang.String类的完全限定类名是:

"java/lang/String"

类数组java.lang.Object[] 数组类型标示是:

"[Ljava/lang/Object;"

LINKAGE:

JNIEnv的接口函数表索引 5。

PARAMETERS:

env: JNI接口指针.

name: 完全限定的类名 (也就是, 包名,用“/”分隔开,然后是类名). 如果名字以 “[“ (数组标示字符)开头,返回数组类.字符串用改进型UTF-8 编码.

RETURNS:

从完全限定类名返回类对象,如果没有找到返回NULL。

THROWS:

ClassFormatError: 如果类数据没有指定一个有效的类.

ClassCircularityError: 如果一个类或者接口是它自己的超类或者超接口.

NoClassDefFoundError: 如果要求的类和接口定义没有找到.

OutOfMemoryError:  如果系统内存溢出.

GetSuperclass

jclass GetSuperclass(JNIEnv *env, jclass clazz);

如果 clazz表示除了Object的任何类,那么这个函数返回代表这个指定类clazz的超类。

如果 clazz 表示 Object类,或者clazz 表示一个接口, 函数返回NULL.

LINKAGE:

JNIEnv的接口函数表索引 10。

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类对象.

RETURNS:

返回指定类clazz的超类, 或者 NULL.

IsAssignableFrom

jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1,

jclass clazz2);

确定对象clazz1 能否安全转化为 clazz2.

LINKAGE:

JNIEnv的接口函数表索引 11。

PARAMETERS:

env:JNI接口指针.

clazz1: 第一个类参数.

clazz2: 第二个类参数.

RETURNS:

如果下面任意一种情况成立,返回JNI_TRUE :

第一个和第二个类参数是同一个JAVA类.

第一个类参数是第二个类参数的超类.

第一个类有第二个类作为接口.

异常 Exceptions

Throw

jint Throw(JNIEnv *env, jthrowable obj);

引起java.lang.Throwable对象的抛出.

LINKAGE:

JNIEnv的接口函数表索引 13。

PARAMETERS:

env: JNI接口指针.

obj: 一个java.lang.Throwable 对象.

RETURNS:

成功返回0,失败返回负数.

THROWS:

java.lang.Throwable 对象.

ThrowNew

jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);

从指定类创建一个附带消息 message的异常类,并且引起该异常抛出.

LINKAGE:

JNIEnv的接口函数表索引 14。

PARAMETERS:

env: JNI接口指针.

obj: 一个java.lang.Throwable 对象.

message: 用来创建java.lang.Throwable对象的消息 ,字符串用改进型UTF-8 编码.

RETURNS:

成功返回0,失败返回负数.

THROWS:

java.lang.Throwable 对象.

ExceptionOccurred

jthrowable ExceptionOccurred(JNIEnv *env);

确定是否有异常抛出。 该异常将会保持抛出直到调用本地方法ExceptionClear(),或者JAVA代码处理了异常.

LINKAGE:

JNIEnv的接口函数表索引 15。

PARAMETERS:

env: JNI接口指针.

RETURNS:

返回当前过程中抛出的异常,如果当前过程中没有抛出异常返回NULL.

ExceptionDescribe

void ExceptionDescribe(JNIEnv *env);

打印异常和栈追踪信息到系统错误记录通道,例如stderr. 这是一个方便调试的程序。

LINKAGE:

JNIEnv的接口函数表索引 16。

PARAMETERS:

env: JNI接口指针.

ExceptionClear

void ExceptionClear(JNIEnv *env);

清除当前抛出的异常,如果当前没有抛出异常,该函数没有效果。

LINKAGE:

JNIEnv的接口函数表索引 17。

PARAMETERS:

env: JNI接口指针.

FatalError

void FatalError(JNIEnv *env, const char *msg);

引起一个重大的错误,不期望虚拟机还原。这个函数没有返回值

LINKAGE:

JNIEnv的接口函数表索引 18。

PARAMETERS:

env:JNI接口指针

msg: 错误信息. 字符串以改进型UTF-8编码.

ExceptionCheck

现在介绍一种方便的不用创建异常对象的局部引用来检查即将发生异常的函数。

jboolean ExceptionCheck(JNIEnv *env);

如果有即将发生的异常,返回JNI_TRUE ,否则返回JNI_FALSE.

LINKAGE:

JNIEnv的接口函数表索引 228。

SINCE:

JDK/JRE 1.2

全局和局部引用 Global and Local References

全局引用 Global References

NewGlobalRef

jobject NewGlobalRef(JNIEnv *env, jobject obj);

创建一个新的指向obj对象的全局引用.参数obj可能是局部或全局的引用. 全局引用必须调用DeleteGlobalRef()明确地处理好.

LINKAGE:

JNIEnv的接口函数表索引 21。

PARAMETERS:

env: JNI接口指针

obj: 全局的和局部的引用.

RETURNS:

返回一个全局引用, 如果系统内存溢出返回NULL。

DeleteGlobalRef

void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

删除 globalRef 指向的全局引用.

LINKAGE:

JNIEnv的接口函数表索引 22。

PARAMETERS:

env: JNI接口指针

globalRef: 全局引用.

局部引用 Local References

局部引用在本地方法调用期间有效。它们在本地方法返回之后自动释放。每个局部引用都会花费一定数量的系统资源。程序员需要确保本地方法不要多度地分配局部引用。尽管局部引用在本地方法返回到JAVA时自动释放,到时过度的分配局部引用会在执行本地方法期间造成虚拟机运行内存溢出。

DeleteLocalRef

void DeleteLocalRef(JNIEnv *env, jobject localRef);

删除 localRef 指向的局部引用.

LINKAGE:

JNIEnv的接口函数表索引 23。

PARAMETERS:

env:JNI接口指针

localRef: 局部引用

注意: JDK/JRE 1.1 提供了 DeleteLocalRef 函数来让程序员手动地删除局部引用. 例如, 如果本地代码迭代一个可能很大的数组对象,并且每次迭代只用一个元素, 在下次迭代一个新的引用创建出来之前删除不再使用的元素的引用是一个好的做法。

JDK/JRE 1.2 附加了一个对局部引用终身管理的函数集,他们是下面列出来的四个函数:

EnsureLocalCapacity

jint EnsureLocalCapacity(JNIEnv *env, jint capacity);

确定在当前线程中至少给定数目的局部引用能被创建.成功返回0; 否则返回一个负数,并抛出内存溢出错误 OutOfMemoryError.

在进入本地方法之前, 虚拟机自动确保至少16个局部引用能够被创建.

为了向后兼容性, 虚拟机分配本地引用超出确保的容量. (作为调试支持, 虚拟机将会给出警告给用户创建了太多的局部引用. 在JDK之中, 程序员能够提供-verbose:jni 命令行选项来打开信息.)如果超出确保的容量而没有更多的局部引用能够被创建,虚拟机调用 FatalError .

LINKAGE:

JNIEnv的接口函数表索引 26。

SINCE:

JDK/JRE 1.2

PushLocalFrame

jint PushLocalFrame(JNIEnv *env, jint capacity);

创建一个新的能创建至少指定数目的局部引用的局部引用框架,成功返回0,失败返回负数或者发生OutOfMemoryError 。

注意已经在先前的局部引用框架中创建的局部引用在当前的局部引用框架中仍然有效。

LINKAGE:

JNIEnv的接口函数表索引 19。

SINCE:

JDK/JRE 1.2

PopLocalFrame

jobject PopLocalFrame(JNIEnv *env, jobject result);

弹出当前的局部引用框架, 释放所有的局部引用, 在前面的局部引用框架中返回指定result对象的局部引用.

如果不需要返回前面框架的引用传递NULL作为result.

LINKAGE:

JNIEnv的接口函数表索引 20。

SINCE:

JDK/JRE 1.2

NewLocalRef

jobject NewLocalRef(JNIEnv *env, jobject ref);

创建一个指向并作为和ref 相同的对象的局部引用.给定的ref 可能是全局的和局部的引用. 如果ref指向为null时返回NULL.

LINKAGE:

JNIEnv的接口函数表索引 25。

SINCE:

JDK/JRE 1.2

弱全局引用 Weak Global References

弱全局引用时一种特殊的全局引用。 不同于常规的全局应用,弱全局引用允许垃圾回收基本的对象,弱局部引用可以用在任意局部和全局引用所用的地方. 当垃圾回收运行时, 将会释放掉值被弱局部引用指向的基本对象. 弱引用指向一个释放掉的对象在功能上等价于NULL. 程序员可以通过使用IsSameObject 来比较弱全局引用和 NULL来检测一个弱局部引用是否指向一个被释放掉的对象.

JNI中的弱全局引用是JAVA中弱全局引用的简单版本,JAVA中弱全局引用作为Java 2平台API ( java.lang.ref 包和它的类 )的一部分而有效。

说明 (添加于 2001 年 1 月)

因为垃圾回收能在本地方法运行的时候发生,弱全局引用指向的对象能在任意时间被释放. 在使用全局引用的地方使用弱全局引用是不合适的,因为它可能在没有通知的情况下变得等价于NULL.

使用IsSameObject 来确定弱全局引用是否指向一个释放掉的对象时,不能阻止对象其后被立即释放. 因此, 程序员不能依靠这个检查来决定弱引用是否能够在后面的任意JNI函数中使用 (作为非空引用) .

为了克服这些内在的限制,推荐使用JNI函数 NewLocalRef 或者 NewGlobalRef来获得标准(强)的局部引用或者全局引用指向相同的对象, 强引用被用来访问想要的对象. 如果对象被释放,这些函数将会返回NULL,否则返回强引用 (能够阻止对象被释放).当不再要求直接访问对象,新的引用将要明确地删除来确保对象可以被释放.

弱全局引用弱于其他类型的弱引用(SoftReference 和 WeakReference类的JAVA对象). 直到SoftReference 或者 WeakReference 对象指向的特定的对象清除了引用,指向该特定对象的弱引用才会在功能上等价于NULL.

弱全局引用弱于 Java内部指向需要终结化的对象的引用.如果该引用对象存在,直到终结器完成引用对象的终结,弱全局引用才能在功能上等价于NULL.

弱全局引用和 PhantomReferences的交互是不明确地. 特别是, JAVA虚拟机的实现可能 (或者不可能)在PhantomReferences之后进行弱全局引用,  使用弱全局引用指向 PhantomReference 对象已经指向的对象可能 (或者不可能)是合理的. 要避免这种弱全局引用的不明确使用.

NewWeakGlobalRef

jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);

创建一个新的弱全局引用. 如果obj指向为空,或者虚拟机运行内存溢出,返回NULL.如果虚拟机运行内存溢出, 抛出OutOfMemoryError.

LINKAGE:

JNIEnv的接口函数表索引 226。

SINCE:

JDK/JRE 1.2

DeleteWeakGlobalRef

void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);

删除指定弱全局引用所需的虚拟机资源.

LINKAGE:

JNIEnv的接口函数表索引 227。

SINCE:

JDK/JRE 1.2

对象操作 Object Operations

AllocObject

jobject AllocObject(JNIEnv *env, jclass clazz);

不调用任何的构造函数来分配一个新的对象.返回对象的引用.

参数clazz 不能指向数组类.

LINKAGE:

JNIEnv的接口函数表索引 27。

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类对象.

RETURNS:

返回JAVA对象, 如果对象不能够被创建返回NULL.

THROWS:

InstantiationException: 如果该类是接口或者抽象类.

OutOfMemoryError: 如果系统内存溢出.

NewObject, NewObjectA, NewObjectV

jobject NewObject(JNIEnv *env, jclass clazz,

jmethodID methodID, ...);

jobject NewObjectA(JNIEnv *env, jclass clazz,

jmethodID methodID, const jvalue *args);

jobject NewObjectV(JNIEnv *env, jclass clazz,

jmethodID methodID, va_list args);

构造新的JAVA对象. 方法ID指示哪个构造方法被调用. 这个ID必须要通过调用GetMethodID()获取以<init> 作为方法名和 void (V) 作为返回值的方法的ID.

参数 clazz 不能指向数组类.

NewObject

程序员放置所有要传递给构造函数的参数在 methodID 参数后面. NewObject()接收这些参数,并传递它们给程序员期望调用的方法.

LINKAGE:

JNIEnv的接口函数表索引 28。

NewObjectA

程序员放置所有要传递给构造函数的参数在一个jvalues类型的数组args中跟随在 methodID 参数后面. NewObjectA()接收这个数组中的所有参数, 并传递它们给程序员期望调用的方法.

LINKAGE:

JNIEnv的接口函数表索引 30。

NewObjectV

程序员放置所有要传递给构造函数的参数在一个va_list类型的参数args中跟随在 methodID 参数后面.NewObjectV()接收这个参数, 并传递它们给程序员期望调用的方法.

LINKAGE:

JNIEnv的接口函数表索引 29。

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类对象.

methodID: 构造函数的方法ID.

Additional Parameter for NewObject:

构造方法的参数

Additional Parameter for NewObjectA:

args: 构造方法的参数数组

Additional Parameter for NewObjectV:

args:构造方法的va_list参数.

RETURNS:

返回JAVA对象,如果不能构造,返回NULL

THROWS:

InstantiationException: 如果该类是接口或者抽象类.

OutOfMemoryError: 如果系统内存溢出.

任意构造函数抛出的异常

GetObjectClass

jclass GetObjectClass(JNIEnv *env, jobject obj);

返回对象的类

LINKAGE:

JNIEnv的接口函数表索引 31。

PARAMETERS:

env: JNI接口指针

obj: JAVA对象 (必须不为 NULL).

RETURNS:

返回JAVA类的对象.

GetObjectRefType

jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);

返回参数obj指向的对象的类型. 参数obj 可以是局部、全局、弱全局引用.

LINKAGE:

JNIEnv的接口函数表索引 232。

PARAMETERS:

env: JNI接口指针.

obj: 局部、全局、弱全局引用.

vm: 接口将被检索的虚拟机实例.

env: 指向当前线程的JNI接口指针放置的地方.

version: 需要的JNI版本.

RETURNS:

函数GetObjectRefType 返回被定义为jobjectRefType类型的枚举值:

JNIInvalidRefType = 0,

JNILocalRefType = 1,

JNIGlobalRefType = 2,

JNIWeakGlobalRefType = 3

如果参数obj 是弱全局引用类型, 返回值是JNIWeakGlobalRefType.

如果参数obj 是全局引用类型, 返回值是JNIGlobalRefType.

如果参数obj 是局部引用类型, 返回值是JNILocalRefType.

如果参数obj 是无效引用, 返回值是JNIInvalidRefType.

无效引用是一个没有有效处理的引用. 也就是,  obj 指针地址没有指向内存中从一个Ref创建函数分配或者从JNI函数返回的地址.

同样地, NULL是无效引用,执行 GetObjectRefType(env,NULL)将返回 JNIInvalidRefType.

另一方面, 指向空的空引用, 将会返回空引用原来创建时的引用类型.

GetObjectRefType 不能用在已经删除的引用上.

因为引用是作为指向可能被虚拟机中任意引用分配服务重新使用的存储数据结构而实现的, 一旦删除,没有指明 GetObjectRefType 将会返回什么值.

SINCE:

JDK/JRE 1.6

IsInstanceOf

jboolean IsInstanceOf(JNIEnv *env, jobject obj,

jclass clazz);

测试一个对象是否是一个类的实例.

LINKAGE:

JNIEnv的接口函数表索引 32。

PARAMETERS:

env: JNI接口指针.

obj: JAVA类.

clazz: JAVA类的对象.

RETURNS:

返回JNI_TRUE 如果obj 能够转成clazz; 否则, 返回JNI_FALSE.一个 NULL 对象不能转成其他类.

IsSameObject

jboolean IsSameObject(JNIEnv *env, jobject ref1,

jobject ref2);

检测两个引用是否指向同一个对象.

LINKAGE:

JNIEnv的接口函数表索引 24。

PARAMETERS:

env: JNI接口指针.

ref1: JAVA对象.

ref2: JAVA对象.

RETURNS:

返回JNI_TRUE 如果 ref1 和ref2 指向相同的JAVA对象或者都为NULL; 否则, 返回JNI_FALSE.

访问对象的域 Accessing Fields of Objects

GetFieldID

jfieldID GetFieldID(JNIEnv *env, jclass clazz,

const char *name, const char *sig);

返回一个类的一个实例(非静态)域的域ID. 域是由名字和标示指定的. Get<type>Field 和Set<type>Field 存取器函数家族用域ID来取出对象的域.

GetFieldID() 引起一个未初始化的类初始化.

GetFieldID() 不能用来获取带长度的数组域. 用 GetArrayLength() 来代替.

LINKAGE:

JNIEnv的接口函数表索引 94。

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类的对象.

name: 域名称,用0终止的改进型UTF-8字符串.

sig: 域标示,用0终止的改进型UTF-8字符串.

RETURNS:

返回域 ID, 或者操作失败返回 NULL.

THROWS:

NoSuchFieldError: 如果指定的域不存在.

ExceptionInInitializerError: 如果类由于异常初始化失败.

OutOfMemoryError:如果系统运行内存溢出.

Get<type>Field Routines

NativeType Get<type>Field(JNIEnv *env, jobject obj,

jfieldID fieldID);

存取器程序家族返回一个类的实例(非静态)域的值. 要访问的域是通过函数GetFieldID()获取的域ID指定的.

下表描述了 Get<type>Field 程序名称和返回类型. 你应该用域的JAVA类型来替换 Get<type>Field 中的类型, 或者使用表中的一个程序名称, 用那个程序相应的本地类型来代替本地类型.


Get<type>Field存取器程序家族


Get<type>Field 程序名称


本地类型


GetObjectField()


jobject


GetBooleanField()


jboolean


GetByteField()


jbyte


GetCharField()


jchar


GetShortField()


jshort


GetIntField()


jint


GetLongField()


jlong


GetFloatField()


jfloat


GetDoubleField()


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


Get<type>Field 存取器程序家族


Get<type>Field 程序名


索引


GetObjectField()


95


GetBooleanField()


96


GetByteField()


97


GetCharField()


98


GetShortField()


99


GetIntField()


100


GetLongField()


101


GetFloatField()


102


GetDoubleField()


103

PARAMETERS:

env: JNI接口指针.

obj: JAVA对象 (必须不为 NULL).

fieldID: 一个有效的域ID.

RETURNS:

返回域的内容.

Set<type>Field Routines

void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,NativeType value);

存取器程序家族设置一个类的实例(非静态)域的值. 要访问的域是通过函数GetFieldID()获取的域ID指定的.

下表描述了 Get<type>Field 程序名称和返回类型. 你应该用域的JAVA类型来替换 Get<type>Field 中的类型, 或者使用表中的一个程序名称, 用那个程序相应的本地类型来代替本地类型.


Set<type>Field 存取器程序家族


Set<type>Field 程序名称


本地类型


SetObjectField()


jobject


SetBooleanField()


jboolean


SetByteField()


jbyte


SetCharField()


jchar


SetShortField()


jshort


SetIntField()


jint


SetLongField()


jlong


SetFloatField()


jfloat


SetDoubleField()


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


Set<type>Field 存取器程序家族


Set<type>Field 程序名称


索引


SetObjectField()


104


SetBooleanField()


105


SetByteField()


106


SetCharField()


107


SetShortField()


108


SetIntField()


109


SetLongField()


110


SetFloatField()


111


SetDoubleField()


112

PARAMETERS:

env: JNI接口指针.

obj: JAVA对象(必须不为NULL).

fieldID: 有效的域ID.

value: 域的新值.

调用实例方法 Calling Instance Methods

GetMethodID

jmethodID GetMethodID(JNIEnv *env, jclass clazz,

const char *name, const char *sig);

返回类或接口的实例(非静态)方法的方法ID. 方法可以是被定义在类clazz的超类中,继承自clazz.方法是由它的名称和类型标示指定的.

GetMethodID() 引起一个未初始化的类初始化.

为了获取构造函数的方法ID, 提供<init> 作为方法名 void (V) 作为返回类型.

LINKAGE:

JNIEnv的接口函数表索引 33。

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类的对象.

name: 方法名,以0结尾的改进型 UTF-8 字符串.

sig: 方法标示,以0结尾的改进型 UTF-8 字符串.

RETURNS:

返回方法ID,如果指定的方法没有找到返回NULL .

THROWS:

NoSuchMethodError: 如果指定的方法没有找到.

ExceptionInInitializerError:如果类由于异常初始化失败.

OutOfMemoryError: 如果系统内存溢出.

Call<type>Method Routines, Call<type>MethodA Routines, Call<type>MethodV Routines

NativeType Call<type>Method(JNIEnv *env, jobject obj,

jmethodID methodID, ...);

NativeType Call<type>MethodA(JNIEnv *env, jobject obj,

jmethodID methodID, const jvalue *args);

NativeType Call<type>MethodV(JNIEnv *env, jobject obj,

jmethodID methodID, va_list args);

这三个家族的方法操作被用来从本地方法中JAVA实例方法.他们仅仅在传递参数给他们调用的方法的机制上不同.

这些操作族通过制定的方法ID调用JAVA对象的实例(非静态)方法,方法ID必须通过调用GetMethodID()获取.

当这些函数被用来调用私有方法和构造函数时,方法 ID必须从实际的类obj,而不是他的超类得到.

Call<type>Method Routines

程序员放置所有要传递给方法的参数在 methodID 参数后面. Call<type>Method 程序接收这些参数,并传递给程序员希望调用的方法.

Call<type>MethodA Routines

程序员放置所有要传递给方法的参数在一个jvalues类型的数组args中跟随在 methodID 参数后面. Call<type>MethodA ()接收这个数组中的所有参数, 并传递它们给程序员期望调用的方法.

Call<type>MethodV Routines

程序员放置所有要传递给方法的参数在一个va_list类型的参数args中跟随在 methodID 参数后面.Call<type>MethodV()接收这个参数, 并传递它们给程序员期望调用的方法.

下表通过他们的返回类型描述了每个方法调用程序.

你应该用你调用方法的JAVA类型来替换Call<type>Method 中的类型(或者使用表中的一个程序名称), 用那个程序相应的本地类型来代替本地类型.


实例方法调用程序


Call<type>Method 程序名称


本地类型


CallVoidMethod() CallVoidMethodA() CallVoidMethodV()


void


CallObjectMethod() CallObjectMethodA() CallObjectMethodV()


jobject


CallBooleanMethod() CallBooleanMethodA() CallBooleanMethodV()


jboolean


CallByteMethod() CallByteMethodA() CallByteMethodV()


jbyte


CallCharMethod() CallCharMethodA() CallCharMethodV()


jchar


CallShortMethod() CallShortMethodA() CallShortMethodV()


jshort


CallIntMethod() CallIntMethodA() CallIntMethodV()


jint


CallLongMethod() CallLongMethodA() CallLongMethodV()


jlong


CallFloatMethod() CallFloatMethodA() CallFloatMethodV()


jfloat


CallDoubleMethod() CallDoubleMethodA() CallDoubleMethodV()


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


实例方法调用程序


Call<type>Method Routine Name


Index


CallVoidMethod() CallVoidMethodA() CallVoidMethodV()


61,63,62


CallObjectMethod() CallObjectMethodA() CallObjectMethodV()


34,36,35


CallBooleanMethod() CallBooleanMethodA() CallBooleanMethodV()


37,39,38


CallByteMethod() CallByteMethodA() CallByteMethodV()


40,42,41


CallCharMethod() CallCharMethodA() CallCharMethodV()


43,45,44


CallShortMethod() CallShortMethodA() CallShortMethodV()


46,48,47


CallIntMethod() CallIntMethodA() CallIntMethodV()


49,51,50


CallLongMethod() CallLongMethodA() CallLongMethodV()


52,54,53


CallFloatMethod() CallFloatMethodA() CallFloatMethodV()


55,57,56


CallDoubleMethod() CallDoubleMethodA() CallDoubleMethodV()


58,60,59

PARAMETERS:

env: JNI接口指针.

obj: JAVA对象.

methodID: 方法ID.

Call<type>Method 程序的附加参数:

JAVA方法的参数.

Call<type>MethodA 程序的附加参数:

args: 数组参数.

Call<type>MethodV 程序的附加参数:

args: va_list类型的参数.

RETURNS:

返回调用方法的结果

THROWS:

JAVA方法执行过程中的异常.

CallNonvirtual<type>Method Routines, CallNonvirtual<type>MethodA Routines, CallNonvirtual<type>MethodV Routines

NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj,

jclass clazz, jmethodID methodID, ...);

NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj,

jclass clazz, jmethodID methodID, const jvalue *args);

NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj,

jclass clazz, jmethodID methodID, va_list args);

此操作族被用来通过指定的类和方法ID调用JAVA对象的实例(非静态)方法 .参数 methodID 通过在类clazz上调用GetMethodID()来获取.

CallNonvirtual<type>Method程序族和Call<type>Method程序族不同. Call<type>Method 程序调用基于类的对象的方法,而CallNonvirtual<type>Method调用基于类的方法, 该类通过 clazz 参数指定,方法ID也从它获取. 方法ID必须从对象的实际类或者它的超类获取.

CallNonvirtual<type>Method 程序

程序员放置所有要传递给方法的参数在 methodID 参数后面. CallNonvirtual<type>Method 程序接收这些参数,并传递给程序员希望调用的方法.

CallNonvirtual<type>MethodA 程序

程序员放置所有要传递给方法的参数在一个jvalues类型的数组args中跟随在 methodID 参数后面.CallNonvirtual<type>MethodA接收这个数组中的所有参数, 并传递它们给程序员期望调用的方法.

CallNonvirtual<type>MethodV 程序

程序员放置所有要传递给方法的参数在一个va_list类型的参数args中跟随在 methodID 参数后面.CallNonvirtual<type>MethodV接收这个参数, 并传递它们给程序员期望调用的方法.

下表通过他们的返回类型描述了每个方法调用程序.

你应该用你调用方法的JAVA类型来替换CallNonvirtual<type>Method中的类型(或者使用表中的一个程序名称), 用那个程序相应的本地类型来代替本地类型.


CallNonvirtual<type>Method 程序


CallNonvirtual<type>Method 程序名称


本地类型


CallNonvirtualVoidMethod() CallNonvirtualVoidMethodA() CallNonvirtualVoidMethodV()


void


CallNonvirtualObjectMethod() CallNonvirtualObjectMethodA() CallNonvirtualObjectMethodV()


jobject


CallNonvirtualBooleanMethod() CallNonvirtualBooleanMethodA() CallNonvirtualBooleanMethodV()


jboolean


CallNonvirtualByteMethod() CallNonvirtualByteMethodA() CallNonvirtualByteMethodV()


jbyte


CallNonvirtualCharMethod() CallNonvirtualCharMethodA() CallNonvirtualCharMethodV()


jchar


CallNonvirtualShortMethod() CallNonvirtualShortMethodA() CallNonvirtualShortMethodV()


jshort


CallNonvirtualIntMethod() CallNonvirtualIntMethodA() CallNonvirtualIntMethodV()


jint


CallNonvirtualLongMethod() CallNonvirtualLongMethodA() CallNonvirtualLongMethodV()


jlong


CallNonvirtualFloatMethod() CallNonvirtualFloatMethodA() CallNonvirtualFloatMethodV()


jfloat


CallNonvirtualDoubleMethod() CallNonvirtualDoubleMethodA() CallNonvirtualDoubleMethodV()


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


CallNonvirtual<type>Method 程序


CallNonvirtual<type>Method 程序名称


索引


CallNonvirtualVoidMethod() CallNonvirtualVoidMethodA() CallNonvirtualVoidMethodV()


91,93,92


CallNonvirtualObjectMethod() CallNonvirtualObjectMethodA() CallNonvirtualObjectMethodV()


64,66,65


CallNonvirtualBooleanMethod() CallNonvirtualBooleanMethodA() CallNonvirtualBooleanMethodV()


67,69,68


CallNonvirtualByteMethod() CallNonvirtualByteMethodA() CallNonvirtualByteMethodV()


70,72,71


CallNonvirtualCharMethod() CallNonvirtualCharMethodA() CallNonvirtualCharMethodV()


73,75,74


CallNonvirtualShortMethod() CallNonvirtualShortMethodA() CallNonvirtualShortMethodV()


76,78,77


CallNonvirtualIntMethod() CallNonvirtualIntMethodA() CallNonvirtualIntMethodV()


79,81,80


CallNonvirtualLongMethod() CallNonvirtualLongMethodA() CallNonvirtualLongMethodV()


82,84,83


CallNonvirtualFloatMethod() CallNonvirtualFloatMethodA() CallNonvirtualFloatMethodV()


85,87,86


CallNonvirtualDoubleMethod() CallNonvirtualDoubleMethodA() CallNonvirtualDoubleMethodV()


88,90,89

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类.

obj: JAVA对象.

methodID: 方法ID.

CallNonvirtual<type>Method 程序附加参数:

JAVA方法的参数.

CallNonvirtual<type>MethodA 程序附加参数:

args: 参数数组.

CallNonvirtual<type>MethodV 程序附加参数:

args: va_list类型的参数.

RETURNS:

返回调用JAVA方法的结果.

THROWS:

JAVA方法执行过程中的异常.

访问静态域 Accessing Static Fields

GetStaticFieldID

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,

const char *name, const char *sig);

返回类的静态域的域ID.域通过名称和标示指定. GetStatic<type>Field 和 SetStatic<type>Field 存取器函数族使用域ID来取回静态域.

GetStaticFieldID()引起未初始化的类初始化.

LINKAGE:

JNIEnv的接口函数表索引 144。

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类的对象.

name: 域名称,用0终止的改进型UTF-8字符串.

sig: 域标示,用0终止的改进型UTF-8字符串.

RETURNS:

返回域ID, 指定静态域未找到返回NULL.

THROWS:

NoSuchFieldError: 如果指定域未找到.

ExceptionInInitializerError: 如果异常导致类初始化失败.

OutOfMemoryError: 如果系统内存溢出.

GetStatic<type>Field Routines

NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz,

jfieldID fieldID);

存取器程序族返回对象的静态域值. 域通过调用GetStaticFieldID()获取的域ID来访问.

下表描述了get家族的程序名称和返回值类型.你应该用你调用方法的JAVA类型来替换GetStatic<type>Field中的类型(或者使用表中的一个程序名称), 用那个程序相应的本地类型来代替本地类型.


GetStatic<type>Field 存取器程序族


GetStatic<type>Field 程序名称


本地类型


GetStaticObjectField()


jobject


GetStaticBooleanField()


jboolean


GetStaticByteField()


jbyte


GetStaticCharField()


jchar


GetStaticShortField()


jshort


GetStaticIntField()


jint


GetStaticLongField()


jlong


GetStaticFloatField()


jfloat


GetStaticDoubleField()


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


GetStatic<type>Field 存取器程序族


GetStatic<type>Field 程序名


索引


GetStaticObjectField()


145


GetStaticBooleanField()


146


GetStaticByteField()


147


GetStaticCharField()


148


GetStaticShortField()


149


GetStaticIntField()


150


GetStaticLongField()


151


GetStaticFloatField()


152


GetStaticDoubleField()


153

PARAMETERS:

env: JNI接口指针.

clazz: JAVA对象.

fieldID: 静态域ID.

RETURNS:

返回静态域的内容.

SetStatic<type>Field Routines

void SetStatic<type>Field(JNIEnv *env, jclass clazz,

jfieldID fieldID, NativeType value);

存取器程序族设置对象的静态域值. 域通过调用GetStaticFieldID()获取的域ID来访问.

下表描述了set家族的程序名称和返回值类型.你应该用你调用方法的JAVA类型来替换SetStatic<type>Field中的类型(或者使用表中的一个程序名称), 用那个程序相应的本地类型来代替本地类型.


SetStatic<type>Field 存取器程序家族


SetStatic<type>Field 程序名称


本地类型


SetStaticObjectField()


jobject


SetStaticBooleanField()


jboolean


SetStaticByteField()


jbyte


SetStaticCharField()


jchar


SetStaticShortField()


jshort


SetStaticIntField()


jint


SetStaticLongField()


jlong


SetStaticFloatField()


jfloat


SetStaticDoubleField()


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


SetStatic<type>Field 存取器程序家族


SetStatic<type>Field 程序名称


索引


SetStaticObjectField()


154


SetStaticBooleanField()


155


SetStaticByteField()


156


SetStaticCharField()


157


SetStaticShortField()


158


SetStaticIntField()


159


SetStaticLongField()


160


SetStaticFloatField()


161


SetStaticDoubleField()


162

PARAMETERS:

env: JNI接口指针.

clazz: JAVA对象.

fieldID: 静态域ID.

value: 域的新值.

调用静态方法 Calling Static Methods

GetStaticMethodID

jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,

const char *name, const char *sig);

返回类的静态方法的方法ID. 方法ID由名称和标示指定.

GetStaticMethodID()引起未初始化的类初始化.

LINKAGE:

JNIEnv的接口函数表索引113.

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类的对象.

name: 静态方法名称,用0终止的改进型UTF-8字符串.

sig: 方法标示,用0终止的改进型UTF-8字符串.

RETURNS:

返回方法ID,操作失败返回NULL.

THROWS:

NoSuchMethodError: 如果指定静态方法未找到.

ExceptionInInitializerError: 由于异常导致类初始化失败.

OutOfMemoryError: 系统内存溢出.

CallStatic<type>Method Routines, CallStatic<type>MethodA Routines, CallStatic<type>MethodV Routines

NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz,

jmethodID methodID, ...);

NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz,

jmethodID methodID, jvalue *args);

NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz,

jmethodID methodID, va_list args);

此操作族通过指定的方法ID调用对象的静态方法.methodID 参数必须通过调用GetStaticMethodID()获得.

方法ID必须从clazz, 而不是他的超类获得.

CallStatic<type>Method 程序

程序员放置所有要传递给方法的参数在 methodID 参数后面. CallStatic<type>Method 程序接收这些参数,并传递给程序员希望调用的方法.

CallStatic<type>MethodA 程序

程序员放置所有要传递给方法的参数在一个jvalues类型的数组args中跟随在 methodID 参数后面.CallStatic<type>MethodA接收这个数组中的所有参数, 并传递它们给程序员期望调用的方法.

CallStatic<type>MethodV 程序

程序员放置所有要传递给方法的参数在一个va_list类型的参数args中跟随在 methodID 参数后面.CallStatic<type>MethodV接收这个参数, 并传递它们给程序员期望调用的方法.

下表通过他们的返回类型描述了每个方法调用程序.

你应该用你调用方法的JAVA类型来替换CallStatic<type>Method中的类型(或者使用表中的一个程序名称), 用那个程序相应的本地类型来代替本地类型.


CallStatic<type>Method调用程序


CallStatic<type>Method 程序名称


本地类型


CallStaticVoidMethod() CallStaticVoidMethodA() CallStaticVoidMethodV()


void


CallStaticObjectMethod() CallStaticObjectMethodA() CallStaticObjectMethodV()


jobject


CallStaticBooleanMethod() CallStaticBooleanMethodA() CallStaticBooleanMethodV()


jboolean


CallStaticByteMethod() CallStaticByteMethodA() CallStaticByteMethodV()


jbyte


CallStaticCharMethod() CallStaticCharMethodA() CallStaticCharMethodV()


jchar


CallStaticShortMethod() CallStaticShortMethodA() CallStaticShortMethodV()


jshort


CallStaticIntMethod() CallStaticIntMethodA() CallStaticIntMethodV()


jint


CallStaticLongMethod() CallStaticLongMethodA() CallStaticLongMethodV()


jlong


CallStaticFloatMethod() CallStaticFloatMethodA() CallStaticFloatMethodV()


jfloat


CallStaticDoubleMethod() CallStaticDoubleMethodA() CallStaticDoubleMethodV()


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


CallStatic<type>Method 调用程序


CallStatic<type>Method 程序名称


索引


CallStaticVoidMethod() CallStaticVoidMethodA() CallStaticVoidMethodV()


141,143

142


CallStaticObjectMethod() CallStaticObjectMethodA() CallStaticObjectMethodV()


114,116

115


CallStaticBooleanMethod() CallStaticBooleanMethodA() CallStaticBooleanMethodV()


117,119

118


CallStaticByteMethod() CallStaticByteMethodA() CallStaticByteMethodV()


120,122

121


CallStaticCharMethod() CallStaticCharMethodA() CallStaticCharMethodV()


123,125

124


CallStaticShortMethod() CallStaticShortMethodA() CallStaticShortMethodV()


126,128

127


CallStaticIntMethod() CallStaticIntMethodA() CallStaticIntMethodV()


129,131

130


CallStaticLongMethod() CallStaticLongMethodA() CallStaticLongMethodV()


132,134

133


CallStaticFloatMethod() CallStaticFloatMethodA() CallStaticFloatMethodV()


135,137

136


CallStaticDoubleMethod() CallStaticDoubleMethodA() CallStaticDoubleMethodV()


138,140

139

PARAMETERS:

env: JNI接口指针.

clazz: JAVA对象.

methodID: 静态方法ID.

Additional Parameter for CallStatic<type>Method 程序附加参数:

JAVA方法的参数.

Additional Parameter for CallStatic<type>MethodA 程序附加参数:

args: 参数数组.

Additional Parameter for CallStatic<type>MethodV 程序附加参数:

args: va_list类型的参数.

RETURNS:

返回调用静态JAVA方法的结果.

THROWS:

JAVA方法执行期间引起的异常.

字符串操作 String Operations

NewString

jstring NewString(JNIEnv *env, const jchar *unicodeChars,

jsize len);

从Unicode 字符数组构建一个新的 java.lang.String 对象.

LINKAGE:

JNIEnv的接口函数表索引 163.

PARAMETERS:

env: JNI接口指针.

unicodeChars: 指向Unicode 字符串的指针.

len: Unicode 字符串的长度.

RETURNS:

返回一个JAVA字符串队形,如果字符串不能被创建返回NULL.

THROWS:

OutOfMemoryError: 系统内存溢出.

GetStringLength

jsize GetStringLength(JNIEnv *env, jstring string);

返回JAVA字符串的长度 ( Unicode字符的数量).

LINKAGE:

JNIEnv的接口函数表索引 164.

PARAMETERS:

env: JNI接口指针.

string: JAVA字符串对象.

RETURNS:

返回JAVA字符串的长度.

GetStringChars

const jchar * GetStringChars(JNIEnv *env, jstring string,

jboolean *isCopy);

返回指向字符串的Unicode字符数组的指针. 该指针持续有效直到调用 ReleaseStringChars().

如果isCopy 不为NULL, 那么进行复制*isCopy设置为JNI_TRUE 或者不进行复制设置为JNI_FALSE .

LINKAGE:

JNIEnv的接口函数表索引 165.

PARAMETERS:

env: JNI接口函数.

string: JAVA字符串对象.

isCopy: 指向布尔值的指针.

RETURNS:

返回指向Unicode字符串的指针,操作失败返回NULL.

ReleaseStringChars

void ReleaseStringChars(JNIEnv *env, jstring string,

const jchar *chars);

通知虚拟机本地代码不再需要访问chars. 参数 chars 是通过GetStringChars()从string获取的指针.

LINKAGE:

JNIEnv的接口函数表索引 166.

PARAMETERS:

env: JNI接口函数.

string: JAVA字符串对象.

chars: 指向Unicode字符串的指针.

NewStringUTF

jstring NewStringUTF(JNIEnv *env, const char *bytes);

从一个改进型UTF-8编码的字符数组中构建一个新的java.lang.String 对象.

LINKAGE:

JNIEnv的接口函数表索引 167.

PARAMETERS:

env: JNI接口函数.

bytes: 指向改进型UTF-8 字符串的指针.

RETURNS:

返回JAVA字符串类型的对象,字符串不能创建返回NULL.

THROWS:

OutOfMemoryError: 如果系统内存溢出.

GetStringUTFLength

jsize GetStringUTFLength(JNIEnv *env, jstring string);

返回表征一个字符串的改进型 UTF-8 字节组的长度.

LINKAGE:

JNIEnv的接口函数表索引 168.

PARAMETERS:

env: JNI接口函数.

string: JAVA字符串对象.

RETURNS:

返回字符串的UTF-8长度.

GetStringUTFChars

const char * GetStringUTFChars(JNIEnv *env, jstring string,

jboolean *isCopy);

返回指向表征字符串的用改进型UTF-8编码的字节数组指针,数组持续有效直到通过 ReleaseStringUTFChars()释放.

如果isCopy 不为NULL, 那么进行复制*isCopy设置为JNI_TRUE 或者不进行复制设置为JNI_FALSE .

LINKAGE:

JNIEnv的接口函数表索引 169.

PARAMETERS:

env: JNI接口函数.

string: JAVA字符串对象.

isCopy: 指向布尔值的指针.

RETURNS:

返回指向改进型UTF-8字符串的指针, 操作失败返回NULL.

ReleaseStringUTFChars

void ReleaseStringUTFChars(JNIEnv *env, jstring string,

const char *utf);

通知虚拟机本地代码不再需要访问utf. 参数 utf是通过GetStringUTFChars()从string获取的指针.

LINKAGE:

JNIEnv的接口函数表索引 170.

PARAMETERS:

env: JNI接口函数.

string: JAVA字符串对象.

utf: 指向改进型UTF-8 字符串的指针.

注意: 在 JDK/JRE 1.1, 程序员能够在用户提供的缓冲区得到原始数组元素. 自 JDK/JRE 1.2 起,提供了额外的函数集允许本地代码从用户提供的缓冲区获取用Unicode(UTF-16)或者改进型UTF-8编码的字符 . 请看下面的函数.

GetStringRegion

void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);

复制len 长度的Unicode 字符以start为偏移量到给定的缓冲区buf.

Throws StringIndexOutOfBoundsException 索引值溢出.

LINKAGE:

JNIEnv的接口函数表索引 220.

SINCE:

JDK/JRE 1.2

GetStringUTFRegion

void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);

以start为偏移量转换len长度的Unicode字符为改进型UTF-8编码,并放置结果到给定的缓冲区buf.

Throws StringIndexOutOfBoundsException 索引溢出.

LINKAGE:

JNIEnv的接口函数表索引 221.

SINCE:

JDK/JRE 1.2

GetStringCritical, ReleaseStringCritical

const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);

void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);

这两个函数在语意上和 Get/ReleaseStringChars 函数相同. 如果可能,虚拟机返回一个指向字符串的指针; 否则, 进行复制操作. 然而,在怎样使用这些函数时有一些重要的限制. 在Get/ReleaseStringCritical调用封闭的代码段中, 本地代码不能流出任意的JNI调用,也不能引起当前当前线程阻塞.

Get/ReleaseStringCritical和 Get/ReleasePrimitiveArrayCritical上的限制相同.

LINKAGE (GetStringCritical):

JNIEnv的接口函数表索引 224.

LINKAGE (ReleaseStingCritical):

JNIEnv的接口函数表索引 225.

SINCE:

JDK/JRE 1.2

数组操作 Array Operations

GetArrayLength

jsize GetArrayLength(JNIEnv *env, jarray array);

返回数组中元素的数量.

LINKAGE:

JNIEnv的接口函数表索引 171.

PARAMETERS:

env: JNI接口指针.

array: JAVA数组对象.

RETURNS:

返回数组的长度

NewObjectArray

jobjectArray NewObjectArray(JNIEnv *env, jsize length,

jclass elementClass, jobject initialElement);

创建一个新的持有elementClass类的数组对象. 所有元素初始化为initialElement.

LINKAGE:

JNIEnv的接口函数表索引 172.

PARAMETERS:

env: JNI接口指针.

length: 数组大小.

elementClass: 数组元素类.

initialElement: 初始化值.

RETURNS:

返回JAVA数组对象, 如果数组不能被创建返回NULL .

THROWS:

OutOfMemoryError: 如果系统内存溢出.

GetObjectArrayElement

jobject GetObjectArrayElement(JNIEnv *env,

jobjectArray array, jsize index);

返回数组 Object 的一个元素.

LINKAGE:

JNIEnv的接口函数表索引 173.

PARAMETERS:

env: JNI接口之后怎.

array: JAVA数组.

index: 数组索引.

RETURNS:

返回JAVA对象.

THROWS:

ArrayIndexOutOfBoundsException:如果不是在数组中的有效索引.

SetObjectArrayElement

void SetObjectArrayElement(JNIEnv *env, jobjectArray array,

jsize index, jobject value);

设置 Object 数组中的一个元素.

LINKAGE:

JNIEnv的接口函数表索引 174.

PARAMETERS:

env: JNI接口指针.

array:JAVA数组.

index: 数组索引.

value: 新值.

THROWS:

ArrayIndexOutOfBoundsException: 索引不是一个数组的有效索引.

ArrayStoreException: 如果值的类不是数组元素的类的子类.

New<PrimitiveType>Array Routines

ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);

此操作族用来构造一个新的原始数组对象. 下表描述了特定的原始数组构造函数. 你需要用一个实际的原始数组构造器程序名称来替换New<PrimitiveType>, 并且用与那个程序相对应的数组类型来替换 ArrayType.


New<PrimitiveType>Array 数组构造器家族


New<PrimitiveType>Array 程序


数组类型


NewBooleanArray()


jbooleanArray


NewByteArray()


jbyteArray


NewCharArray()


jcharArray


NewShortArray()


jshortArray


NewIntArray()


jintArray


NewLongArray()


jlongArray


NewFloatArray()


jfloatArray


NewDoubleArray()


jdoubleArray

LINKAGE:

JNIEnv的接口函数表索引 :


New<PrimitiveType>Array 数组构造器家族


New<PrimitiveType>Array 程序名称


索引


NewBooleanArray()


175


NewByteArray()


176


NewCharArray()


177


NewShortArray()


178


NewIntArray()


179


NewLongArray()


180


NewFloatArray()


181


NewDoubleArray()


182

PARAMETERS:

env: JNI接口函数.

length: 数组长度.

RETURNS:

返回JAVA数组, 数组没能构造返回NULL.

Get<PrimitiveType>ArrayElements Routines

NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env,ArrayType array, jboolean *isCopy);

该函数族返回原始数组的本体.其结果一直有效直到相一致的Release<PrimitiveType>ArrayElements() 函数调用. 因为返回的数组可能是JAVA数组的复制品, 对返回数组的改变直到调用Release<PrimitiveType>ArrayElements() 才必然的体现到原始数组中.

如果isCopy 不为NULL, 那么进行复制*isCopy设置为JNI_TRUE 或者不进行复制设置为JNI_FALSE .

下表描述了详细的原始数组元素访问函数. 需要做如下的置换:

用下表一个明确的原始元素访问函数替换Get<PrimitiveType>ArrayElements.

用相应的数组类型替换 ArrayType .

用相应的程序本地类型替换NativeType .

不论布尔数组在JAVA虚拟机中怎么描述, GetBooleanArrayElements()总是返回一个指向jbooleans的指针, 每个字节指示一个元素 (未压缩的标示方法).所有其他类型的数组必须保证在内存中连续.


Get<PrimitiveType>ArrayElements 访问函数家族


Get<PrimitiveType>ArrayElements 程序名


数组类型


本地类型


GetBooleanArrayElements()


jbooleanArray


jboolean


GetByteArrayElements()


jbyteArray


jbyte


GetCharArrayElements()


jcharArray


jchar


GetShortArrayElements()


jshortArray


jshort


GetIntArrayElements()


jintArray


jint


GetLongArrayElements()


jlongArray


jlong


GetFloatArrayElements()


jfloatArray


jfloat


GetDoubleArrayElements()


jdoubleArray


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


Get<PrimitiveType>ArrayElements Family of Accessor Routines


Get<PrimitiveType>ArrayElements Routines


Index


GetBooleanArrayElements()


183


GetByteArrayElements()


184


GetCharArrayElements()


185


GetShortArrayElements()


186


GetIntArrayElements()


187


GetLongArrayElements()


188


GetFloatArrayElements()


189


GetDoubleArrayElements()


190

PARAMETERS:

env: JNI接口指针.

array: JAVA字符串数组.

isCopy: 指向布尔类型的指针.

RETURNS:

返回指向数组元素的指针, 操作失败返回NULL.

Release<PrimitiveType>ArrayElements Routines

void Release<PrimitiveType>ArrayElements(JNIEnv *env,ArrayType array, NativeType *elems, jint mode);

此函数家族通知虚拟机本地代码不再需要访问elems.参数elems是使用相对应的函数Get<PrimitiveType>ArrayElements() 得到的的指针.如果必要的话,函数将把对原始数组elems所做的变化拷贝回来.

参数mode提供了数组缓冲区怎么释放的信息.如果elems不是array中的一个元素的拷贝,mode将不起作用.否则mode有如下表的影响.


原始数组释放模式


mode(模式)


actions(行动)


0


将内容拷贝回来并释放 elems 缓冲区


JNI_COMMIT


将内容拷贝回来但不释放 elems 缓冲区


JNI_ABORT


释放 elems 缓冲区但不拷贝回来可能的变化

大多数情况下,程序员传递”0”给mode参数来确保固定的和复制的数组的一致性.其他的选项给程序员在存储管理上更多的控制,使用时要特别注意.

下表描述了构成原始数组清除器的详细程序.你需要做如下的替换:

用下表一种实际原始数组清除程序替换Release<PrimitiveType>ArrayElements.

用对应的数组类型替换ArrayType.

用程序对应的本地类型来替换NativeType.


Release<PrimitiveType>ArrayElements 数组程序家族


Release<PrimitiveType>ArrayElements 程序


数组类型


本地类型


ReleaseBooleanArrayElements()


jbooleanArray


jboolean


ReleaseByteArrayElements()


jbyteArray


jbyte


ReleaseCharArrayElements()


jcharArray


jchar


ReleaseShortArrayElements()


jshortArray


jshort


ReleaseIntArrayElements()


jintArray


jint


ReleaseLongArrayElements()


jlongArray


jlong


ReleaseFloatArrayElements()


jfloatArray


jfloat


ReleaseDoubleArrayElements()


jdoubleArray


jdouble

LINKAGE:

JNIEnv的接口函数表索引:


Release<PrimitiveType>ArrayElements 数组程序家族


Release<PrimitiveType>ArrayElements 程序


索引


ReleaseBooleanArrayElements()


191


ReleaseByteArrayElements()


192


ReleaseCharArrayElements()


193


ReleaseShortArrayElements()


194


ReleaseIntArrayElements()


195


ReleaseLongArrayElements()


196


ReleaseFloatArrayElements()


197


ReleaseDoubleArrayElements()


198

PARAMETERS:

env: JNI接口指针.

array: JAVA对象.

elems:指向数组元素的指针.

mode: 释放的模式.

Get<PrimitiveType>ArrayRegion 程序

void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,

jsize start, jsize len, NativeType *buf);

此函数族拷贝原始数组的一个范围到缓冲区.下表描述了详细的原始数组访问器,你需要做如下的替换:

用表中实际的原始数组访问程序替换Get<PrimitiveType>ArrayRegion.

用对应的数组类型替换ArrayType.

用此程序对应的本地类型替换NativeType.


Get<PrimitiveType>ArrayRegion 数组访问程序家族


Get<PrimitiveType>ArrayRegion 程序


数组类型


本地类型


GetBooleanArrayRegion()


jbooleanArray


jboolean


GetByteArrayRegion()


jbyteArray


jbyte


GetCharArrayRegion()


jcharArray


jchar


GetShortArrayRegion()


jshortArray


jhort


GetIntArrayRegion()


jintArray


jint


GetLongArrayRegion()


jlongArray


jlong


GetFloatArrayRegion()


jfloatArray


jloat


GetDoubleArrayRegion()


jdoubleArray


jdouble

LINKAGE:

JNIEnv的接口函数表索引:

Get<PrimitiveType>ArrayRegion 数组访问程序家族


Get<PrimitiveType>ArrayRegion 程序


索引


GetBooleanArrayRegion()


199


GetByteArrayRegion()


200


GetCharArrayRegion()


201


GetShortArrayRegion()


202


GetIntArrayRegion()


203


GetLongArrayRegion()


204


GetFloatArrayRegion()


205


GetDoubleArrayRegion()


206

PARAMETERS:

env: JNI接口指针.

array: JAVA数组.

start: 开始的索引.

len: 要拷贝的元素数量.

buf: 目的缓冲区.

THROWS:

ArrayIndexOutOfBoundsException: 如果一个数组中的索引无效.

Set<PrimitiveType>ArrayRegion Routines

void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,

jsize start, jsize len, const NativeType *buf);

此函数族从缓冲区中拷贝一个原始数组的区域回来.

下表描述了详细的原始数组元素访问程序.你需要做如下的替换:

用下表一种实际的原始元素访问程序替换Set<PrimitiveType>ArrayRegion.

用对应的数组类型替换ArrayType.

用此程序对应的本地类型替换NativeType.

Set<PrimitiveType>ArrayRegion 数组访问程序家族


Set<PrimitiveType>ArrayRegion 程序


数组类型


本地类型


SetBooleanArrayRegion()


jbooleanArray


jboolean


SetByteArrayRegion()


jbyteArray


jbyte


SetCharArrayRegion()


jcharArray


jchar


SetShortArrayRegion()


jshortArray


jshort


SetIntArrayRegion()


jintArray


jint


SetLongArrayRegion()


jlongArray


jlong


SetFloatArrayRegion()


jfloatArray


jfloat


SetDoubleArrayRegion()


jdoubleArray


jdouble

LINKAGE:

JNIEnv的接口函数表索引

Set<PrimitiveType>ArrayRegion 数组访问程序家族


Set<PrimitiveType>ArrayRegion 程序


索引


SetBooleanArrayRegion()


207


SetByteArrayRegion()


208


SetCharArrayRegion()


209


SetShortArrayRegion()


210


SetIntArrayRegion()


211


SetLongArrayRegion()


212


SetFloatArrayRegion()


213


SetDoubleArrayRegion()


214

PARAMETERS:

env: JNI接口指针.

array: JAVA数组.

start: 开始索引.

len: 要拷贝的元素数目.

buf: 源缓冲区.

THROWS:

ArrayIndexOutOfBoundsException: 索引中的一个在区域中无效.


注意: 自 JDK/JRE 1.1起, 程序员能够使用函数 Get/Release<primitivetype>ArrayElements来获取一个指向原始数组元素的指针. 如果虚拟机支持定位,返回指向原始数据的指针;否则进行拷贝.

自JDK/JRE 1.3 起的新函数允许本地代码获取一个直接指向数组元素的指针,即使虚拟机不支持定位.

GetPrimitiveArrayCritical, ReleasePrimitiveArrayCritical

void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);

void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);

在语意上这两个函数和Get/Release<primitivetype>ArrayElements函数相似,如果可能的话,虚拟机返回一个指向原始数组的指针,否则,进行拷贝.然而,在这些函数的使用上有重要的限制.

调用GetPrimitiveArrayCritical之后, 本地代码不能在调用函数ReleasePrimitiveArrayCritical之前运行一段时间.我们必须把这一对函数中的代码作为运行在"关键区域"对待. 在关键区域里面, 本地代码必须不能调用可能引起当前线程阻塞而等待别的线程的其他的JNI函数和任何系统调用. (例如,当前线程不能调用读从其他线程写的流.)

这些限制致使本地代码将会获取数组的一个非拷贝版本,即使虚拟机不支持定位.例如,当本地代码持有一个指向通过 GetPrimitiveArrayCritical获取的数组的指针,虚拟机可能会临时的禁止垃圾回收.

多对GetPrimtiveArrayCritical 和ReleasePrimitiveArrayCritical可能会嵌套. 例如:

jint len = (*env)->GetArrayLength(env, arr1);

jbyte *a1 = (*env)->GetPrimitiveArrayCritical(env, arr1, 0);

jbyte *a2 = (*env)->GetPrimitiveArrayCritical(env, arr2, 0);

/* We need to check in case the VM tried to make a copy. */

if (a1 == NULL || a2 == NULL) {

... /* out of memory exception thrown */

}

memcpy(a1, a2, len);

(*env)->ReleasePrimitiveArrayCritical(env, arr2, a2, 0);

(*env)->ReleasePrimitiveArrayCritical(env, arr1, a1, 0);

注意,如果虚拟机内部用不同的格式表示数组,GetPrimitiveArrayCritical 可能仍然会进行数组的拷贝. 因此,我们为了确定内存溢出情况需要检查它的返回值是否为NULL.

LINKAGE (GetPrimitiveArrayCritical):

JNIEnv的接口函数表索引 222.

LINKAGE (ReleasePrimitiveArrayCritical):

JNIEnv的接口函数表索引 223.

SINCE:

JDK/JRE 1.2

注册本地方法 Registering Native Methods

RegisterNatives

jint RegisterNatives(JNIEnv *env, jclass clazz,

const JNINativeMethod *methods, jint nMethods);

用参数clazz指定的类来注册本地方法.参数methods指定了一个JNINativeMethod类型的包含本地方法名称,标示,函数指针的结构体.

name和signature 域是JNINativeMethod 结构体中指向改进型 modified UTF-8字符串的指针.nMethods 参数指定了数组中的本地方法的编号. JNINativeMethod 结构体定义如下:

typedef struct {

char *name;

char *signature;

void *fnPtr;

} JNINativeMethod;

函数指针名义上必须有下面的标示:

ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...);

LINKAGE:

JNIEnv的接口函数表索引 215.

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类对象.

methods: 类中的本地方法.

nMethods: 类中本地方法的编号.

RETURNS:

成功返回“0” ; 失败返回负值.

THROWS:

NoSuchMethodError: 如果指定的方法没有找到或者不是本地方法.

UnregisterNatives

jint UnregisterNatives(JNIEnv *env, jclass clazz);

取消注册类中的本地方法. 类回到连接和注册本地方法函数之前的状态.

这个函数在正常的本地代码不要使用. 但它为特殊的程序提供了一种重新连接,重新加载本地库的方法.

LINKAGE:

JNIEnv的接口函数表索引 216.

PARAMETERS:

env: JNI接口指针.

clazz: JAVA类对象.

RETURNS:

成功返回“0”; 失败返回负值.

监视程序操作 Monitor Operations

MonitorEnter

jint MonitorEnter(JNIEnv *env, jobject obj);

进入obj指定的相关的JAVA对象的监视程序.参数obj必须不能为NULL.

每个JAVA都有对应的监视程序. 如果当前线程已经拥有obj关联的监视程序,增加指示当前线程进入监视程序次数的计数器. 如果obj关联的监视程序不被任何线程拥有,当前线程将成为监视程序的拥有者,并设置监视器的进入计数器为1. 如果其他的线程已经拥有了obj关联的监视程序,当前线程将会等待直到监视程序被释放,并再次尝试获取所有权.

通过JNI函数MonitorEnter 进入的监视程序不能够使用JAVA虚拟机指令monitorexit 和同步方法返回来退出. MonitorEnter JNI函数调用和JAVA虚拟机指令monitorenter 可能进入相同对象的关联监视函数.

为了避免死锁,通过MonitorEnter JNI 函数调用进入的监视函数必须使用MonitorExit JNI 调用退出, 除非DetachCurrentThread 调用已经暗中调用释放了JNI 监视程序.

LINKAGE:

JNIEnv的接口函数表索引 217.

PARAMETERS:

env: JNI接口指针.

obj:一个常规的JAVA对象或者类对象.

RETURNS:

成功返回“0”; 失败返回负值.

MonitorExit

jint MonitorExit(JNIEnv *env, jobject obj);

当前线程必须是 obj指向的JAVA对象相关联的监视程序的拥有者. 线程减少指示进入监视程序次数的计数器,如果计数器的值为0,当前线程释放监视程序.

本地代码不能使用 MonitorExit 来退出通过同步方法和JAVA虚拟机指令monitorenter 进入的监视程序.

LINKAGE:

JNIEnv的接口函数表索引 218.

PARAMETERS:

env: JNI接口指针.

obj: 常规的JAVA对象或者类对象.

RETURNS:

成功返回“0”; 失败返回负值.

EXCEPTIONS:

IllegalMonitorStateException:如果当前线程不拥有监视程序.

网络接口对象支持 NIO Support

网络接口相关的切入点允许本地代码访问java.nio 直接缓冲区.直接缓冲区的内容能够潜在地驻留在普通垃圾回收堆之外的本地存储区.更多关于直接缓冲区的内容,请看 New I/O APIs 和 java.nio.ByteBuffer 类的规格书.

JDK/JRE 1.4 中的三个新函数允许JNI代码创建,检测,操作直接缓冲区:

· NewDirectByteBuffer

· GetDirectBufferAddress

· GetDirectBufferCapacity

每个JAVA虚拟机的实现都必须支持这些函数,但不是每个实现都要求能够支持JNI访问直接缓冲区. 如果一个JAVA虚拟机不支持这样的访问,那么函数NewDirectByteBuffer 和 GetDirectBufferAddress 必须总是返回NULL, GetDirectBufferCapacity 函数必须总是返回-1. 如果JAVA虚拟机支持这些访问,那么这三个函数被实现地返回合适的值.

NewDirectByteBuffer

jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);

分配和返回一个直接的 java.nio.ByteBuffer 指向开始于存储器地址 address 并扩展 capacity 字节的存储区域.

调用这个函数并返回位缓冲对象给JAVA层代码的本地代码应该确保这个指向有效存储区域的缓冲区可读,在适当的时候可写.从JAVA代码访问一个无效的存储位置要么返回任意值而没有可见的影响,要么引起一个未指明的异常抛出.

LINKAGE:

JNIEnv的接口函数表索引 229.

PARAMETERS:

env: JNIEnv 接口指针

address: 存储区域开始地址 (必须不为 NULL)

capacity: 存储区域字节大小(必须为正数)

RETURNS:

返回新的 java.nio.ByteBuffer实例化对象的局部引用. 如果异常发生,或者这个虚拟机不支持JNI访问直接缓冲区返回NULL.

EXCEPTIONS:

OutOfMemoryError: 如果分配 ByteBuffer 对象失败

SINCE:

JDK/JRE 1.4

GetDirectBufferAddress

void* GetDirectBufferAddress(JNIEnv* env, jobject buf);

获取和返回java.nio.Buffer指定的直接存储区域的开始地址 .

这个函数允许本地代码访问通过JAVA代码中缓冲区对象可以访问的相同存储区域.

LINKAGE:

JNIEnv的接口函数表索引 230.

PARAMETERS:

env: JNIEnv 接口指针

buf: 直接java.nio.Buffer 对象(必须不为NULL)

RETURNS:

返回缓冲区指定的直接存储区域的开始地址,如果存储区域未定义、给定的对象不是一个直接的java.nio.Buffer、这个虚拟机不支持JNI访问直接缓冲区返回NULL.

SINCE:

JDK/JRE 1.4

GetDirectBufferCapacity

jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf);

获取和返回给定的直接 java.nio.Buffer指向的存储区域的容量. 容量是存储区域包含的元素数量.

LINKAGE:

JNIEnv的接口函数表索引 231.

PARAMETERS:

env:JNIEnv 接口指针

buf: 直接的java.nio.Buffer 对象(必须不为 NULL)

RETURNS:

返回缓冲区相关的存储区域的容量.如果给定的对象不是一个直接java.nio.Buffer,对象是一个为对齐的视图缓冲区而处理器结构不支持未对齐访问,这个虚拟机不支持JNI访问直接缓冲区返回-1.

SINCE:

JDK/JRE 1.4

反射支持 Reflection Support

程序员如果知道方法和域的名称和类型,就能够使用JNI来调用JAVA方法和访问JAVA域. JAVA核心反射API允许程序员在运行时内省JAVA类.JNI提供了一系列在JNI中使用的方法和域ID和JAVA核心反射API中使用的域和方法对象之间的转换函数.

FromReflectedMethod

jmethodID FromReflectedMethod(JNIEnv *env, jobject method);

转换一个java.lang.reflect.Method 或者java.lang.reflect.Constructor 对象为方法ID.

LINKAGE:

JNIEnv的接口函数表索引 7.

SINCE:

JDK/JRE 1.2

FromReflectedField

jfieldID FromReflectedField(JNIEnv *env, jobject field);

转换一个java.lang.reflect.Field 到域ID.

LINKAGE:

JNIEnv的接口函数表索引 8.

SINCE:

JDK/JRE 1.2

ToReflectedMethod

jobject ToReflectedMethod(JNIEnv *env, jclass cls,

jmethodID methodID, jboolean isStatic);

转换从cls到java.lang.reflect.Method 或java.lang.reflect.Constructor对象推导出的方法ID, 如果方法ID指向静态域,isStatic 必须设置为 JNI_TRUE ,否则设置为JNI_FALSE .

失败抛出OutOfMemoryError 返回0.

LINKAGE:

JNIEnv的接口函数表索引 9.

SINCE:

JDK/JRE 1.2

ToReflectedField

jobject ToReflectedField(JNIEnv *env, jclass cls,

jfieldID fieldID, jboolean isStatic);

转换一个从cls到java.lang.reflect.Field 对象推导出的域ID, 如果fieldID指向静态域,isStatic 必须设置为 JNI_TRUE ,否则设置为JNI_FALSE .

失败抛出 OutOfMemoryError返回 0 .

LINKAGE:

JNIEnv的接口函数表索引 12.

SINCE:

JDK/JRE 1.2

JAVA虚拟机接口Java VM Interface

GetJavaVM

jint GetJavaVM(JNIEnv *env, JavaVM **vm);

返回当前线程相关联的JAVA虚拟机接口(在调用API中使用). 结果放在第二个参数vm指向的地址中.

LINKAGE:

JNIEnv的接口函数表索引 219.

PARAMETERS:

env: JNI接口指针.

vm: 指向需要替换的结果的指针.

RETURNS:

成功返回“0” ; 失败返回负数.

第五章 调用API

调用API允许软件供应商加载JAVA虚拟机到任意的本地应用,供应商不必连接到虚拟机的源代码而实现JAVA可行的应用。

本章开头概述了调用API,随后是所有调用API函数的参考资料。

下面的代码举例说明了如何使用调用API的函数,这个例子中,C++代码创建了一个JAVA虚拟机并调用了一个叫做Main.test的静态方法,为了更加清晰,删去了错误检测部分.

#include <jni.h>       /* 所有的都定义在这里 */

...

JavaVM *jvm;       /* 表示一个JAVA虚拟机 */

JNIEnv *env;       /* 指向本地方法接口的指针 */

JavaVMInitArgs vm_args; /* JDK/JRE 6 虚拟机初始化参数 */

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;

/* 加载和初始化JAVA虚拟机, 在env中返回一个JNI接口指针*/

JNI_CreateJavaVM(&jvm, &env, &vm_args);

delete options;

/* 用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接口指针访问虚拟机特性。这个设计和网景公司的JRI嵌入式接口类似。

创建虚拟机

JNI_CreateJavaVM()函数加载和初始化JAVA虚拟机并返回执行JNI接口指针的指针。调用JNI_CreateJavaVM()的线程被认为是主线程。

连接虚拟机

JNI接口指针(JNIEnv)只在当前线程有效。另外的线程需要访问虚拟机,必须首先调用 AttachCurrentThread()来连接自己到虚拟机并获取JNI接口指针。一旦连接到虚拟机,在本地方法中,本地线程就像一个普通的JAVA线程一样工作。本地线程将会保持与虚拟机的连接状态直到调用DetachCurrentThread() 来分开自己。

被连接的线程必须有足够的栈空间来实现适当数量的工作。每个线程的栈空间的分配是系统指定的。例如,使用pthreads,栈空间的大小能够在pthread_create的参数pthread_attr_t 中指定。

从虚拟机分离

本地线程必须调用 DetachCurrentThread()来实现在退出之前与虚拟机的分离。如果有JAVA方法在调用栈里,线程将不能和虚拟机分离。

卸载虚拟机

JNI_DestroyJavaVM()函数卸载JAVA虚拟机。从 JDK/JRE 1.1 开始,只有主线程能够通过调用JNI_DestroyJavaVM()卸载虚拟机,从 JDK/JRE 1.2 开始,这个限制被删除了,任何的线程能够通过JNI_DestroyJavaVM()卸载虚拟机。

虚拟机将会等待直到当前线程是卸载之前唯一的非守护用户线程。用户线程包括JAVA线程和连接的本地线程。这个限制会存在是因为JAVA线程或者连接的本地线程可能会持有系统的资源,例如锁,窗口等。虚拟机不能自动地释放这些资源。通过限制当前线程是虚拟机卸载时唯一的运行线程,释放任意线程持有的系统资源将由程序员实现。

库和版本管理

从JDK/JRE 1.1,一旦加载本地库,将对所有的类加载器可见。因此两个不同的类加载器的类可能链接到相同的本地方法。这导致了两个问题:

· 一个类可能错误地连接到不同的类加载器通过相同名字加载的本地库。

· 本地方法能够轻松地混合从不同类加载器加载的类,这破坏了类加载器提供的名字空间分隔机制,造成了类型安全问题。

从JDK/JRE 1.1,每个类加载器管理它自己的本地库集合,相同的JNI本地库不能被加载进超过一个类加载器。这样做将引起UnsatisfiedLinkError 错误抛出,例如,当加载一个本地库到两个类加载器,System.loadLibrary将抛出UnsatisfiedLinkError 。这种新方式的好处在于:

· 基于类加载器的名字空间分隔机制被保护起来,本地库不能混合不同类加载器的类。

· 另外,本地库能够卸载当他们相对应的类加载器被垃圾回收。

JNI_OnLoad

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

虚拟机调用JNI_OnLoad,当本地库加载的时候(例如,通过System.loadLibrary). JNI_OnLoad必须返回本地库需要的JNI版本.

为了使用任意的新的JNI函数, 本地库必须导出一个返回JNI_VERSION_1_2的JNI_OnLoad 函数 .如果本地库没有导出 JNI_OnLoad 函数, 虚拟机假设库只需要JNI_VERSION_1_1的JNI版本.如果虚拟机不能识别JNI_OnLoad返回的版本号,本地库就不能被加载.

LINKAGE:

从包含本地方法实现的本地库导出.

SINCE:

JDK/JRE 1.4

为了使用在J2SE 发行版1.2中介绍的JNI函数, 和那些 JDK/JRE 1.1中可用的,本地库必须导出返回JNI_VERSION_1_2的JNI_OnLoad函数 .

为了使用在J2SE 发行版1.4中介绍的JNI函数, 和那些 JDK/JRE 1.2中可用的,本地库必须导出返回JNI_VERSION_1_4的JNI_OnLoad函数 .

如果本地库不导出JNI_OnLoad 函数,虚拟机假设只要求JNI_VERSION_1_1的JNI版本. 如果虚拟机不能识别JNI_OnLoad返回的版本号,本地库就不能被加载.

JNI_OnUnload

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

当包含本地库的类加载器被垃圾回收时虚拟机调用JNI_OnUnload.这个函数被用来执行清除操作. 因为这个函数从未知的上下文 (例如从终结器)调用, 在使用JAVA虚拟机服务上程序员要保守,要避免从任意的JAVA回调.

注意JNI_OnLoad 和JNI_OnUnload 是JNI库随意提供地两个函数,而不是从虚拟机导出的.

LINKAGE:

从包含本地方法实现的本地库导出.

调用API函数

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

typedef const struct JNIInvokeInterface *JavaVM;

const struct JNIInvokeInterface ... = {

NULL,

NULL,

NULL,

DestroyJavaVM,

AttachCurrentThread,

DetachCurrentThread,

GetEnv,

AttachCurrentThreadAsDaemon

};

注意JNI_GetDefaultJavaVMInitArgs(), JNI_GetCreatedJavaVMs(),和JNI_CreateJavaVM()这三个调用API函数不是JavaVM函数表的一部分。这些函数能够不用已经存在的JavaVM结构体而被使用。

JNI_GetDefaultJavaVMInitArgs

jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);

返回JAVA虚拟机的默认配置. 调这个函数之前, 本地代码必须设置 vm_args->version 域到它期望支持的虚拟机的JNI版.这个函数返回后, vm_args->version 将被设置成虚拟机支持的实际JNI版本.

LINKAGE:

从实现JAVA虚拟机的本地库导出.

PARAMETERS:

vm_args: 指向充满默认参数的JavaVMInitArgs 结构体.

RETURNS:

如果要求的版本支持返回JNI_OK; 如果要求的版本不支持返回JNI错误代码(负值).

JNI_GetCreatedJavaVMs

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

返回所有创建的JAVA虚拟机.指向虚拟机的指针按创建顺序写入缓冲区 vmBuf.至多bufLen 数目的条目将被写入. 创建的虚拟机总数在 *nVMs 中返回.

从JDK/JRE 1.2, 单进程创建多虚拟机不被支持.

LINKAGE:

从实现JAVA虚拟机的本地库导出.

PARAMETERS:

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

bufLen: 缓冲区长度.

nVMs: 指向整型的指针.

RETURNS:

成功返回JNI_OK ; 失败返回合适的JNI错误代码 (负值).

JNI_CreateJavaVM

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

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

从JDK/JRE 1.2 , 单进程创建多虚拟机不被支持.

NI_CreateJavaVM 的第二个参数总是一个指向 JNIEnv *的指针,  当第三个参数是指向一个使用选项字符串来编码任意虚拟机启动项的JavaVMInitArgs:

typedef struct JavaVMInitArgs {

jint version;

jint nOptions;

JavaVMOption *options;

jboolean ignoreUnrecognized;

} JavaVMInitArgs;

域version 必须至少设置成 JNI_VERSION_1_2. 域 options 是下面类型的数组:

typedef struct JavaVMOption {

char *optionString;  /* the option as a string in the default platform encoding */

void *extraInfo;

} JavaVMOption;

数组大小用JavaVMInitArgs中的域 nOptions来表示 . 如果 ignoreUnrecognized是JNI_TRUE, JNI_CreateJavaVM 忽略所有未被认可的以 "-X" or "_"开头的选项字符串.如果 ignoreUnrecognized是JNI_FALSE,JNI_CreateJavaVM 一遇到任何未被认可的选项字符串就返回JNI_ERR . 所有的JAVA虚拟机必须识别下面的标准选项集:


选项字符串


意义


-D<name>=<value>


设置系统属性


-verbose[:class|gc|jni]


允许详细输出. 这个选项能够跟随一个逗号分隔的指示虚拟机哪种信息将被打印的名称的清单. 例如, "-verbose:gc,class"指示虚拟机打印垃圾回收和类加载相关的信息. 标准的名称包括: gc, class, 和jni. 所有非标准的(虚拟机限定的)名称必须以"X"开头.


vfprintf


extraInfo 是指向vfprintf 钩子的指针.


exit


extraInfo 是指向exit 钩子的指针.


abort


extraInfo 是指向abort 钩子的指针.

另外, 每个虚拟机实现可能实现它自己的非标准选项字符串集.非标准选项名称必须以 "-X" 或者下划线 ("_")开头. 例如, JDK/JRE 支持-Xms 和-Xmx 选项来允许程序员指定初始的和最大的堆空间大小. 以 "-X" 开头的选项能够从"java" 命令行访问.

这是JDK/JRE创建JAVA虚拟机的代码例子:

JavaVMInitArgs vm_args;

JavaVMOption options[4];

options[0].optionString = "-Djava.compiler=NONE";           /* 禁用JIT */

options[1].optionString = "-Djava.class.path=c:\myclasses"; /* 用户类 */

options[2].optionString = "-Djava.library.path=c:\mylibs";  /* 设置本地库路径 */

options[3].optionString = "-verbose:jni";                   /* 打印JNI相关信息 */

vm_args.version = JNI_VERSION_1_2;

vm_args.options = options;

vm_args.nOptions = 4;

vm_args.ignoreUnrecognized = TRUE;

/* 注意在JDK/JRE 中,不需要调用

* JNI_GetDefaultJavaVMInitArgs.

*/

res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);

if (res < 0) ...

LINKAGE:

从实现JAVA虚拟机的本地库导出.

PARAMETERS:

p_vm:指向放置结果虚拟机结构体位置的指针.

p_env: 指向主线程JNI接口指针放置位置的指针.

vm_args: JAVA虚拟机初始化参数.

RETURNS:

成功返回JNI_OK ; 失败返回合适的JNI错误代码 (负值).

DestroyJavaVM

jint DestroyJavaVM(JavaVM *vm);

卸载JAVA虚拟机并回收资源.

在 JDK/JRE 1.1中不支持 DestroyJavaVM . 从JDK/JRE 1.1 只有主线程能够调用 DestroyJavaVM. 自 JDK/JRE 1.2以后, 任何的线程,不论连接的或者没有连接的都能够调用此函数. 如果当前线程连接着, 虚拟机等待直到当前线程是唯一的非守护用户级JAVA线程. 如果当前线程未连接,虚拟机连接当前线程并等待直到当前线程是唯一的非守护用户级JAVA线程. 然而,JDK/JRE 仍然不支持虚拟机卸载.

LINKAGE:

JavaVM 接口函数表索引3.

PARAMETERS:

vm: 将被销毁的JAVA虚拟机.

RETURNS:

成功返回JNI_OK ; 失败返回合适的JNI错误代码 (负值).

从JDK/JRE 1.1.2 不支持卸载JAVA虚拟机.

AttachCurrentThread

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

连接当前线程到JAVA虚拟机.在参数JNIEnv 返回JNI接口指针.

尝试连接线程,如果已经连接则空操作.

一个本地线程不能同时连接两个JAVA虚拟机.

当一个线程连接虚拟机, 上下文类加载器是引导程序加载器.

LINKAGE:

JavaVM 接口函数表索引4.

PARAMETERS:

vm: 当前线程将要连接的虚拟机.

p_env: 指向当前线程的JNI接口指针将要放置的位置的指针.

thr_args: 可以是 NULL 或者指向指定附加信息的JavaVMAttachArgs 结构体的指针:

从JDK/JRE 1.1, 第二个参数 AttachCurrentThread总是指向 JNIEnv的指针. 第三个参数 AttachCurrentThread 被保留, 应该被置为NULL.

从JDK/JRE 1.2, 第三个参数传 NULL作为1.1中的形式,或者传一个指向下面的指定附加信息的结构体的指针:

typedef struct JavaVMAttachArgs {

jint version;  /* must be at least JNI_VERSION_1_2 */

char *name;    /* the name of the thread as a modified UTF-8 string, or NULL */

jobject group; /* global ref of a ThreadGroup object, or NULL */

} JavaVMAttachArgs

RETURNS:

成功返回JNI_OK ; 失败返回合适的JNI错误代码 (负值).

AttachCurrentThreadAsDaemon

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

和 AttachCurrentThread 相同的语意, 但新建的 java.lang.Thread 实例是守护线程.

如果线程已经通过AttachCurrentThreadAsDaemon或AttachCurrentThread 连接了,这个程序简单地设置penv指向的值为当前线程的JNIEnv.在这种情况下,AttachCurrentThread和这个程序都不会对线程的守护状态有影响。

LINKAGE:

JavaVM 接口函数表索引7.

PARAMETERS:

vm: 当前线程将要连接的虚拟机实例.

penv: 指向JNIEnv 接口指针将要放置的位置的指针.

args: 指向JavaVMAttachArgs 结构体的指针.

RETURNS

成功返回JNI_OK ; 失败返回合适的JNI错误代码 (负值).

EXCEPTIONS

无.

SINCE:

JDK/JRE 1.4

DetachCurrentThread

jint DetachCurrentThread(JavaVM *vm);

从JAVA虚拟机分离当前线程. 此线程持有的所有JAVA监视程序将被释放. 所有等待此线程死亡的JAVA线程将被通知到.

从JDK/JRE 1.2 ,主线程能够虚拟机分离.

LINKAGE:

JavaVM 接口函数表索引5.

PARAMETERS:

vm:当前线程将要分离的虚拟机.

RETURNS:

成功返回JNI_OK ; 失败返回合适的JNI错误代码 (负值).

GetEnv

jint GetEnv(JavaVM *vm, void **env, jint version);

LINKAGE:

JavaVM 接口函数表索引6.

PARAMETERS:

vm: 取回接口的虚拟机实例.

env: 指向当前线程JNI 接口指针将要放置的位置的指针.

version: 需要的JNI版本.

RETURNS:

如果当前线程没有连接到虚拟机,设置*env 为 NULL,并返回JNI_EDETACHED.如果指定的版本不支持, 设置*env 为NULL, 并返回JNI_EVERSION. 否则, 设置*env为合适的接口, 并返回 JNI_OK.

SINCE:

JDK/JRE 1.2

最后附上整理的PDF文档,WPS生成的,部分标签可能不准,望谅解:

文档地址:http://download.csdn.net/detail/u011430511/7425867

Oracle官网JNI简介和接口函数分析,布布扣,bubuko.com

时间: 2024-10-05 04:09:37

Oracle官网JNI简介和接口函数分析的相关文章

oracle官网下载老版本jdk + 如何命令行下wget下载jdk

一.文章由来 1.前天有人再去你咨询如何下载jdk的老版本,在oracle官网上找了老半天,找不到相应的选项~ 2.等待问题解决了之后,又抛出来一个新的问题,如何wget直接下载,毕竟百十来兆的文件,下载下来再上传对于我们这种蜗牛带宽来说也是一件苦逼的事情~ 二.如何下载jdk的历史版本 1.访问http://www.oracle.com 2.点击Downloads---->Java for Developers 3.在弹出的的页面中,下拉页面到最下面,点击历史归档 4.点击进去,同意协议,然后

如何在Oracle官网下载历史版本JDK

打开Oracle官网,准备下载java JDK(下载时需要使用注册用户登陆,可以免费注册) 官网地址:http://www.oracle.com/ 2 鼠标悬停Downloads,会出现相关内容,如下图: 3 点击“Java for Developers”,进入JDK下载页面: 4 可以看到下载页面如下,不过是最新的: 5 此页面最下面中有这一项,如下图. 内容大概是: (在Java档案提供一些我们的历史的Java版本. 警告:这些年长的JRE和版本的JDK提供帮助开发人员调试问题在更旧的系统.

Oracle官网JDK的API离线文档下载方法

最近在学习JAVA开发,使用频率最高的工具莫过于JAVA API,当我们身边没有可连接的网络,而又急需API文档时候,很明显我们需要在我们的电脑存储一份离线文档.下面是去Oracle官网下载API Documentation的步骤: 0.在地址栏输入http://www.oracle.com/index.html 进入Oracle官网,如下图: 1.把鼠标移到Downloads那里,不要点它哦!会看到展开一系列Oracle的产品下载,注意到第一列有个叫做“Popular Downloads” 红

如何在Oracle官网下载java的JDK最新版本和历史版本

官网上最显眼位置只显示了Java SE的JDK的最新版本下载链接,因为都是英文,如果英文不是很好,寻找之前的JDK版本需要很长时间,而且未必能在那个隐蔽的位置找到之前版本列表. 今天小编来给你详细讲解下如何在ORACLE官网下载JDK 步骤: 1.打开Oracle官网,准备下载JDK(下载时需要使用注册用户登陆,可以免费注册) 官网地址:http://www.oracle.com/ 2.有账户的直接登录下载,没有的注册一下就可以下载了 3.开始下载JDK.用鼠标将网页拉到最下面 4.进去后,默认

在Oracle官网下载JDK详细步骤

首先进入Oracle官网,链接地址:https://www.oracle.com/cn/downloads/index.html 进入界面后往下滑,在所有下载中点击Java  点击进入后再详细定位到Java的相关下载位置  随后界面会滑动到对应的界面,然后选择红线框内的JDK插件 然后进入界面,选择你想要的版本,我选择的是第二个,然后选择第一个JDK下载 然后等它下载完,再安装就可以了,一般默认安装在C:\Program Files下 原文地址:https://www.cnblogs.com/l

oracle 官网下载

1.官网: http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html 2.数据库网址:' http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html 3.weblogic 下载网址 http://www.oracle.com/technetwork/middleware/weblogi

Oracle官网下载参考文档

最近有人问我有没有Oracle11g数据库官方参考文档,我就想,这不是在官网可以下载到的吗,疑惑,问了之后才知道,他官网找过,但时没有找到.不要笑,其实有很多人一样是找不到的,下面就一步一步操作下: 1.打开浏览器,输入网址 :www.oracle.com(这步不会的话,建议你可以改行了) 2.找到Menu-->OTN -->Documentation-->Database  点击database后跳转到新的界面 3.选择版本,这里以11gR2为例 5.选好版本后,拉到页面最下面找到do

到Oracle官网下载 Oracle11 G 数据可和客户端操作

1.准备一个Oracle的官网账号 用户名:[email protected] 密码:LR4ever.1314 2.在搜索框中输入Oracle 11 G 3.点击Database Downloadds 4.选中要下载的对应的系统版本 5.下载对应的数据库 原文地址:https://www.cnblogs.com/yvanBk/p/10031767.html

ORACLE官网JAVA学习文档

Trails Covering the Basics 1 Getting Started 1.1 The Java Technology Phenomenon 1.1.1 About the Java Technology The Java Programming Language ?? Figure 1 an overview of the software development process java文件以.java作为后缀 源文件被javac compiler编译为.class 文件