JNI是Java Native Interface的缩写,中文为JAVA本地调用,它允许Java代码和其他语言写的代码进行交互,主要是C和C++。由于在实现的时候遇到很多坑,所以记录下实现步骤,本文主要讲解在windows环境下实现,废话不多说,上代码。
1 环境
本文使用的环境:
jdk1.8
windows10,64位
mingw_64
2 java代码
public class JniDemo {
public native void callCppMethod();
public void callJavaMethod() {
System.out.println("hello JNI");
}
}
本文是JNI的一个简单的实现,通过java native方法调用c语言函数,再通过c函数调用java方法,即上图代码通过callCppMethod调用callJavaMethod方法。
3 生成.h头文件
进入JniDemo .java源文件执行命令:javac -h ./jni JniDemo .java
该命令是编译源文件,并在源文件的jni目录生成.h的头文件。
执行后结果:
4 编写c的实现
本文使用的c的开发工具为Visual Studio 2013
Visual Studio 2013下载链接
提取码:nvg7
安装好后创建项目:
拷贝头文件com_jt_learn_jni_JniDemo.h到项目的头文件目录,如下图
此时打开头文件是有报错的,需要引入两个头文件jni.h和jni_md.h;
jni.h文件在:JAVA_HOME/include
jni_md.h文件在:JAVA_HOME/include/win32
步骤如下:
添加完成后报错消失。
创建.c文件实现头文件中的方法,如下图:
打开com_jt_learn_jni_JniDemo.c 引入头文件后发现报错
这是由于我们刚刚直接将头文件复制进来的原因,现在找不到,需要将刚刚生成的头文件所在目录添加进来。
编辑com_jt_learn_jni_JniDemo.c文件,实现调用java的callJavaMethod方法。
#include "com_jt_learn_jni_JniDemo.h"
/*
抛出RuntimeException
*/
void throwRuntimeException(JNIEnv *jEnv, const char *msg) {
JNIEnv env = *jEnv;
jclass clazz = env->FindClass(jEnv, "java/lang/RuntimeException");
env->ThrowNew(jEnv, clazz, msg);
}
/*
通过c++调用java方法
*/
JNIEXPORT void JNICALL Java_com_jt_learn_jni_JniDemo_callCppMethod
(JNIEnv *jEnv, jobject jObj){
JNIEnv env = *jEnv;
/* 获取类的字节码 */
jclass jniDemoClass = env->GetObjectClass(jEnv, jObj);
if (NULL == jniDemoClass) {
throwRuntimeException(jEnv, "获取类字节码失败");
}
/* 获取需要指向的函数 */
jmethodID callJavaMethod = env->GetMethodID(jEnv, jniDemoClass, "callJavaMethod", "()V");
if (NULL == callJavaMethod) {
throwRuntimeException(jEnv, "获取运行的JAVA方法ID失败");
}
/* 执行方法 */
env->CallVoidMethod(jEnv, jObj, callJavaMethod);
}
5 生成动态链接库
5.1 安装gcc
inux 略.
windows:
首先下载mingw:
mingw_64位最新下载地址:mingw_64位最新版下载地址
mingw_32最新版下载地址:mingw_32位最新版下载地址
下载后需要安装。
官网下载比较慢,提供百度网盘的一个压缩包ming-w64-v7.0.0:
ming-w64-v7.0.0.7z 提取码:9ak0
下载后解压缩将bin目录配置到环境变量中
5.2 生成动态链接库
linux:
gcc -shared -fpic -I /JAVA_HOME/include com_jt_learn_jni_JniDemo.c -o /JAVA_HOME/bin/libcom_jt_learn_jni_JniDemo.so
将JAVA_HOME修改为自己的jdk目录
-I 给gcc添加自定义的头文件的路径。
生成的库文件名必须以lib开始,加载的时候不用lib
System.loadLibrary(“com_jt_learn_jni_JniDemo”);
生成动态链接库com_jt_learn_jni_JniDemo.so,动态链接库需要生成在jvm能加载到的地方,如果不知道放哪,可通过打印参数看jvm默认从哪些位置加载,即将com_jt_learn_jni_JniDemo.so拷贝到下面java代码输出的一个目录。
System.out.println(System.getProperty("java.library.path"));
windows:
找到com_jt_learn_jni_JniDemo.c文件目录,
将com_jt_learn_jni_JniDemo.h拷贝到com_jt_learn_jni_JniDemo.c目录
进入该目录命令窗口,执行命令:
gcc -c -I D:\jdk\jdk1.8.0_211-64\include -I D:\jdk\jdk1.8.0_211-64\include\win32 com_jt_learn_jni_JniDemo.c
执行完上述命令会生成com_jt_learn_jni_JniDemo.o文件。如下图
然后执行命令:
gcc -Wl,--add-stdcall-alias -shared -o com_jt_learn_jni_JniDemo.dll com_jt_learn_jni_JniDemo.o
执行完上述命令生成com_jt_learn_jni_JniDemo.dll文件
将com_jt_learn_jni_JniDemo.dll文件拷贝到jvm能够加载动态链接库的地方即下面代码输出的目录中的一个:
System.out.println(System.getProperty(“java.library.path”));
6 加载动态链接库,执行本地方法
public class App {
public static void main(String[] args) {
// 打印加载动态链接库的目录
System.out.println(System.getProperty("java.library.path"));
// 加载动态链接库
System.loadLibrary("com_jt_learn_jni_JniDemo");
// 调用本地方法
new JniDemo().callCppMethod();
}
}
执行main方法:
可以看到通过调用本地方法,调用了c的实现,在c中又调用了java方法。