现在有个需求,就是使用Java调用linux或者windows的系统API,或者需要C语言才能实现的本地方法,这时候就JNI就出场了。
下面看一个完整的例子:
准备工作:windows下安装mingw-64编译器 使用gcc命令编译
一、创建Java类文件
Main.java
public class Main {
// 静态初始化代码块,保证虚拟机在第一次使用该类时就会装载库
static{
System.loadLibrary("MyNativeLib");
}
// native 关键字表示本地方法,提醒编译器该方法将在外部定义(注:不能包含_)
public static native void sayHello();
// 主函数调用本地方法
public static void main(String[] args) {
sayHello();
}
}
二、生成.h头文件
> javac Main.java
> javah -classpath . -jni Main
使用上述命令生成的 Main.h 文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */
#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Main
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Main_sayHello
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
三、创建C实现文件
创建Main.c文件,实现头文件中的函数。
#include <stdio.h>
#include "Main.h"
JNIEXPORT void JNICALL Java_Main_sayHello(JNIEnv *env, jobject c1) {
printf("Hello Native !!\n");
}
四、编译生成动态链接库
使用gcc的-I参数(大写i)指定需要需找头文件的目录。
或者,将JDK目录下的 include/jni.h 和 include/win32/jni_md.h 拷贝到当前工程的目录下来。
然后执行命令(在这之前需要将Main.h中的#include <jni.h>改成 #include "jni.h",先在当前目录搜素头文件,再到系统目录搜索)
> gcc -fPIC -shared -o MyNativeLib.dll Main.c
至此目录中已生成 MyNativeLib.dll 动态链接库文件,其他的C语言文件可以删除。
扫描二维码关注公众号,回复:
10867164 查看本文章
五、测试运行
> java Main
运行结果为:
常见问题:
- java.lang.UnsatisfiedLinkError: 类名.函数名()V
此时动态库已经加载,检查.h文件和.c文件函数实现名字是否一致,且命名不能带有下划线_。
c文件中的函数名是有规则的 Java_Main_sayHello ,Java_类名_函数名。
- no MyNativeLib in java.library.path at java.lang.ClassLoader.loadLibrary(Unknown Source)
如果是dll库都没有加载成功,看对应dll是否成功生成,编译命令是否正确和项目位置等。
- 在linux环境编译和上述相同,动态库文件扩展名为.so