JNI是Java Native Interface的缩写,中文为JAVA本地调用。使用JNI可以很方便的用我们的Java程序调用C/C++程序。很多时候,某些功能用Java无法实现,比如说涉及到底层驱动的一些功能,这时候我们就可以利用JNI来调用C或者C++程序来实现,这就是JNI的强大之处。但是JNI也有它的缺点,使用java与本地已编译的代码交互,通常会丧失平台可移植性。
下面是一个Helloworld例子,调用C++方法输出“Hello World. From JavaAndC++.”
第一步:创建Java类,在里面定义一个本地方法(用native关键字修饰的方法)
第二步:使用javah命令(javah 类的全路径)生成本地方法的C++头文件
打开MainCls.java文件所在目录,打开DOS窗口,输入: javac MainCls.java,回车,生成class文件,MainCls.class
在类文件的包目录的起始目录下打开DOS,如:com目录下,
输入javah 包.类名,回车,生成.h文件,这个头文件是根据包名和类名来命名的。
这是上面那个文件的内容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lvshi_javac_test_MainCls */
#ifndef _Included_com_lvshi_javac_test_MainCls
#define _Included_com_lvshi_javac_test_MainCls
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_lvshi_javac_test_MainCls
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_lvshi_javac_test_MainCls_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
JNIEXPORT void JNICALL Java_com_lvshi_javac_test_MainCls_sayHello (JNIEnv *, jobject);
是对 MainCls 类中的本地方法sayHello()的声明,这个h文件相当于我们在java里面的接口,这里声明了一个 Java_com_lvshi_javac_test_MainCls_sayHello(JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。
然后把这个文件拷贝到和.cpp文件同级目录下:
第三步:编写C/C++本地代码,生成动态链接库文件
使用Visual Studio或者其他的编辑器新建一个C++项目,新建一个.cpp文件(这是C++的源文件)
(1) 把Java的jdk的include目录下的jni.h文件、win32目录下的jawt_md.h、jni_md.h文件拷贝到Visual Studio的D:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.11.25503\include安装目录下(在其安装的头目录下的搜索框中输入iostream,点击搜索即可看到)
拷贝到:
在.cpp文件中引入这几个库文件:
按F7编译这个文件,可能会出现以下几个错误,解决方法附上:
<1>#include "com_lvshi_javac_test_MainCls.h" 显示无法打开源文件
1.把com_lvshi_javac_test_MainCls.h文件拷贝到当前编辑文件的同级目录下
2.右键C++项目,属性,C/C++,常规,将该文件的全路径拷贝到“附加包含目录”一栏中,两个路径之间使用分号;隔开
<2>fatal error C1083: 无法打开包括文件:“jawt.h”: No such file or directory
双击错误的地方打开jawt.h文件,把波浪线标红的文件改成#include "jawt_md.h",替换掉原来的文件
<3>LINK : fatal error LNK1561: 必须定义入口点
每个应用程序(文件格式为exe)都应该有个启动点,这个点就是main函数。缺少就会出现link error 。
解决方法有2:
1.添加含有main函数的CPP文件(这里不采用这个方法)
2 右击属性,在常规项中修改配置类型,将应用程序修改为库,动静态库
以上按F7编译如果没有错误,就会在C++项目的Debug目录下生成 .dll文件,以 .cpp文件命名
拷贝这个文件到Java的jdk的安装目录的bin目录下,或者为了方便,我们也可以将dll所在的工程目录加入到环境变量path中去,这样可以避免每次都要拷贝的麻烦。注意修改环境变量之后要重启myeclipse
第四步:Java调用本地函数
继续编写Java程序,编写main方法,调用C++的函数方法sayHello()
package com.lvshi.javac.test;
public class MainCls {
// 声明本地方法
public native void sayHello();
// 调用本地方法
public static void main(String[] args) {
/**
* 加载本地函数,Demo01这个名称要和生成的Demo01.dll名称一致,
* 加载动态链接库,JVM只需要加载一次就可以调用了,
* “NativeCode”是上面生成的动态链接库的名字,不含后缀名
*/
System.loadLibrary("Demo01");
// 创建Java类对象
MainCls mainCls = new MainCls();
// 调用本地函数
mainCls.sayHello();
}
}
运行以上的Java程序,如果报以下错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError:
D:\Program Files\Java\jdk1.7.0_79\bin\Demo01.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
就到Visual Studio中把.cpp文件改成x64系统下的文件(具体看物理机的系统是32位还是64位)
按F7重新编译,生成Demo01.dll文件,拷贝这个文件到D:\Program Files\Java\jdk1.7.0_79\bin目录下重新覆盖掉原来的.dll文件,clean一下Java项目,
再一次执行Java程序,就会打印出结果: