JNI(Java Native Interface)是Java在本地语言与Java之间进行通信的桥梁。使用JNI,开发者可以通过Java调用C/C++代码,也可以通过C/C++调用Java代码。
一、原理:
1、Java 调用 C/C++ 函数的流程:
Java代码 -> JNI 接口函数 -> C/C++ 函数
2、C/C++ 调用 Java 方法的流程:
C/C++ 函数 -> JNI 接口函数 -> Java 方法
其中,JNI 接口函数是连接 Java 虚拟机和 native 代码的接口,Java虚拟机通过 JNI 接口函数与 native 代码进行交互。
二、JNI 接口函数
JNI 接口函数是 Java 和 native 代码之间的桥梁,分为两类:
1、本地方法声明函数
Java代码用 native 关键字声明本地方法,本地方法没有方法体。Java虚拟机在调用本地方法时会先执行本地方法声明函数。
例如:
public class NativeTest {
public native void callNativeMethod();
}
JNIEXPORT void JNICALL Java_NativeTest_callNativeMethod(JNIEnv *env, jobject obj) {
// TODO: 调用 native 函数的代码
}
2、JNI 函数
JNI 函数,是由 native 代码实现的较为具体的函数,Java虚拟机在调用 Java 与native互调函数时,就是去调用这些 JNI 函数。
三、JNI 开发步骤
1、在 Java 中声明 native 方法。
例如:
public class NativeTest {
public native void callNativeMethod();
}
2、Java代码执行 native 方法时会调用 JNI 接口函数,实现 JNI 接口函数。
JNIEXPORT void JNICALL Java_NativeTest_callNativeMethod(JNIEnv *env, jobject obj) {
// TODO: 调用 native 函数的代码
}
3、在 native 代码中实现 JNI 函数。
例如:
JNIEXPORT void JNICALL Java_NativeTest_callNativeMethod(JNIEnv *env, jobject obj) {
jclass clazz = (*env)->GetObjectClass(env, obj);
jmethodID methodId = (*env)->GetMethodID(env, clazz, "setName", "(Ljava/lang/String;)V");
jstring jstr = (*env)->NewStringUTF(env, "JNI");
(*env)->CallVoidMethod(env, obj, methodId, jstr);
}
四、线程管理
JNI在多线程环境下的使用需要注意线程安全。Java线程和native线程不是同一个线程,Java线程在进入native代码时,native线程会阻塞等待,直到Java线程退出native代码为止。
因此,在native代码中涉及到共享数据的操作时,必须加上同步控制。
五、异常处理
当native代码中发生异常时,Java虚拟机会抛出对应的Java异常。在 JNI 代码中需要调用相应的JNI函数处理异常,例如:
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), "native function error");
六、总结
JNI 提供了 Java 和 native 代码之间进行通信的桥梁,能够实现 Java 和 C/C++ 等本地语言的混合开发。在开发过程中,需要注意线程安全和异常处理,保证程序的可靠性。