探机之家
探机的自我修养

JNI初步使用


JNI 是什么

Java Native Interface, 通常缩写为 JNI,。它可以使得在JVM中运行的 Java 代码能够调用 C/C++ 或者汇编所编写的代码。JNI 可以让 C 代码创建或者使用 Java 对象,来更加高效的运行一些性能敏感型代码,也可以绕过 Java 的内存管理机制来更加高效的使用内存。很多机器学习的 Java binding 都是通过 JNI 实现的。

下边举一个简单的例子来带大家写一个 JNI 程序。所用 IDE 是 Intellij IDEA。

编写 Java class

首先创建 Java 工程,步骤略(例子中项目名为 hellojni)

org/cclin/hellojni/HelloJNI.java
package org.cclin.hellojni;
public class HelloJNI {
static {
//加载的 动态链接库 的文件名
System.loadLibrary("hello");
}
//native 表示 jni 的方法
private native void sayHello();
public static void main(String[] args) {
new HelloJNI().sayHello();
}
}

生成头文件

点击编译按钮(或者使用下边第一条命令),会在项目根目录生成 out 文件夹

随后用以下命令生成头文件

Terminal window
javac HelloJNI.java # 如果不使用IDEA
javah -jni -classpath out/production/jni/ -d jni/include org.cclin.hellojni.HelloJNI

其中 classpath 指定类目录,-d 指定输出位置,后面是类名 此时会在 jni/include 中生成名为 org_cclin_hellojni_HelloJNI.h 的头文件

org_cclin_hellojni_HelloJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_cclin_hellojni_HelloJNI */
#ifndef _Included_org_cclin_hellojni_HelloJNI
#define _Included_org_cclin_hellojni_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_cclin_hellojni_HelloJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_cclin_hellojni_HelloJNI_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

接下来需要为这个生成的头文件编写对应的实现

配置 CmakeLists.txt 并编写实现

CmakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(hellojni)
set(CMAKE_CXX_STANDARD 20)
# 查找 JNI 的头文件库
find_package(JNI REQUIRED)
# library 名字应与 `System.loadLibrary("hello")` 中的相同
add_library(hello SHARED HelloJNI.cpp)
target_include_directories(hello PRIVATE include)
# 添加头文件
target_include_directories(hello PRIVATE ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})

这样我们便准备好了 cmake,剩下的只需要在 HelloJNI.cpp 中编写实现就够了

HelloJNI.cpp
#include <iostream>
#include "org_cclin_hellojni_HelloJNI.h"
JNIEXPORT void JNICALL Java_org_cclin_hellojni_HelloJNI_sayHello
(JNIEnv *, jobject) {
std::cout << "HelloJNI" << std::endl;
}

在 CLION 中点击编译即可在 cmake-build-debug 中看到 libhello.dylib 文件(Linux 下为 libhello.so) 或者使用命令行cmake -B build && cmake --build build

为 java 添加动态链接库

在运行 java 时提供 JVM 参数 -Djava.library.path 指向上一步的 .so 文件位置 可以在 IDEA 中添加 VM options -Djava.library.path=$ContentRoot$/jni/cmake-build-debug

设置完成后点击运行即可

(以下内容未经测试) 在实际使用中,.so 文件会被放入到 resource 目录下, 通过 Class.getResource 或者 ClassLoader.getResource 获取资源, 存储到临时文件中,再加载该临时文件,而不用通过 Djava.library.path 显示指定路径

https://docs.oracle.com/en/java/javase/11/docs/specs/jni/design.html https://blog.csdn.net/createchance/article/details/53783490