JNI 全称 Java Native Interface。Java本地方法接口,它是Java语言允许Java代码与C、C++代码交互的标准机制。维基百科是这样解释的:“当应用无法完全用Java编程语言实现的时候,(例如,标准Java类库不支持的特定平台特性或者程序库时),JNI使得编程者能够编写native方法来处理这种情况”。这就意味着,在一个Java应用程序中,我们可以使用我们需要的C++类库,并且直接与Java代码交互,而且在可以被调用的C++程序内,反过来调用Java方法(回调函数)。
JNI的优势
- 操作系统相关 JNI使得一些"过程"无需在Java中实现。例如,硬件敏感的,或者直接与操作系统API关联的命令。
- 性能优化 底层的库,如图形,计算,各种类型的渲染等等,可以提高应用的运行性能。
- 既有实现 已经有大量的库已经被实现,编程者可直接使用,不用再自行编写。这里的库指的是用其他编程语言实现的程序库,例如IO流或者线程等底层与OS交互的操作都是由C/C++实现的。
JNI的实现
1、定义Java类
定义一个带native方法的java类如:
/**
* @author jack
*/
public class JniCompileTest {
/**
* 执行jni调用
*/
public native Map excute(String param1, Object param2);
}
2、生成头文件
在项目的根目录下执行javah命令
javah -classpath . -jni com.package.jni.test.JniCompileTest
完成后,会在根目录下生成一个同名的.h结尾的头文件。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_package_jni_test_JniCompileTest */
#ifndef _Included_com_package_jni_test_JniCompileTest
#define _Included_com_package_jni_test_JniCompileTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_package_jni_test_JniCompileTest
* Method: excute
* Signature: (Ljava/lang/String;Ljava/lang/Object;)Ljava/util/Map;
*/
JNIEXPORT jobject JNICALL Java_com_package_jni_test_JniCompileTest_excute
(JNIEnv *, jobject, jstring, jobject);
#ifdef __cplusplus
}
#endif
#endif
3、引入头文件
在头文件中的 jni.h 如果在编译动态链接库(.so文件)的时候找不到,可以去本地的JDK目录中的include文件下找到两个文件:jni.h和jni_md.h 。然后拷贝到与JniCompileTest.h同目录下。
修改引入方式
#include
改为
#include "jni.h"
然后可以定义一个同名的cpp源码文件,开发JNI的cpp实现了
//
// Created by jack on 2021/7/10.
//
#include "com_package_jni_test_JniCompileTest.h"
JNIEXPORT jobject JNICALL Java_com_package_jni_test_JniCompileTest_excute
(JNIEnv *env, jobject, jstring, jobject){
// do something
return NULL;
}
4、编译动态链接库
在clion中构建一个cmake项目,然后写CMakeLists.txt
project(JniCompileTest C CXX)
cmake_minimum_required(VERSION 2.8.0)
# 这里用自己的目录即可
INCLUDE_DIRECTORIES(src/main/cpp/jnilib/include/)
FILE(GLOB SRC_JNI_LIBRARY src/main/cpp/jnilib/src/*.cpp)
FILE(GLOB HEADER_JNI_LIBRARY src/main/cpp/jnilib/include/*.h)
add_library(JniCompileTest SHARED ${HEADER_JNI_LIBRARY} ${LIST_SRC} ${SRC_JNI_LIBRARY})
完成后执行编译
5、java引入动态链接库
java 使用动态库,需要用System类load,例如在上面的类的构造方法的中加入即可。
/**
* @author jack
*/
public class JniCompileTest {
public JniCompileTest(){
// mac下文件为JniCompileTest.dylib
File outputJNIFile = new File("/user/jack/JniCompileTest.so");
System.load(outputJNIFile.getAbsolutePath());
}
/**
* 执行jni调用
*/
public native Map excute(String param1, Object param2);
}
结尾
JNI的封装完成,但是在JNI的开发中,还是不少注意的地方,例如JNI对象的定义,如何解析java传递来的对象,如果回调java方法等。