JNI是Java Native Interface的缩写,Native指C/C++。
JNI内容涉及两个方面:
- Java调用C,这种情况是最主要的
- C调用Java,这种情况不常见
第一步:编写Java代码
package haha;
public class Haha {
public native int add(int x, int y);
public native int sub(int x, int y);
public static void main(String[] args) {
System.loadLibrary("haha");
Haha haha = new Haha();
int x = haha.add(3, 6);
System.out.println(x);
x = haha.sub(3, 6);
System.out.println(x);
}
}
定义一个类Haha,它包括两个方法:add和sub,分别是加法和减法。使用Native关键字表明这两个方法是本地方法,无需在Java中给出实现。
注意:加载dll时,不需要带.dll后缀
第二步:javah命令生成头文件
本项目目录结构如下:src/haha/Haha.java,控制台进入到src/目录下,使用javah命令分析haha.Haha.java文件,生成头文件。
javah haha.Haha
注意:
- javah是jdk安装之后自带的命令,使用之前请确保jdk/bin在环境变量Path中。
- javah haha.Haha,不需要后缀名.java
第三步:编写C代码
打开VisualStudio,新建win32项目,选择dll作为项目类型。
默认分成头文件、源文件、资源文件三个文件夹,没有必要,把这三个文件夹删掉。
新建haha_Haha.h头文件,把第二步中生成的头文件内容复制进来。
右键项目属性,在VC++/包含目录中,添加JDK/include和JDK/include/win32这两个文件夹(用分号隔开),不要删除原来已经有的目录。
新建“源.cpp”,include 那个头文件,并给出实现:
#include"haha_Haha.h"
JNIEXPORT jint JNICALL Java_haha_Haha_add
(JNIEnv *env, jobject obj, jint x, jint y){
return x + y;
}
JNIEXPORT jint JNICALL Java_haha_Haha_sub
(JNIEnv *env, jobject obj, jint x, jint y){
return x - y;
}
最后,点击菜单“生成/生成解决方案(快捷键F7)”或者“生成/重新生成解决方案”,就会在debug目录下找到dll了。
复制上面的dll路径,准备去IntelliJ里面试试能不能加载这个dll
第四步:添加dll运行Java代码
直接运行Java代码会报错:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no haha in java.library.path
看来,必须让java知道dll在哪里,这可以通过java.library.path属性来实现。
点击菜单“Run/Edit Configuration”添加java运行参数
这时运行CTRL+SHIFT+F10运行代码,报错了:
Can't load IA 32-bit .dll on a AMD 64-bit platform
看来,生成的dll是32位的,而我的JDK是64位的(我的系统是64位的)。
切换到VS中,更改项目属性/配置属性,选择64位平台(如果没有x64,点击配置管理器新建平台即可)。之后重新生成dll。
会发现此时dll的目录也发生了改变,所以需要更改java.library.path参数(这个参数跟jar包的classpath是两回事)。
再次运行,终于成功了。
最后
本文只是JNI的入门小例子,更多内容参考《Java核心技术 卷二:高级特性》最后一章。
JNI包括:
- Java和C中数据类型的对应关系,尤其是String类型,C只支持ASCII码,而Java中的String是Unicode范围。
- C中代码访问Java静态成员的变量和方法
- C中代码访问Java对象的成员变量和方法
- C中变量资源申请和释放
- C中代码访问Java数组