JNI开发教程

JNI 是 Java 语言提供的 Java 和 C/C++ 相互沟通的机制,Java 可以通过 JNI 调用本地的 C/C++ 代码,本地的 C/C++ 的代码也可以调用 Java 代码。JNI 是本地编程接口,Java 和 C/C++
互相通过的接口。Java 通过 C/C++ 使用本地的代码的一个关键性原因在于 C/C++ 代码的高效性。 代码和其他语言写的代码进行交互。

JNI概述

JNI 全称是 Java Native Interface(Java 本地接口)单词首字母的缩写,本地接口就是指用 C 和 C++ 开发的接口。由于 JNI 是 JVM 规范中的一部份,因此可以将我们写的 JNI 程序在任何实现了 JNI
规范的 Java 虚拟机中运行。同时,这个特性使我们可以复用以前用 C/C++ 写的大量代码。

开发 JNI 程序会受到系统环境的限制,因为用 C/C++
语言写出来的代码或模块,编译过程当中要依赖当前操作系统环境所提供的一些库函数,并和本地库链接在一起。而且编译后生成的二进制代码只能在本地操作系统环境下运行,因为不同的操作系统环境,有自己的本地库和 CPU 指令集,而且各个平台对标准
C/C++ 的规范和标准库函数实现方式也有所区别。这就造成使用了 JNI 接口的 JAVA 程序,不再像以前那样自由的跨平台。如果要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相应的动态库。

JNI与NDK

JNI 是 Java 提供的一种和其他语言(一般为C或C++)沟通的机制,而 NDK 则是 Android 通过封装 JNI、gcc、g++ 等多种工具实现的工具包。一般情况下,只有运行效率较高的应用会使用 JNI ,例如:手机游戏开发等。

JNI开发流程

一般情况下,JNI 开发流程主要分为以下几个步骤:

  • 编写声明了native方法的Java类或接口。
  • 将编写好的类编译为class字节码文件。
  • 利用 JDK 提供的javah命令为字节码文件生成对应的 JNI 头文件。
  • 搭建C/C++开发环境、编写代码。
  • 编译为合适的动态链接库:
    • Windows —— *.dll
    • Linux/Unix —— *.so
    • Mac —— *.jnilib
  • 在Java代码中通过System.load()System.loadLibrary()加载java.library.path下合适的链接库,以下是两种映射模式:
    • 直接生成头文件并引用实现
    • 通过RegisterNatives()的方式注册函数映射

JNIEXPORTJNICALL的作用

在生成的头文件定义中,你可以发现有两个常量定义的关键字:JNIEXPORTJNICALL
为什么会存在他们?这是因为在 Windows 中编译 dll 动态库规定,如果动态库中的函数要被外部调用,需要在函数声明中添加__declspec(dllexport)标识,表示将该函数导出在外部可以调用。在 Linux/Unix
系统中,这两个宏可以省略不加。这两个平台的区别是由于各自的编译器所产生的可执行文件格式不一样。 所以为了让代码统一,用#define等预处理手段保证了不同系统环境下代码仍然保持一致。

数据类型映射

在调用 Java native 方法将实参传递给 C/C++ 函数的时候,会自动将 java 形参的数据类型自动转换成 C/C++ 相应的数据类型,所以我们在写 JNI 程序的时候,必须要明白它们之间数据类型的对应关系。

Java Type Native Type Sign
void void V
boolean jboolean Z
byte jbyte B
char jchar C
short jshort S
int jint I
long jlong J
float jfloat F
double jdouble D
int[] jarray [I
int[][] jarray [[I
Object jobject Ljava/lang/Object;
Object[] jarray [Ljava/lang/Object;

JNI API

对象的创建

JNI API 参数列表 参数说明 函数说明
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: 对象类,methodID: 构造方法ID,…: 构造方法参数 创建一个新的Java对象 该函数创建并返回一个新的Java对象
jobject NewGlobalRef(JNIEnv *env, jobject obj) env: JNI环境变量,obj: 要创建全局引用的对象 创建一个全局引用 该函数创建并返回一个指向Java对象的全局引用
jobject NewLocalRef(JNIEnv *env, jobject obj) env: JNI环境变量,obj: 要创建局部引用的对象 创建一个局部引用 该函数创建并返回一个指向Java对象的局部引用
jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args) env: JNI环境变量,clazz: 对象类,methodID: 构造方法ID,args: 构造方法参数数组 创建一个新的Java对象 该函数创建并返回一个新的Java对象

对象的操作

JNI API 参数列表 参数说明 函数说明
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig) env: JNI环境变量,clazz: 对象类,name: 字段名称,sig: 字段签名 获取Java对象字段ID 该函数返回Java对象字段ID
void SetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID, jobject value) env: JNI环境变量,obj: Java对象,fieldID: 字段ID,value: 要设置的值 设置Java对象的字段值 该函数设置Java对象的字段值
jobject GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID) env: JNI环境变量,obj: Java对象,fieldID: 字段ID 获取Java对象的字段值 该函数返回Java对象的字段值
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig) env: JNI环境变量,clazz: 对象类,name: 字段名称,sig: 字段签名 获取Java类静态字段ID 该函数返回Java类静态字段ID
jobject GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID) env: JNI环境变量,clazz: 对象类,fieldID: 静态字段ID 获取Java类静态字段值 该函数返回Java类静态字段值
void SetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value) env: JNI环境变量,clazz: 对象类,fieldID: 静态字段ID,value: 要设置的值 设置Java类静态字段值 该函数设置Java类静态字段值
jclass GetObjectClass(JNIEnv *env, jobject obj) env: JNI环境变量,obj: Java对象 获取Java对象的类 该函数返回Java对象的类
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) env: JNI环境变量,clazz: 对象类,name: 方法名称,sig: 方法签名 获取Java方法ID 该函数返回Java方法ID
jobject CallObjectMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: 方法ID,…: 方法参数 调用Java对象的方法 该函数调用Java对象的方法并返回结果
jobject CallStaticObjectMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: 对象类,methodID: 方法ID,…: 方法参数 调用Java类的静态方法 该函数调用Java类的

方法调用

静态方法调用

JNI API 参数列表 参数说明 函数说明
jobject CallStaticObjectMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: Java类,methodID: Java方法ID,…: 可变参数 调用Java静态方法并返回jobject类型结果 该函数调用Java静态方法并返回jobject类型结果
jboolean CallStaticBooleanMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: Java类,methodID: Java方法ID,…: 可变参数 调用Java静态方法并返回boolean类型结果 该函数调用Java静态方法并返回boolean类型结果
jbyte CallStaticByteMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: Java类,methodID: Java方法ID,…: 可变参数 调用Java静态方法并返回byte类型结果 该函数调用Java静态方法并返回byte类型结果
jchar CallStaticCharMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: Java类,methodID: Java方法ID,…: 可变参数 调用Java静态方法并返回char类型结果 该函数调用Java静态方法并返回char类型结果
jshort CallStaticShortMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: Java类,methodID: Java方法ID,…: 可变参数 调用Java静态方法并返回short类型结果 该函数调用Java静态方法并返回short类型结果
jint CallStaticIntMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: Java类,methodID: Java方法ID,…: 可变参数 调用Java静态方法并返回int类型结果 该函数调用Java静态方法并返回int类型结果
jlong CallStaticLongMethod(JNIEnv *env, jclass clazz, jmethodID methodID, …) env: JNI环境变量,clazz: Java类,methodID: Java方法ID,…: 可变参数 调用Java静态方法并返回long类型结果 该函数调用Java静态方法并返回long类型结果

动态方法调用

JNI API 参数列表 参数说明 函数说明
jobject CallObjectMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回jobject类型结果 该函数调用Java动态方法并返回jobject类型结果
jboolean CallBooleanMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回boolean类型结果 该函数调用Java动态方法并返回boolean类型结果
jbyte CallByteMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回byte类型结果 该函数调用Java动态方法并返回byte类型结果
jchar CallCharMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回char类型结果 该函数调用Java动态方法并返回char类型结果
jshort CallShortMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回short类型结果 该函数调用Java动态方法并返回short类型结果
jint CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回int类型结果 该函数调用Java动态方法并返回int类型结果
jlong CallLongMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回long类型结果 该函数调用Java动态方法并返回long类型结果
jfloat CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: Java方法ID,…: 可变参数 调用Java动态方法并返回float类型结果 该函数调用Java动态方法并返回float类型结果
jdouble CallDoubleMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: 方法ID,…: 方法参数 调用Java对象的方法 该函数调用Java对象的方法并返回双精度浮点型结果
void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID, …) env: JNI环境变量,obj: Java对象,methodID: 方法ID,…: 方法参数 调用Java对象的方法 该函数调用Java对象的方法并返回void

异常处理

JNI API 参数列表 参数说明 函数说明
jthrowable ExceptionOccurred(JNIEnv *env) env: JNI环境变量 判断是否有异常发生 如果有异常发生,返回异常对象
void ExceptionDescribe(JNIEnv *env) env: JNI环境变量 打印异常信息 如果有异常发生,该函数将异常信息打印到标准输出
void ExceptionClear(JNIEnv *env) env: JNI环境变量 清除异常 该函数清除当前线程中的异常
void ThrowNew(JNIEnv *env, jclass clazz, const char *msg) env: JNI环境变量,clazz: 异常类,msg: 异常信息 抛出异常 该函数抛出一个新的异常
jint Throw(JNIEnv *env, jthrowable obj) env: JNI环境变量,obj: 异常对象 抛出异常 该函数抛出一个异常
jint FatalError(JNIEnv *env, const char *msg) env: JNI环境变量,msg: 错误信息 抛出致命错误 该函数抛出一个致命错误并终止程序的运行
jint ThrowNew(JNIEnv *env, jclass clazz, const char *msg) env: JNI环境变量,clazz: 异常类,msg: 异常信息 抛出异常 该函数抛出一个新的异常并返回一个非零值表示成功
jint Throw(JNIEnv *env, jthrowable obj) env: JNI环境变量,obj: 异常对象 抛出异常 该函数抛出一个异常并返回一个非零值表示成功

JNI开发教程
https://blog.cikaros.top/doc/aaec9140.html
作者
Cikaros
发布于
2023年2月19日
许可协议