第一章 概述
本章主要介绍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代码创建,检测,操作直接缓冲区:
每个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