前言
认识JNI(Java Native Interface)技术,了解Java调用本地C/C++库的简单原理以及一些基本的知识点;自己编写一个自定义的JNI接口。
一、简介
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。
本地代码与 Java 虚拟机之间是通过 JNI 函数实现相互操作的。JNI 函数通过接口指针来获得,本地方法将 JNI 接口指针当作参数来接受。虚拟机保证在从相同的 Java 线程中对本地方法进行多次调用时,传递给本地方法的接口指针是相同的,本地方法被不同的 Java 线程调用时,它接受不同的 JNI接口指针。
JNI的强大特性使我们在使用JAVA平台的同时,还可以重用原来的本地代码。作为虚拟机实现的一部分,JNI允许JAVA和本地代码间的双向交互。
1、什么是java jni
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。
本地代码与 Java 虚拟机之间是通过 JNI 函数实现相互操作的。JNI 函数通过接口指针来获得,本地方法将 JNI 接口指针当作参数来接受。虚拟机保证在从相同的 Java 线程中对本地方法进行多次调用时,传递给本地方法的接口指针是相同的,本地方法被不同的 Java 线程调用时,它接受不同的 JNI接口指针。
JNI的强大特性使我们在使用JAVA平台的同时,还可以重用原来的本地代码。作为虚拟机实现的一部分,JNI允许JAVA和本地代码间的双向交互。
2、jni 的应用场景
2.1 解决性能问题
在程序对时间敏感或对性能要求特别高时,有必要使用更底层的语言,例如使用C或者C++来实现具体功能,然后在java中直接调用这些功能。
2.2、 解决本机平台接口调用问题
JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。
2.3、嵌入式开发应用
“一次编程,到处使用”的Java软件概念原本就是针对网上嵌入式小设备提出的,几经周折,目前SUN公司已推出了J2ME(Java 2 P1atform Micro Edition)针对信息家电的Java版本,其技术日趋成熟,开始投入使用。SUN公司Java虚拟机(JVM)技术的有序开放,使得Java软件真正实现跨平台运行,即Java应用小程序能够在带有JVM的任何硬软件系统上执行。加上Java语言本身所具有的安全性、可靠性和可移植性等特点,对实现瘦身上网的信息家电等网络设备十分有利,同时对嵌入式设备特别是上网设备软件编程技术产生了很大的影响。也正是由于JNI解决了本机平台接口调用问题,于是JNI在嵌入式开发领域也是如火如荼。
3、Java 和JNI基本类型对照及基本实现
java的基本类型可以直接与C/C++的基本类型映射。
Java与JNI引用类型对照表:
4、简单的实现案例
4.1、 在java类中申明Native方法(此类为JNI类程序,担任代理角色),并编译成class文件(在此类中同时也需要加载需要使用的类库)
public class MainActivity {
static {
//这种方式需要把MainActivity.dll放在jdk/bin目录下
System.loadLibrary("MainActivity");
//System.load("C:\\Users\\Administrator.DESKTOP-Q7DQAHS\\source\\repos\\ProjectNewDll\\x64\\Debug\\ProjectNewDll.dll");
}
public static native String stringFromJNI();
public static void main(String[] args) {
System.out.println("exec " + stringFromJNI());
}
}
这里声明了一个native方法stringFromJNI(),并指定实现此接口的动态库名称为MainActivity.dll,存放在jdk/bin目录下。
4.2、 用javah将第一步的class文件生成头文件(具体命令:javah -jni XXX, javah程序统一了java中的native方法、头文件中的函数名和动态库中的函数实现之间的对应关系)
javac MainActivity :生成字节码文件
javah -classpath absolutepath\src\main\java com.game.test.jni.MainActivity :生成可用的头文件com_game_test_jni_MainActivity.h,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_game_test_jni_MainActivity */
#ifndef _Included_com_game_test_jni_MainActivity
#define _Included_com_game_test_jni_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_game_test_jni_MainActivity
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_game_test_jni_MainActivity_stringFromJNI
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
4.3、 用其他语言(C、C++等)实现上述头文件中的函数,生成动态库,供java程序使用
关于动态库的编写,参照网上他人的实现:visual studio编写jni踩坑 - 简书,不在复述。
4.4、 发布java和动态库
把生成的MainActivity.dll动态库放在jdk/bin目录下,执行MainActivity → main() 就能看到输出结果了!