JNI实现流程图
1. 首先编写需要JNI功能的Java类源文件。其中,需要JNI实现的方法应当用native关键字声明。在该类中,用System. loadLibrary()方法加载需要的动态链接库。关键代码如下:
//Compute.java
……
public class Compute {
public native double comp (double [] params);
……
static {
// 调用动态链接库
System. loadLibrary(“mathlib”);
}
……
}
2. 将该类源文件用Java类编译器编译成二进制字节码文件。由于采用了native关键字声明,编译器会忽视没有代码体的JNI方法部分。
3. 利用javah -jni *.class 生成相关JNI方法的头文件。我们能手工生成该文件,不过由于Java虚拟机是根据一定的命名规范完成对JNI方法的调用,所以手工编写头文件需要特别小心。
上述文件产生的头文件部分代码如下:
//Compute. h
……
extern “C” {
JNIEXPORT jdouble JNICALL Java_Compute_comp (JNIEnv *, jobject, jdoubleArray);
}
……
能看出,JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。
JNI函数的参数也由三部分组成: 首先是JNIEnv *,是个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同——非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其 Java 类的引用; 其余的参数对应通常 Java 方法的参数,参数类型需要根据一定规则进行映射。
4. 根据头文件编写相应方法的实现代码。由于篇幅所限,具体的实现部分在此不再赘述。在编码过程中,需要注意变量的长度问题,例如Java的整型变量长度为32位,而C语言为16位,所以要仔细核对变量类型映射表,防止在传值过程中出现问题。
5. 利用C/C++编译器将JNI实现代码编译成动态链接库。调用者类中需要显式调用该链接库。
在Win32环境下,能利用Visual C ++或其他能产生DLL文件的C/C++编译器将实现代码编译成动态链接库。笔者利用的是Microsoft.NET Framework的编译器。编译指令如下,其中%Java_HOME%是笔者的jdk安装目录变量:
cl -I%Java_HOME%\include
-I%Java_HOME%\include\win32
-LD jnicomp. c -Femathlib. dll
在Sun Soloaris下,相应指令为:
cc -G -I/usr/local/java/include -I/usr/local/java/include/solaris jnicomp. c \
-o mathlib. so
注意,编译的时候需要用I指令包含必要的库文件路径。
经过上述处理,就基本上完成了一个包含本地化方法的Java类的研发。
JNI技术的应用
一些主要的Java技术,如JDBC和RMI,大部分都采用JNI方式实现。不过,采用JNI确实会影响程式的平台无关性,所以只能在特别需要的地方才能使用。通常来说,如果遇见下面的情况,我们能考虑JNI:
● 需要直接操作物理设备,而没有相关的驱动程式,这时候我们可能需要用C甚至汇编语言来编写该设备的驱动,然后通过JNI调用;
● 涉及大量数学运算的部分,用Java会带来些效率上的损失;
● 用Java会产生系统难以支付的开销,如需要大量网络链接的场合;
● 存在大量可重用的C/C++代码,通过JNI能减少研发工作量,避免重复研发。
另外,在利用JNI技术的时候要注意以下几点:
● 由于Java安全机制的限制,不要试图通过Jar文件的方式发布包含本地化方法的Applet到客户端;
● 注意内存管理问题,虽然在本地方法返回 Java 后将自动释放局部引用,但过多的局部引用将使虚拟机在执行本地方法时耗尽内存;
● JNI技术不仅能让Java程式调用C/C++代码,也能让C/C++代码调用Java代码。
文章来自艾米美甲培训学院 http://www.amysnail.com.cn