在学习Android NDK开发,需要了解JNI技术,在网上找到了一本书《The Java Native Interface Programmer’s Guide and Specification》,讲JDK1.0与JDK1.2的JNI接口和功能(目前最新的是JDK1.4)。我已经看完一遍了,觉得不错,决定提取重点翻译一下,留作参考。
为了方便查找对应相关知识,关于这本书的一系列文章仍按照书本的章节来写。本文的知识点和图片、示例等均来源于上面提到的书。也不知道会不会侵犯版权。
阅读前,确定你已经熟悉Java、C和C++语言的基本语法和基本用法,比如Java定义类,C++定义类和成员函数,C语言定义函数,还有控制流等。只需要非常基本的知识就可以了。
我不对你的基本知识产生任何怀疑,因为你已经熟悉了基本知识,那就继续。
一、简介
JNI(Java Native Interface),即Java原生接口,将C/C++等语言写成的原生代码与用Java写成的代码整合起来,协同工作。这样可以在Java中使用已有的其他代码,无需用Java重写,也可以在Java虚拟机中完成Java不能胜任的工作(如在Java中操作文件系统)。
Java平台是一个包括了Java虚拟机(VM)和API的编程环境,任何一个Java平台都保证支持Java语言、Java虚拟机和API。Java平台一般是在操作系统(Windows,Linux,Mac等)的基础上安装,它提供了一些特性,保证平台上的程序不依赖特定的操作系统(该程序用Java语言写成,可以在任何Java虚拟机中运行,与具体的操作系统无关)。而原生程序由原生语言(操作系统支持的编程语言)如C/C++写成,它们是与特定的操作系统有关的,因此在使用JNI时,程序员要保证编程环境所在的操作系统支持要使用的原生代码。
二、JNI的角色
JNI是JavaVM实现的一部分,有了它,Java代码与原生代码之间可以相互调用,非常方便。下图是JNI的角色示意图。
从上图中可以看出,Java程序库可以在任何一个JavaVM上运行,而JavaVM与原生应用和原生库是依赖于具体的宿主环境(即操作系统),因此在不同的操作系统上要实现不同的JavaVM和不同的原生应用和原生库。不同的JavaVM由Orical公司实现,一部分不同的原生应用和原生库由操作系统提供(如stdio.h文件和标准C库),另一部分原生应用和原生库由程序员本人提供。
JNI作为连接Java代码和原生代码之间的桥梁,支持两种原生代码:原生库和原生应用程序。
原生库:使用JNI实现原生方法(函数),将这些函数封装到库中。Java代码就可以调用库里的原生方法了,这与Java调用本身的方法一样。这展示了JNI支持Java代码调用原生代码的特性。
原生应用程序:JNI同时也支持原生代码调用Java代码的特性,即它提供了回调接口。可以在不同的操作系统上安装对应的JavaVM,因为JavaVM实现是依赖操作系统的,因此实现会提供原生库,封装了JavaVM的相关方法(如启动、停止JavaVM等),这些方法支持回调,即允许操作系统或其他程序调用这些库中的方法实现启动、停止VM,运行Java程序等一系列操作。因此原生应用程序可以使用JNI,调用VM的回调方法,把一个VM嵌入到原生应用程序中。简单的例子就是,操作系统上的浏览器一般是原生应用程序(C/C++实现的),但它们可以运行Java Applet,这是因为它们使用了JNI的这种回调特性,把一个JavaVM嵌入到了它本身,于是Java Applets就可以在这个VM中运行。
三、使用JNI的影响
相比较完全用Java实现的Java应用程序,使用了JNI的Java应用程序丢失了某些特性。
1.它不再平台无关,不能方便的移植。虽然用Java实现的部分可以在不同平台之间移植,但是用原生代码实现的部分就必须在不同的平台修改和重新编译。
2.Java语言是类型安全的,但原生编程语言如C/C++不是。使用JNI时,要对原生代码部分额外关心,如错误检查、类型检查等。如果原生代码崩溃了,那么很有可能整个应用程序都会崩溃。因此,原生代码在调用JNI提供的接口时,必须做类型检查等。
通常,原生代码所占的比例越少越好,原生代码与Java代码之间的界限越明显越好。
四、何时使用JNI
官方推荐尽可能的不使用JNI,只有Java不提供某项功能或考虑到效率等问题时,再考虑使用JNI。
简单点来说,如果Java应用程序与原生代码的通信可以放在不同的进程中或不同的主机上,那么使用进程间通信(IPC)或网络通信(TCP/IP);Java可以使用JDBC访问数据库等等等等。但如果Java应用程序必须要在同一个进程中使用到底层的特性,而Java本身不提供这种特性(如操作文件系统),那么必须使用JNI。
使用JNI的例子如下:
1.Java应用程序想要某些操作,但Java API不提供这些操作或在不同的进程中实现效率低,那么可以使用JNI。
2.想在本进程中访问已有的原生库,如标准C库,可以使用JNI。免去了在不同进程中拷贝代码和数据的资源浪费。
3.一个应用程序占用多个进程,可能导致占用过多的内存空间或CPU资源。如果不能接受这种情况,可以使用JNI。
五、JNI的演变
简单说,JNI目前是Java 2 SDK 1.2。相比较1.1,1.2改进了很多地方,提供了很多新接口,并且向后兼容。想要了解更多,可以去Orical官网上查询。