0%

JNI 基本语法

JNI (Java Native Interface) 是 Java 提供的一个接口,它允许 Java 代码与其他语言(通常是 C 或 C++)编写的本地代码进行交互。通过 JNI,Java 程序可以调用本地代码中的函数,或者本地代码可以调用 Java 程序中的方法。

以下是 JNI 的基本语法整理,包括常用的定义、声明、调用方法等。

1. 声明 JNI 函数

JNI 中的本地方法是通过 native 关键字来声明的。Java 类中的本地方法通常是以 native 修饰的方法,表示该方法是由本地代码实现的。

Java 代码中的 JNI 声明:

1
2
3
4
5
6
7
8
9
public class MyClass {
// 声明一个本地方法
public native void myNativeMethod();

static {
// 加载本地库
System.loadLibrary("myNativeLib");
}
}
  • native 关键字:指明该方法是由本地代码提供实现的。
  • System.loadLibrary("myNativeLib"):加载本地库 myNativeLib,该库中包含本地方法的实现。

2. 生成头文件

在 Java 中声明了本地方法后,需要生成与本地代码对应的 C/C++ 头文件。可以使用 javah 工具来生成该头文件:

1
2
javac MyClass.java         # 编译 Java 类
javah MyClass # 生成头文件 MyClass.h

或者如果使用较新的 JDK 版本,可以使用 javac -h 来直接生成头文件:

1
javac -h . MyClass.java    # 在当前目录生成 MyClass.h 头文件

3. C/C++ 中的本地方法实现

生成的 C/C++ 头文件会包含 Java 类和本地方法的声明。然后,在本地代码中实现这些方法。

C/C++ 代码中的 JNI 实现:

1
2
3
4
5
6
7
8
#include <jni.h>
#include "MyClass.h" // 生成的头文件

// 实现 Java MyClass 中的 myNativeMethod 方法
JNIEXPORT void JNICALL Java_MyClass_myNativeMethod(JNIEnv *env, jobject obj) {
// 本地方法的实现
printf("Hello from Native Code!\n");
}
  • JNIEXPORTJNICALL 是 JNI 约定的宏,用于指定函数的导出和调用约定。
  • JNIEnv *env:指向 JNI 环境的指针,提供了与 Java 虚拟机交互的接口。
  • jobject obj:表示调用本地方法的 Java 对象的引用。

4. JNI 数据类型映射

Java 和 C/C++ 之间的数据类型有对应的映射关系,以下是常见的映射:

Java 类型 JNI 类型
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble
String jstring
Object jobject
Array jarray

例如:

1
2
3
4
JNIEXPORT void JNICALL Java_MyClass_myMethod(JNIEnv *env, jobject obj) {
jint a = 5; // Java int -> jint
jstring str = (*env)->NewStringUTF(env, "Hello, JNI!"); // Java String -> jstring
}

5. 调用 Java 方法(从本地代码调用 Java)

在本地代码中,可以通过 JNIEnv 来调用 Java 方法。

1
2
3
4
5
JNIEXPORT void JNICALL Java_MyClass_callJavaMethod(JNIEnv *env, jobject obj) {
jclass clazz = (*env)->GetObjectClass(env, obj); // 获取 Java 类
jmethodID methodID = (*env)->GetMethodID(env, clazz, "javaMethod", "()V"); // 获取方法 ID
(*env)->CallVoidMethod(env, obj, methodID); // 调用 Java 方法
}
  • GetObjectClass:获取 Java 对象的类引用。
  • GetMethodID:获取 Java 方法的 ID,第二个参数为方法名,第三个参数为方法签名。
  • CallVoidMethod:调用无返回值的 Java 方法(可以替换成 CallIntMethodCallObjectMethod 等,取决于方法的返回类型)。

6. 处理 Java 异常

JNI 允许本地代码在调用 Java 方法时抛出异常。可以通过 JNIEnv 提供的函数来检测和处理 Java 异常。

1
2
3
4
5
if ((*env)->ExceptionOccurred(env)) {
// 处理异常
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
  • ExceptionOccurred:检查是否发生异常。
  • ExceptionDescribe:打印异常信息。
  • ExceptionClear:清除异常状态。

7. 加载和链接本地库

在 Android 或普通 Java 应用中,加载本地库的方式如下:

1
2
3
static {
System.loadLibrary("native-lib"); // 加载本地库
}

本地库 native-lib 是包含 C/C++ 代码实现的动态链接库(通常为 .so 文件)。