JNI

0x01 What Is JNI

JNI:Java Native Interface

Java是基于C语言实现的,很多底层API都是通过调用JNI来实现的。

与硬件、操作系统交互的操作可以使用JNI来提升程序性能

JNI让Java能够调用编译好的动态链接库里的方法(存在跨平台问题👊)

像我们熟悉的Class.forName()最底层就是C去实现的,它被native修饰

image-20230322164250276

0x02 Best Pratice

  • 定义native修饰方法

  • javah生成.h头文件

javac -h . JNITest.java(JDK ≥ 10)

老版本:javah -jni JNITest

得到JNITest.h头文件和JNITest.class文件

  • C++实现头文件

VS新建dll项目,调试属性=》C/C++预编译头 => 不使用预编译头

image-20230322172805579

生成后得到x64的dll

javah生成的头文件中的函数命名方式是有非常强制性的约束

(JNIEnv *, jclass, jstring)表示分别是JNI环境变量对象java调用的类对象参数入参类型

无法包含jni.h的看这里:JNI报错:"无法打开源文件jni.h" "JNIEXPORT此声明没有存储类或类型说明符"_"无法打开源文件 “jni.h"

  • 加载dll

将生成的JniHi.dll放在class同目录下,执行java JNITest

成功弹出计算器

Dynamic Register

上面只是定义了一个native方法,然后通过System.loadLibrary()System.load()加载native方法对应的动态链接库,来实现Java层面调用C/C++提供的服务。

还有另外一种方法来实现native方法,在JNI_OnLoad函数中进行函数映射,将java里面的方法映射到自己实现的C/C++函数,这样就不用通过javah生成头文件了(还有那串又臭又长的函数名)。JNI_OnLoad会在加载动态链接库时首先被调用。

既然这样的话,我们就能将原本java里定义的native方法映射到我们自己实现的方法。

JNI Interface

JNI_OnLoad

第一个参数表示Java虚拟机,第二个参数一般为NULL,返回值为JNI版本。

JNINativeMethod

该结构体定义了C/C++函数与Java方法的映射

  • name:Java中定义的native方法名

  • signature:native方法的函数签名

  • fnPtr:C/C++中native函数指针

注册的时候需要设置一个JNINativeMethod数组,数组中有几个元素,就表示映射到一个类的多少个native方法。

native方法通过JNI函数来访问JVM中的数据结构。

JNIEnv

Java本地接口环境,一般为一个指针,指向native方法的一个函数表

可以从JavaVM获取JNIEnv,第一个参数为JNIEnv的二级指针,第二个参数为JNI版本

JNIEnv提供了一些与Java建立桥梁的方法,如FindClass,根据类名字符串找到对应的jclass

RegisterNatives 注册C/C++函数,映射到Java层的native方法

  • clazz:要注册方法所在的Java类

  • methods:Java方法与native方法映射的关系数组

  • nMethods:映射的方法个数,一般为methods数组元素的个数

此外JNIEnv还有一些用于jxxx的Java数据类型和C数据类型之间转换的方法,这里不展开。

Build

CLion新建项目

image-20231029194401186

CMakeLists.txt👇

library.cpp👇,这里为方便编写,就不管stdHandles标准输入输出错误这些了。

Build得到一个动态链接库

image-20231029195029812
image-20231029200100188

Last updated

Was this helpful?