Android JNI 学习(JNI 简介)

于今腐草无萤火,终古垂杨有暮鸦。这篇文章主要讲述Android JNI 学习:JNI 简介相关的知识,希望能为你提供帮助。
JNI 即 java Native Interface 是 native 编程接口,它允许在Java虚拟机(VM)内运行Java代码与其他编程语言(主要是C和C++)编写的应用程序和库进行交互操作。
JNI最重要的好处是它对底层Java VM的实现没有任何限制。因此,Java VM供应商可以添加对JNI的支持,而不会影响VM的其他部分。程序员可以编写一个native应用程序或库的版本,并期望它可以与支持JNI的所有Java VM一起使用。
本文主要将从以下几点讲述JNI相关的内容,用于我们了解JNI:
概述、背景、目标、实现方式。
一、JNI 概述虽然您可以完全使用Java编写应用程序,但有些情况下Java本身并不能满足您的应用程序的需求。当应用程序无法完全用Java编写时,程序员使用JNI编写Java Native 方法来处理这些情况。
【Android JNI 学习(JNI 简介)】以下示例说明何时需要使用Java Native方法:

  • 标准Java类库不支持应用程序所需的与平台相关的功能。
  • 您已经有一个使用另一种语言编写的库,并希望通过JNI使其可以访问Java代码。
  • 您希望在较低级别的语言(如C/C++)中实现一小部分时间关键代码。
通过JNI编程,您可以使用Native方法:
  • 创建,检查和更新Java对象(包括数组和字符串)。
  • 调用Java方法。
  • 捕获并抛出异常。
  • 加载类并获取类信息。
  • 执行运行时类型检查。
二、JNI 产生的历史背景来自不同供应商的VM提供了不同的Native方法接口。这些不同的接口迫使程序员在给定平台上生成,维护和分发多个版本的本机方法库。
我们简要介绍一些本机方法接口,例如:
  • JDK 1.0本机方法接口
  • Netscape的Java运行时接口
  • Microsoft的原始本机接口和Java / COM接口
1、JDK 1.0本机方法接口
JDK 1.0 附带了Native方法接口。不幸的是,这个接口不适合其他Java VM采用有两个主要原因。 
首先,本机代码访问Java对象中的字段作为C结构的成员。但是,Java语言规没有定义对象在内存中的布局方式。如果Java VM在内存中以不同方式布局对象,则程序员必须重新编译本机方法库。 
其次,JDK 1.0的本机方法接口依赖于保守的垃圾收集器。unhand例如,不受限制地使用宏使得必须保守地扫描本机堆栈。
2、Java运行时接口
Netscape提出了Java运行时接口(JRI),它是Java虚拟机中提供的服务的通用接口。JRI的设计考虑了可移植性 - 它对底层Java VM中的实现细节做了很少的假设。JRI解决了广泛的问题,包括本机方法,调试,反射,嵌入(调用)等。 
3、原始本机接口和Java / COM接口
Microsoft Java VM支持两种本机方法接口。在低级别,它提供了有效的原始本机接口(RNI)。RNI提供了与JDK本机方法接口的高度源级向后兼容性,尽管它有一个主要区别。本机代码必须使用RNI函数与垃圾收集器明确交互,而不是依赖于保守的垃圾收集。
在更高级别,Microsoft的Java / COM接口为Java VM提供了与语言无关的标准二进制接口。Java代码可以像使用Java对象一样使用COM对象。Java类也可以作为COM类公开给系统的其余部分。
三、JNI 产生的目标我们相信,统一且经过深思熟虑的标准接口为每个人提供以下好处:
  • 每个VM供应商都可以支持更多的natvie代码。
  • 工具构建器不必维护不同类型的natvie方法接口。
  • 应用程序编程人员将能够编写其natvie代码的一个版本,该版本将在不同的VM上运行。
实现标准本机方法接口的最佳方法是让所有各方都参与Java VM。因此,我们在Java许可证持有者之间组织了一系列关于统一本机方法接口设计的讨论。从讨论中可以清楚地看出,标准本机方法接口必须满足以下要求:
  • 二进制兼容性 - 主要目标是在给定平台上的所有Java VM实现中对本机方法库进行二进制兼容。程序员应该只为给定平台维护其本机方法库的一个版本。
  • 效率 - 为了支持时间关键代码,本机方法接口必须施加很少的开销。确保VM独立性(以及二进制兼容性)的所有已知技术都带有一定量的开销。我们必须以某种方式在效率和VM独立性之间达成妥协。
  • 功能 - 接口必须公开足够的Java VM内部,以允许本机方法完成有用的任务。
四、实现JNI的方式讨论我们希望采用现有方式之一作为标准,主要是为了给在不同VM中学习多个接口的程序员带来最小的负担。但是令人失望的是,目前没有现有的解决方案来实现我们这样的想法。
Netscape的JRI是最接近我们想要的的便携式Natvie方法接口,并被用作我们设计的起点。熟悉JRI的读者会注意到API命名约定,方法和字段ID的使用,本地和全局引用的使用等方面的相似之处。尽管我们尽最大努力,但JNI与JRI不是二进制兼容的,尽管VM可以同时支持JRI和JNI。
微软的RNI是对JDK 1.0的改进,因为它解决了使用非保守垃圾收集器的本机方法的问题。但是,RNI不适合作为独立于VM的本机方法接口。与JDK一样,RNI本机方法将Java对象作为C结构访问,但也导致两个问题:
  • RNI将内部Java对象的布局暴露给本机代码。
  • 直接访问Java对象作为C结构使得无法有效地合并“写入障碍”,这在高级垃圾收集算法中是必需的。 
作为二进制标准,COM确保跨不同VM的完全二进制兼容性。调用COM方法只需要间接调用,这几乎不会产生任何开销。此外,COM对象在解决版本问题方面比动态链接库有了很大的改进。
但是,使用COM作为标准Java本机方法接口受到以下几个因素的阻碍:
  • 首先,Java / COM接口缺少某些所需的功能,例如访问私有字段和引发一般异常。
  • 其次,Java / COM接口自动为Java对象提供标准的IUnknown和IDispatch COM接口,以便本机代码可以访问公共方法和字段。遗憾的是,IDispatch接口不处理重载的Java方法,并且在匹配方法名称时不区分大小写。此外,通过IDispatch接口公开的所有Java方法都被包装以执行动态类型检查和强制。这是因为IDispatch接口在设计时考虑了弱类型语言(例如Basic)。
  • 第三,COM不是处理单独的低级功能,而是旨在允许软件组件(包括完整的应用程序)协同工作。我们认为将所有Java类或低级本机方法视为软件组件是不合适的。
  • 第四,由于缺乏对UNIX平台的支持,因此立即采用COM受到阻碍。
虽然Java对象不作为COM对象公开给本机代码,但JNI接口本身与COM二进制兼容。JNI使用与COM相同的跳转表结构和调用约定。这意味着,只要跨平台支持COM,JNI就可以成为Java VM的COM接口。
JNI不被认为是给定Java VM支持的唯一本机方法接口。标准接口使程序员受益,他们希望将本机代码库加载到不同的Java VM中。在某些情况下,程序员可能必须使用较低级别的VM特定接口来实现最高效率。在其他情况下,程序员可能使用更高级别的界面来构建软件组件。实际上,随着Java环境和组件软件技术的日趋成熟,本机方法将逐渐失去意义。
五、总结首先本文是译文,原文地址为:https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/intro.html。
同时也是本人整理的JNI教程的第一篇,可能部分内容语法有点不通顺,但是看完了也能基本了解JNI是什么,产生的背景,以及JNI被实现的方式。

    推荐阅读