收藏 分销(赏)

JNIAPI完全基础手册专业资料.doc

上传人:天**** 文档编号:2998470 上传时间:2024-06-12 格式:DOC 页数:98 大小:439.54KB 下载积分:18 金币
下载 相关 举报
JNIAPI完全基础手册专业资料.doc_第1页
第1页 / 共98页
JNIAPI完全基础手册专业资料.doc_第2页
第2页 / 共98页


点击查看更多>>
资源描述
1 - 简介 本章简介 Java 本地接口(Java Native Interface,JNI)。JNI 是本地编程接口。它使得在 Java 虚拟机 (VM) 内部运营 Java 代码可以与用其他编程语言(如 C、C++ 和汇编语言)编写应用程序和库进行互操作。 JNI 最重要好处是它没有对底层 Java 虚拟机实现施加任何限制。因而,Java 虚拟机厂商可以在不影响虚拟机其他某些状况下添加对 JNI 支持。程序员只需编写一种版本本地应用程序或库,就可以与所有支持 JNI Java 虚拟机协同工作。 本章论及如下主题: · Java 本地接口概述 · 背景 · 目的 · Java 本地接口办法 · 运用 JNI 编程 · JDK 1.1.2 中变化 Java 本地接口概述 尽管可以完全用 Java 编写应用程序,但是有时单独用 Java 不能满足应用程序需要。程序员使用 JNI 来编写 Java 本地办法,可以解决那些不能完全用 Java 编写应用程序状况。 如下示例阐明了何时需要使用 Java 本地办法: · 原则 Java 类库不支持与平台有关应用程序所需功能。 · 已经拥有了一种用另一种语言编写库,而又但愿通过 JNI 使 Java 代码可以访问该库。 · 想用低档语言(如汇编语言)实现一小段时限代码。 通过用 JNI 编程,可以将本地办法用于: · 创立、检查及更新 Java 对象(涉及数组和字符串)。 · 调用 Java 办法。 · 捕获和抛出异常。 · 加载类和获得类信息。 · 执行运营时类型检查。 也可以与调用 API 一起使用 JNI,以容许任意本地应用程序嵌入到 Java 虚拟机中。这样使得程序员可以容易地让已有应用程序支持 Java,而不必与虚拟机源代码相链接。 背景 当前,不同厂商虚拟机提供了不同本地办法接口。这些不同接口使程序员不得不在给定平台上编写、维护和分发各种版本本地办法库。 下面简要分析一下某些已有本地办法接口,例如: · JDK 1.0 本地办法接口 · Netscape Java 运营时接口 · Microsoft 原始本地接口和 Java/COM 接口 JDK 1.0 本地办法接口 JDK 1.0 附带有本地办法接口。遗憾是,有两点因素使得该接口不适合于其他 Java 虚拟机。 第一,平台有关代码将 Java 对象中域作为 C 构导致员来进行访问。但是,Java 语言规范没有规定在内存中对象是如何布局。如果 Java 虚拟机在内存中布局对象方式有所不同,程序员就不得不重新编译本地办法库。 第二,JDK 1.0 本地办法接口依赖于保守垃圾收集器。例如,无限制地使用 unhand 宏使得有必要以保守方式扫描本地堆栈。 Java 运营时接口 Netscape 建议使用 Java 运营时接口 (JRI),它是 Java 虚拟机所提供服务通用接口。JRI 设计融入了可移植性---它几乎没有对底层 Java 虚拟机实现细节作任何假设。JRI 提出了各种各样问题,涉及本地办法、调试、反射、嵌入(调用)等等。 原始本地接口和 Java/COM 接口 Microsoft Java 虚拟机支持两种本地办法接口。在低一级,它提供了高效原始本地接口 (RNI)。RNI 提供了与 JDK 本地办法接口有高度源代码级向后兼容性,尽管它们之间尚有一种重要区别,即平台有关代码必要用 RNI 函数来与垃圾收集器进行显式交互,而不是依赖于保守垃圾收集。 在高一级,Microsoft Java/COM 接口为 Java 虚拟机提供了与语言无关原则二进制接口。Java 代码可以象使用 Java 对象同样来使用 COM 对象。Java 类也可以作为 COM 类显示给系统别的某些。 目的 咱们以为统一,通过细致考虑原则接口可以向每个顾客提供如下好处: · 每个虚拟机厂商都可以支持更多平台有关代码。 · 工具构造器不必维护不同本地办法接口。 · 应用程序设计人员可以只编写一种版本平台有关代码就可以在不同虚拟机上运营。 获得原则本地办法接口最佳途径是联合所有对 Java 虚拟机有兴趣当事方。因而,咱们在 Java 获得允许方之间组织了一系列研讨会,对设计统一本地办法接口进行了讨论。从研讨会可以明确地看出原则本地办法接口必要满足如下规定: · 二进制兼容性 - 重要目的是在给定平台上所有 Java 虚拟机实现之间实现本地办法库二进制兼容性。对于给定平台,程序员只需要维护一种版本本地办法库。 · 效率 - 若要支持时限代码,本地办法接口必要增长一点系统开销。所有已知用于保证虚拟机无关性(因而具备二进制兼容性)技术都会占用一定系统开销。咱们必要在效率与虚拟机无关性之间进行某种折衷。 · 功能 - 接口必要显示足够 Java 虚拟机内部状况以使本地办法可以完毕有用任务。 Java 本地接口办法 咱们但愿采用一种已有办法作为原则接口,由于这样程序员(程序员不得不学习在不同虚拟机中各种接口)工作承担最轻。遗憾是,已有解决方案中没有任何方案可以完全地满足咱们目的。 Netscape JRI 最接近于咱们所设想可移植本地办法接口,因而咱们采用它作为设计起点。熟悉 JRI 读者将会注意到在 API 命名规则、办法和域 ID 使用、局部和全局引用使用,等等中相似点。虽然咱们进行了最大努力,但是 JNI 并不具备对 JRI 二进制兼容性,但是虚拟机既可以支持 JRI,又可以支持 JNI。 Microsoft RNI 是对 JDK 1.0 改进,由于它可以解决使用非保守垃圾收集器本地办法问题。然而,RNI 不适合用作与虚拟机无关本地办法接口。与 JDK 类似,RNI 本地办法将 Java 对象作为 C 构造来访问。这将导致两个问题: · RNI 将内部 Java 对象布局暴露给了平台有关代码。 · 将 Java 对象作为 C 构造直接进行访问使得不也许有效地加入“写屏障”,写屏障是高档垃圾收集算法所必须。 作为二进制原则,COM 保证了不同虚拟机之间完全二进制兼容性。调用 COM 办法只规定间接调用,而这几乎不会占用系统开销。此外,COM 对象对动态链接库解决版本问题方式也有很大改进。 然而,有几种因素阻碍了将 COM 用作原则 Java 本地办法接口: · 第一,Java/COM 接口缺少某些必须功能,例如访问私有域和抛出普通异常。 · 第二,Java/COM 接口自动为 Java 对象提供原则 IUnknown 和 IDispatch COM 接口,因而平台有关代码可以访问公有办法和域。遗憾是,IDispatch 接口不能解决重载 Java 办法,并且在匹配办法名称时不区别大小写。此外,通过 IDispatch 接口暴露所有 Java 办法被打包在一起来执行动态类型检查和强制转换。这是由于 IDispatch 接口设计只考虑到了弱类型语言(例如 Basic)。 · 第三,COM 容许软件组件(涉及完全成熟应用程序)一起工作,而不是解决单个低层函数。咱们以为将所有 Java 类或低层本地办法都当作软件组件是不恰当。 · 第四,在 UNIX 平台上由于缺少对 COM 支持,因此阻碍了直接采用 COM。 虽然咱们没有将 Java 对象作为 COM 对象暴露给平台有关代码,但是 JNI 接口自身与 COM 具备二进制兼容性。咱们采用与 COM 同样跳转表和调用商定。这意味着,一旦具备对 COM 跨平台支持,JNI 就能成为 Java 虚拟机 COM 接口。 咱们以为 JNI 不应当是给定 Java 虚拟机所支持唯一本地办法接口。原则接口好处在于程序员可以将自己平台有关代码库加载到不同 Java 虚拟机上。在某些状况下,程序员也许不得不使用低层且与虚拟机关于接口来获得较高效率。但在其他状况下,程序员也许使用高层接口来建立软件组件。事实上,咱们但愿随着 Java 环境和组件软件技术发展得越来越成熟,本地办法将变得越来越不重要。 运用 JNI 编程 本地办法程序设计人员应开始运用 JNI 进行编程。运用 JNI 编程隔离了某些未知条件,例如终端顾客也许正在运营厂商虚拟机。遵守 JNI 原则是本地库能在给定 Java 虚拟机上运营最佳保证。例如,虽然 JDK 1.1 将继续支持 JDK 1.0 中所实现旧式本地办法接口,但是可以必定是 JDK 将来版本将停止支持旧式本地办法接口。依赖于旧式接口本地办法将不得不重新编写。 如果您正在实现 Java 虚拟机,则应当实现 JNI。咱们(Javasoft 和获得允许方)竭力保证 JNI 不会占用虚拟机实现系统开销或施加任何限制,涉及对象表达,垃圾收集机制等。如果您遇到了咱们也许忽视了问题,请告知咱们。 JDK 1.1.2 中变化 为了更好地支持 Java 运营时环境 (JRE),在 JDK 1.1.2 中对调用 API 在几种方面作了扩展。这些变化没有破坏任何已有代码,JNI 本地办法接口也没有变化。 · JDK1_1InitArgs 构造中 reserved0 域已被重新命名为 version。JDK1_1InitArgs 构造保存 JNI_CreateJavaVM 初始化参数。JNI_GetDefaultJavaVMInitArgs 和 JNI_CreateJavaVM 调用者必要将版本域设立为 0x00010001。JNI_GetDefaultJavaVMInitArgs 被更改为返回 jint,用于表达与否支持所祈求版本。 · JDK1_1InitArgs 构造中 reserved1 域已被重新命名为 properties。这是一种 NULL-终结字符串数组。每个字符串具备如下格式: name=value 表达系统属性(该功能相应于 Java 命令行中 -D 选项)。 · 在 JDK 1.1.1 中,调用 DestroyJavaVM 线程必要是虚拟机中唯一顾客线程。JDK 1.1.2 放松了这一限制。如果调用 DestroyJavaVM 时有各种顾客线程,则虚拟机将等待直到当前线程成为唯一顾客线程,然后销毁自己。 2 - 设计概述 本章着重讨论 JNI 中重要设计问题,其中大某些问题都与本地办法关于。调用 API 设计将在 第 5 章 “调用 API” 中讨论。 JNI 接口函数和指针 平台有关代码是通过调用 JNI 函数来访问 Java 虚拟机功能。JNI 函数可通过接口指针来获得。接口指针是指针指针,它指向一种指针数组,而指针数组中每个元素又指向一种接口函数。每个接口函数都处在数组某个预定偏移量中。图 2-1 阐明了接口指针组织构造。 图 2-1 接口指针 JNI 接口组织类似于 C++ 虚拟函数表或 COM 接口。使用接口表而不使用硬性编入函数表好处是使 JNI 名字空间与平台有关代码分开。虚拟机可以很容易地提供各种版本 JNI 函数表。例如,虚拟机可支持如下两个 JNI 函数表: · 一种表对非法参数进行全面检查,合用于调试程序; · 另一种表只进行 JNI 规范所规定最小限度检查,因而效率较高。 JNI 接口指针只在当前线程中有效。因而,本地办法不能将接口指针从一种线程传递到另一种线程中。实现 JNI 虚拟机可将本地线程数据分派和储存在 JNI 接口指针所指向区域中。 本地办法将 JNI 接口指针当作参数来接受。虚拟机在从相似 Java 线程中对本地办法进行多次调用时,保证传递给该本地办法接口指针是相似。但是,一种本地办法可被不同 Java 线程所调用,因而可以接受不同 JNI 接口指针。 加载和链接本地办法 对本地办法加载通过 System.loadLibrary 办法实现。下例中,类初始化办法加载了一种与平台关于本地库,在该本地库中给出了本地办法 f 定义: 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 调用。 程序员还可调用 JNI 函数 RegisterNatives() 来注册与类关联本地办法。在与静态链接函数一起使用时,RegisterNatives() 函数将特别有用。 解析本地办法名 动态链接程序是依照项名称来解析各项。本地办法名由如下几某些串接而成: · 前缀 Java_ · mangled 全限定类名 · 下划线(“_”)分隔符 · mangled 办法名 · 对于重载本地办法,加上两个下划线(“__”),后跟 mangled 参数签名 虚拟机将为本地库中办法查找匹配办法名。它一方面查找短名(没有参数签名名称),然后再查找带参数签名长名称。只有当某个本地办法被另一种本 地办法重载时程序员才有必要使用长名。但如果本地办法名称与非本地办法名称相似,则不会有问题。由于非本地办法(Java 办法)并不放在本地库中。 下例中,不必用长名来链接本地办法 g,由于另一种办法 g 不是本地办法,因而它并不在本地库中。 class Cls1 { int g(int i); native int g(double d); } 咱们采用简朴名字搅乱方案,以保证所有 Unicode 字符都能被转换为有效 C 函数名。咱们用下划线(“_”) 字符来代替全限定类名中斜杠(“/”)。由于名称或类型描述符从来不会以数字打头,咱们用 _0、...、_9 来代替转义字符序列,如表 2-1 所示: 表 2-1 Unicode 字符转换 转义字符序列 表达 _0XXXX Unicode 字符 XXXX。 _1 字符“_” _2 签名中字符“;” _3 签名中字符“[” 本地办法和接口 API 都要遵守给定平台上库调用原则商定。例如,UNIX 系统使用 C 调用商定,而 Win32 系统使用 __stdcall。 本地办法参数 JNI 接口指针是本地办法第一种参数。其类型是 JNIEnv。第二个参数随本地办法是静态还是非静态而有所不同。非静态本地办法第二个参数是对对象引用,而静态本地办法第二个参数是对其 Java 类引用。 别的参数相应于普通 Java 办法参数。本地办法调用运用返回值将成果传回调用程序中。第 3 章 “JNI 类型和数据构造” 将描述 Java 类型和 C 类型之间映射。 代码示例 2-1 阐明了如何用 C 函数来实现本地办法 f。对本地办法 f 声明如下: package pkg; class Cls { native double f(int i,String s); ... } 具备长 mangled 名称 Java_pkg_Cls_f_ILjava_lang_String_2 C 函数实现本地办法f: 代码示例 2-1: 用 C 实现本地办法 jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( JNIEnv *env, /* 接口指针 */ jobject obj, /* “this”指针 */ jint i, /* 第一种参数 */ jstring s) /* 第二个参数 */ { /* 获得 Java 字符串 C 版本 */ const char *str = (*env)->GetStringUTFChars(env,s,0); /* 解决该字符串 */ ... /* 至此完毕对 str 解决 */ (*env)->ReleaseStringUTFChars(env,s,str); return ... } 注意,咱们总是用接口指针 env 来操作 Java 对象。可用 C++ 将此代码写得稍微简洁某些,如代码示例 2-2 所示: 代码示例 2-2: 用 C++ 实现本地办法 extern "C" /* 指定 C 调用商定 */ jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( JNIEnv *env, /* 接口指针 */ jobject obj, /* “this”指针 */ jint i, /* 第一种参数 */ jstring s) /* 第二个参数 */ { const char *str = env->GetStringUTFChars(s,0); ... env->ReleaseStringUTFChars(s,str); return ... } 使用 C++ 后,源代码变得更为直接,且接口指针参数消失。但是,C++ 内在机制与 C 完全同样。在 C++ 中,JNI 函数被定义为内联成员函数,它们将扩展为相应 C 相应函数。 引用 Java 对象 基本类型(如整型、字符型等)在 Java 和平台有关代码之间直接进行复制。而 Java 对象由引用来传递。虚拟机必要跟踪传到平台有关代码中对象,以使这些对象不会被垃圾收集器释放。反之,平台有关代码必要能用某种方式告知虚拟机它不再需要那些对象,同步,垃圾收集器必要可以移走被平台有关代码引用过对象。 全局和局部引用 JNI 将平台有关代码使用对象引用提成两类:局部引用和全局引用。局部引用在本地办法调用期间有效,并在本地办法返回后被自动释放掉。全局引用将始终有效,直到被显式释放。 对象是被作为局部引用传递给本地办法,由 JNI 函数返回所有 Java 对象也都是局部引用。JNI 容许程序员从局部引用创立全局引用。规定 Java 对象 JNI 函数既可接受全局引用也可接受局部引用。本地办法将局部引用或全局引用作为成果返回。 大多数状况下,程序员应当依托虚拟机在本地办法返回后释放所有局部引用。但是,有时程序员必要显式释放某个局部引用。例如,考虑如下情形: · 本地办法要访问一种大型 Java 对象,于是创立了对该 Java 对象局部引用。然后,本地办法要在返回调用程序之前执行其他计算。对这个大型 Java 对象局部引用将防止该对象被当作垃圾收集,虽然在剩余运算中并不再需要该对象。 · 本 地办法创立了大量局部引用,但这些局部引用并不是要同步使用。由于虚拟机需要一定空间来跟踪每个局部引用,创立太多局部引用将也许使系统耗尽内存。 例如,本地办法要在一种大型对象数组中循环,把取回元素作为局部引用,并在每次迭代时对一种元素进行操作。每次迭代后,程序员不再需要对该数组元素局 部引用。 JNI 容许程序员在本地办法内任何地方对局部引用进行手工删除。为保证程序员可以手工释放局部引用,JNI 函数将不能创立额外局部引用,除非是这些 JNI 函数要作为成果返回引用。 局部引用仅在创立它们线程中有效。本地办法不能将局部引用从一种线程传递到另一种线程中。 实现局部引用 为了实现局部引用,Java 虚拟机为每个从 Java 到本地办法控制转换都创立了注册服务程序。注册服务程序将不可移动局部引用映射为 Java 对象,并防止这些对象被当作垃圾收集。所有传给本地办法 Java 对象(涉及那些作为 JNI 函数调用成果返回对象)将被自动添加到注册服务程序中。本地办法返回后,注册服务程序将被删除,其中所有项都可以被当作垃圾来收集。 可用各种不同办法来实现注册服务程序,例如,使用表、链接列表或 hash 表来实现。虽然引用计数可用来避免注册服务程序中有重复项,但 JNI 实现不是必要检测和消除重复项。 注意,以保守方式扫描本地堆栈并不能如实地实现局部引用。平台有关代码可将局部引用储存在全局或堆数据构造中。 访问 Java 对象 JNI 提供了一大批用来访问全局引用和局部引用函数。这意味着无论虚拟机在内部如何表达 Java 对象,相似本地办法实现都能工作。这就是为什么 JNI 可被各种各样虚拟机实现所支持核心因素。 通过不透明引用来使用访问函数开销比直接访问 C 数据构造开销来得高。咱们相信,大多数状况下,Java 程序员使用本地办法是为了完毕某些重要任务,此时这种接口开销不是首要问题。 访问基本类型数组 对于具有大量基本数据类型(如整数数组和字符串) Java 对象来说,这种开销将高得不可接受 (考虑一下用于执行矢量和矩阵运算本地办法情形便知)。对 Java 数组进行迭代并且要通过函数调用取回数组每个元素,其效率是非常低。 一种解决办法是引入“钉住”概念,以使本地办法可以规定虚拟机钉住数组内容。而后,该本地办法将接受指向数值元素直接指针。但是,这种办法包括如下两个前提: · 垃圾收集器必要支持钉住。 · 虚拟机必要在内存中持续存储基本类型数组。虽然大多数基本类型数组都是持续存储,但布尔数组可以压缩或不压缩存储。因而,依赖于布尔数组确切存储方式本地办法将是不可移植。 咱们将采用折衷办法来克服上述两个问题。 一方面,咱们提供了一套函数,用于在 Java 数组一某些和本地内存缓冲之间复制基本类型数组元素。这些函数只有在本地办法只需访问大型数组中一小某些元素时才使用。 另一方面,程序员可用另一套函数来取回数组元素受约束版本。记住,这些函数也许规定 Java 虚拟机分派存储空间和进行复制。虚拟机实现将决定这些函数与否真正复制该数组,如下所示: · 如果垃圾收集器支持钉住,且数组布局符合本地办法规定,则不需要进行复制。 · 否则,该数组将被复制到不可移动内存块中(例如,复制到 C 堆中),并进行必要格式转换,然后返回指向该副本指针。 最后,接口提供了某些函数,用以告知虚拟机本地办法已不再需要访问这些数组元素。当调用这些函数时,系统或者释放数组,或者在原始数组与其不可移动副本之间进行协调并将副本释放。 这种解决办法具备灵活性。垃圾收集器算法可对每个给定数组分别作出复制或钉住决定。例如,垃圾收集器也许复制小型对象而钉住大型对象。 JNI 实现必要保证各种线程中运营本地办法可同步访问同一数组。例如,JNI 可觉得每个被钉住数组保存一种内部计数器,以便某个线程不会解开同步被另一种线程钉住数组。注意,JNI 不必将基本类型数组锁住以专供某个本地办法访问。同步从不同线程对 Java 数组进行更新将导致不拟定成果。 访问域和办法 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,则它必要保证: · 保存对所涉及类活引用,或 · 重新计算该办法 ID 或域 ID。 JNI 对域 ID 和办法 ID 内部实现并不施加任何限制。 报告编程错误 JNI 不检查诸如传递 NULL 指针或非法参数类型之类编程错误。非法参数类型涉及诸如要用 Java 类对象时却用了普通 Java 对象这样错误。JNI 不检查这些编程错误理由如下: · 逼迫 JNI 函数去检查所有也许错误状况将减少正常(对的)本地办法性能。 · 在许多状况下,没有足够运营时类型信息可供这种检查使用。 大多数 C 库函数对编程错误不进行防范。例如,printf() 函数在接到一种无效地址时普通是引起运营错而不是返回错误代码。逼迫 C 库函数检查所有也许错误状况将有也许引起这种检查被重复进行--先是在顾客代码中进行,然后又在库函数中再次进行。 程序员不得将非法指针或错误类型参数传递给 JNI 函数。否则,也许产生意想不到后果,涉及也许使系统状态受损或使虚拟机崩溃。 Java 异常 JNI 容许本地办法抛出任何 Java 异常。本地办法也可以解决突出 Java 异常。未被解决 Java 异常将被传回虚拟机中。 异常和错误代码 某些 JNI 函数使用 Java 异常机制来报告错误状况。大多数状况下,JNI 函数通过返回错误代码并抛出 Java 异常来报告错误状况。错误代码普通是特殊返回值(如 NULL),这种特殊返回值在正常返回值范畴之外。因而,程序员可以: · 迅速检查上一种 JNI 调用所返回值以拟定与否出错,并 · 通过调用函数 ExceptionOccurred() 来获得异常对象,它具有对错误状况更详细阐明。 在如下两种状况中,程序员需要先查出异常,然后才干检查错误代码: · 调用 Java 办法 JNI 函数返回该 Java 办法成果。程序员必要调用 ExceptionOccurred() 以检查在执行 Java 办法期间也许发生异常。 · 某些用于访问 JNI 数组函数并不返回错误代码,但也许会抛出 ArrayIndexOutOfBoundsException 或 ArrayStoreException。 在所有其他状况下,返回值如果不是错误代码值就可保证没有抛出异常。 异步异常 在各种线程状况下,当前线程以外其他线程也许会抛出异步异常。异步异常并不及时影响当前线程中平台有关代码执行,直到浮现下列状况: · 该平台有关代码调用某个有也许抛出同步异常 JNI 函数,或者 · 该平台有关代码用 ExceptionOccurred() 显式检查同步异常或异步异常。 注意,只有那些有也许抛出同步异常 JNI 函数才检查异步异常。 本地办法应在必要地方(例如,在一种没有其他异常检查紧密循环中)插入 ExceptionOccurred() 检查以保证当前线程可在恰当时间内对异步异常作出响应。 异常解决 可用两种办法来解决平台有关代码中异常: · 本地办法可选取及时返回,使异常在启动该本地办法调用 Java 代码中抛出。 · 平台有关代码可通过调用 ExceptionClear() 来清除异常,然后执行自己异常解决代码。 抛出了某个异常之后,平台有关代码必要先清除异常,然后才干进行其他 JNI 调用。当有待定异常时,只有如下这些 JNI 函数可被安全地调用:ExceptionOccurred()、ExceptionDescribe() 和 ExceptionClear()。ExceptionDescribe() 函数将打印关于待定异常调试消息。 3 - JNI 类型和数据构造 本章讨论 JNI 如何将 Java 类型映射到本地 C 类型。 基本类型 表 3-1 描述 Java 基本类型及其与计算机有关本地等效类型。 表 3-1 基本类型和本地等效类型 Java 类型 本地类型 阐明 boolean jboolean 无符号,8 位 byte jbyte 无符号,8 位 char jchar 无符号,16 位 short jshort 有符号,16 位 int jint 有符号,32 位 long jlong 有符号,64 位 float jfloat 32 位 double jdouble 64 位 void void N/A 为了使用以便,特提供如下定义。 #define JNI_FALSE 0 #define JNI_TRUE 1 jsize 整数类型用于描述重要指数和大小: typedef jint jsize; 引用类型 JNI 包括了诸多相应于不同 Java 对象引用类型。JNI 引用类型组织层次如图 3-1 所示。 图 3-1 引用类型层次 在 C 中,所有其他 JNI 引用类型都被定义为与 jobject 同样。例如: typedef jobject jclass; 在 C++ 中,JNI 引入了虚构类以加强子类关系。例如: class _jobject {}; class _jclass :public _jobject {}; ... typedef _jobject *jobject; typedef _jclass *jclass; 域 ID 和办法 ID 办法 ID 和域 ID 是常规 C 指针类型: struct _jfieldID; /*不透明构造 */ typedef struct _jfieldID *jfieldID; /* 域 ID */ struct _jmethodID; /* 不透明构造 */ typedef struct _jmethodID *jmethodID;/* 办法 ID */ 值类型 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 虚拟机类型签名表述。表 3-2 列出了这些类型签名。 表 3-2 Java 虚拟机类型签名 类型签名 Java 类型 Z boolean B byte C char S short I int J long F float D double L fully-qualified-class ; 全限定类 [ type type[] ( arg-types ) ret-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 字符字符序列可以按每字符一种字节表达,但是最多只能表达 16 位字符。所有在 \u0001 到 \u007F 范畴内字符都用单字节表达,如下所示: 字节中七位数据拟定了所示字符值。空字符 (\u000) 和 \u0080 到 \u07FF 范畴内字符用一对字节表达, 即 x 和 y,如下所示: 值为 ((x&0x1f)<<6)+(y&0x3f) 字符需用两个字节表达。 \u0800 到 \uFFFF 范畴内字符用三个字节表达,即 x,y,和 z: 值为 ((x&0xf)<<12)+(y&0x3f)<<6)+(z&0x3f) 字符需用三个字节表达。 此格式与“原则” UTF-8 格式之间有两个区别。第一,空字节 (byte)0 使用双字节格式进行编码,而不是单字节格式。这意味着 Java 虚拟机 UTF-8 字符串不也许有嵌入空值。第二,只使用单字节、双字节和三字节格式。Java 虚拟机不能辨认更长 UTF-8 格式。 4 - JNI 函数 本章为 JNI 函数提供参照信息。其中列出了所有 JNI 函数,同步也给出了 JNI 函数表精确布局。 注意:“必要”一词用于约束 JNI 编程人员。例如,当阐明某个 JNI 函数必要接受非空对象时,就应保证不要向该 JNI 函数传递 NULL。这时,JNI 实现将无需在该 JNI 函数中执行 NULL 指针检查。 本章某些资料改编自 Netscape JRI 文档。 该参照资料按用法对函数进行组织。参照某些按下列函数区域进行组织: · 版本信息 · 类操作 · 异常 · 全局及局部引用 · 对象操作 · 访问对象域 · 调用实例办法 · 访问静态域 · 调用静态办法 · 字符串操作 · 数组操作 · 注册本地办法 · 监视程序操作 · Java 虚拟机接口 接口函数表 每个函数均可通过 JNIEnv 参数以固定偏移量进行访问。JNIEnv 类型是一种指针,指向存储所有 JNI 函数指针构造。其定义如下: 注意:前三项留作将来与 COM 兼容。此外,咱们在函数表开头某些也留出来各种 NULL 项,从而可将将来与类关于 JNI 操作添加到 FindClass 背面,而非函数表末尾。 注意,函数表可在所有 JNI 接口指针间共享。 代码示例 4-1 const struct JNINativeInterface ... = { NULL, NULL, NULL, NULL, GetVersion,
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服