JNI (Java Native Interface) 是 Java 提供的一个接口,它允许 Java 代码与其他语言(通常是 C 或 C++)编写的本地代码进行交互。通过 JNI,Java 程序可以调用本地代码中的函数,或者本地代码可以调用 Java 程序中的方法。
以下是 JNI 的基本语法整理,包括常用的定义、声明、调用方法等。
1. 声明 JNI 函数
JNI 中的本地方法是通过 native
关键字来声明的。Java 类中的本地方法通常是以 native
修饰的方法,表示该方法是由本地代码实现的。
Java 代码中的 JNI 声明:
1 | public class MyClass { |
native
关键字:指明该方法是由本地代码提供实现的。System.loadLibrary("myNativeLib")
:加载本地库myNativeLib
,该库中包含本地方法的实现。
2. 生成头文件
在 Java 中声明了本地方法后,需要生成与本地代码对应的 C/C++ 头文件。可以使用 javah
工具来生成该头文件:
1 | javac MyClass.java # 编译 Java 类 |
或者如果使用较新的 JDK 版本,可以使用 javac -h
来直接生成头文件:
1 | javac -h . MyClass.java # 在当前目录生成 MyClass.h 头文件 |
3. C/C++ 中的本地方法实现
生成的 C/C++ 头文件会包含 Java 类和本地方法的声明。然后,在本地代码中实现这些方法。
C/C++ 代码中的 JNI 实现:
1 |
|
JNIEXPORT
和JNICALL
是 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 | JNIEXPORT void JNICALL Java_MyClass_myMethod(JNIEnv *env, jobject obj) { |
5. 调用 Java 方法(从本地代码调用 Java)
在本地代码中,可以通过 JNIEnv
来调用 Java 方法。
1 | JNIEXPORT void JNICALL Java_MyClass_callJavaMethod(JNIEnv *env, jobject obj) { |
GetObjectClass
:获取 Java 对象的类引用。GetMethodID
:获取 Java 方法的 ID,第二个参数为方法名,第三个参数为方法签名。CallVoidMethod
:调用无返回值的 Java 方法(可以替换成CallIntMethod
、CallObjectMethod
等,取决于方法的返回类型)。
6. 处理 Java 异常
JNI 允许本地代码在调用 Java 方法时抛出异常。可以通过 JNIEnv
提供的函数来检测和处理 Java 异常。
1 | if ((*env)->ExceptionOccurred(env)) { |
ExceptionOccurred
:检查是否发生异常。ExceptionDescribe
:打印异常信息。ExceptionClear
:清除异常状态。
7. 加载和链接本地库
在 Android 或普通 Java 应用中,加载本地库的方式如下:
1 | static { |
本地库 native-lib
是包含 C/C++ 代码实现的动态链接库(通常为 .so
文件)。