1、什么是JNI?
Java本地接口(Java Native Interface ),JNI是属于Java特有的特性,主要作用是让Java代码与用其他编程语言(例如C、C ++、汇编)编写的应用程序和库进行交互。通过JNI,可以让Java具备跨平台的特性,增强Java和本地语言交互的能力。
2、什么时候需要用到JNI?
- 因为JAVA是跨平台的,一些与平台相关的功能就不能很好的支持,这时可以使用JNI。
- 已经有一个用另一种语言编写的库(比如C/C++编写的),并且希望通过JNI使Java代码可以访问它。
- 希望使用诸如汇编之类的较低级语言来实现一小部分对时间要求严格的代码。
3、JNI开发步骤。
- 在Java类中先声明native方法。
public class HelloJNI {
//这里加载动态库 动态库现在还没有先空着
static {
System.load("");
}
public static void main(String[] args) {
print("Hello JNI");
}
//声明一个native方法
public native static void print(String string);
}
- 通过javac编译Java源文件得到该类的.class文件。
- 使用javah -jni导出c/c++原生函数的头文件(这一步会将所有的native方法导出为C/C++头文件中的函数声明)。
- 新建C/C++项目,然后将生成的.h头文件复制到项目中,同时将jdk中的jni_md.h和jni.h复制到项目中。然后点击项目的头文件,右击-》添加-》现有项-》选中这3个文件点击添加。
- 将导出的头文件(这里是HelloJNI.h)中的#include <jni.h>改为#include “jni.h”。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class HelloJNI */
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: print
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_HelloJNI_print
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
备注,使用尖括号编译器会到系统路径下查找头文件,通常用来导入系统文件。而使用双引号" ",编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找,通常用来导入我们自己的头文件)
- 编写HelloJNI.h头文件的实现。
#include "pch.h"
#include <iostream>
#include "HelloJNI.h"
JNIEXPORT void JNICALL
Java_HelloJNI_print(JNIEnv * env, jclass jcls, jstring jstr) {
char* s=(char*)env->GetStringUTFChars(jstr, NULL);
printf("print--> %s ", s);
}
- 生成动态库。
在生成动态库之前需要先配置下。
点击调试-》XX属性 将常规中的配置类型改为动态库,因为最终的生成结果不是exe程序,而是一个动态库。
再点击生成-》配置管理器 配置和平台相关的信息。
最后点击生成-》重新生成解决方案。VS就会帮我们创建动态库。
将生成的动态库路径复制到 System.load方法中去加载动态库。
public class HelloJNI {
//加载动态库
static {
System.load("D:\\programme\\c++\\repos\\JNIHello\\x64\\Debug\\JNIHello.dll");
}
public static void main(String[] args) {
print("Hello JNI");
}
//声明一个native方法
public native static void print(String string);
}
- 运行JAVA程序
可以看到这句话实际上是在C++层面输出的。
可以看到JNI下一共涉及到三个角色:C/C++代码、JNI层、Java层。JNI相当于桥梁,native层和JAVA层的交互通过JNI来实现。
4、补充
1、Java中的native方法会被导出为C/C++头文件中的函数申明,命名规则为:Java_包名_类名_方法名
2、JNIEnv * env 是一个指向JNI环境的指针,通过这个指针我们可以访问JNI提供的方法。通过这些方法我们就可以做到和Java层代码的交互,调用 Java 函数、操作 Java 对象。Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象。
4、JNIEnv只在当前线程中有效。nitive方法不能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对nitive方法多次调用时,传递给该native方法的JNIEnv是相同的。但是,一个nitive方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。
3、Java层的数据类型在native层肯定是无法使用的,因此他们会被转为对应的JNI类型。
-
基本类型对应关系表
-
引用类型对应表