第3课_JNI的数据传递机制
热度🔥:19 免费课程
授课语音
JNI 实现数据传递
在 Java 和本地代码(C/C++)之间进行数据传递时,JNI(Java Native Interface)提供了一套强大的机制来处理各种类型的数据。通过 JNI,Java 可以调用本地 C/C++ 方法并传递参数,反之,本地代码也可以访问和修改 Java 对象的数据。以下是如何在 JNI 中实现不同类型的数据传递的基本方法。
1. 基本数据类型传递
Java 中声明的 native
方法:
public native int add(int a, int b);
C/C++ 中的实现:
#include <jni.h>
JNIEXPORT jint JNICALL
Java_com_example_MyNativeClass_add(JNIEnv *env, jobject obj, jint a, jint b) {
return a + b; // 返回加法结果
}
解释:
jint
对应 Java 的int
类型。JNIEnv *env
是指向 JNI 环境的指针,用于调用 JNI 提供的函数。jobject obj
是调用该方法的 Java 对象的引用。
2. 传递字符串(String)
在 JNI 中,Java 的 String
类型被表示为 jstring
。Java 字符串和 C 字符串的内存布局不同,因此需要使用 JNI 提供的函数来进行转换。
Java 中声明的 native
方法:
public native String getStringFromNative();
C/C++ 中的实现:
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_MyNativeClass_getStringFromNative(JNIEnv *env, jobject obj) {
const char *nativeString = "Hello from Native";
return (*env)->NewStringUTF(env, nativeString); // 将 C 字符串转化为 Java 字符串
}
解释:
(*env)->NewStringUTF(env, nativeString)
:将 C 字符串nativeString
转换为 Java 字符串并返回。jstring
是 Java 字符串的 JNI 类型。
3. 传递数组
在 JNI 中,数组的类型有很多种,取决于数组中的元素类型。常见的有:
jintArray
:Javaint[]
数组jdoubleArray
:Javadouble[]
数组jbyteArray
:Javabyte[]
数组
Java 中声明的 native
方法:
public native int[] multiplyArray(int[] array);
C/C++ 中的实现:
#include <jni.h>
JNIEXPORT jintArray JNICALL
Java_com_example_MyNativeClass_multiplyArray(JNIEnv *env, jobject obj, jintArray array) {
jsize length = (*env)->GetArrayLength(env, array); // 获取数组长度
jint *elements = (*env)->GetIntArrayElements(env, array, NULL); // 获取数组元素
// 进行数组元素操作
for (int i = 0; i < length; i++) {
elements[i] *= 2; // 将数组每个元素乘以2
}
// 使用原数组元素返回新的数组
jintArray result = (*env)->NewIntArray(env, length); // 创建新数组
(*env)->SetIntArrayRegion(env, result, 0, length, elements); // 将修改后的数据放入新数组
// 释放元素引用
(*env)->ReleaseIntArrayElements(env, array, elements, 0); // 释放内存
return result; // 返回新数组
}
解释:
(*env)->GetArrayLength(env, array)
:获取 Java 数组的长度。(*env)->GetIntArrayElements(env, array, NULL)
:获取数组的元素并返回指向这些元素的指针。(*env)->NewIntArray(env, length)
:创建一个新的int[]
数组。(*env)->SetIntArrayRegion(env, result, 0, length, elements)
:将修改后的元素设置回新数组。
4. 传递对象
JNI 允许在 Java 和本地代码之间传递对象。通过 jobject
类型,可以访问传递给本地方法的 Java 对象。
Java 中声明的 native
方法:
public native void modifyPerson(Person p);
C/C++ 中的实现:
#include <jni.h>
JNIEXPORT void JNICALL
Java_com_example_MyNativeClass_modifyPerson(JNIEnv *env, jobject obj, jobject person) {
// 获取 Person 类的 class 引用
jclass personClass = (*env)->GetObjectClass(env, person);
// 获取 Person 类中的 name 字段 ID
jfieldID nameFieldID = (*env)->GetFieldID(env, personClass, "name", "Ljava/lang/String;");
// 获取 name 字段的值
jstring name = (*env)->GetObjectField(env, person, nameFieldID);
// 修改 name 字段
const char *newName = "Updated Name";
jstring newNameStr = (*env)->NewStringUTF(env, newName);
(*env)->SetObjectField(env, person, nameFieldID, newNameStr);
}
解释:
jobject person
:本地代码可以操作传递给它的Person
对象。(*env)->GetObjectClass(env, person)
:获取对象的类引用。(*env)->GetFieldID(env, personClass, "name", "Ljava/lang/String;")
:获取Person
类中name
字段的 ID。(*env)->GetObjectField(env, person, nameFieldID)
:获取字段的值。(*env)->SetObjectField(env, person, nameFieldID, newNameStr)
:修改对象的字段值。
5. 异常处理
在 JNI 中,我们可以捕获和处理 Java 异常。通过 JNIEnv
,我们可以查询是否发生了异常,并进行相应的处理。
示例:
JNIEXPORT void JNICALL
Java_com_example_MyNativeClass_triggerException(JNIEnv *env, jobject obj) {
jclass exceptionClass = (*env)->FindClass(env, "java/lang/Exception");
if (exceptionClass != NULL) {
(*env)->ThrowNew(env, exceptionClass, "An error occurred in native code");
}
}
解释:
(*env)->FindClass(env, "java/lang/Exception")
:查找Exception
类。(*env)->ThrowNew(env, exceptionClass, "An error occurred in native code")
:抛出一个新的 Java 异常。
总结
在 JNI 中,Java 和本地代码之间的数据传递需要使用 JNI 提供的特定数据类型和函数。常见的传递类型包括基本数据类型、字符串、数组、对象等。JNI 通过 JNIEnv
提供了一些接口来访问和修改 Java 对象或数据结构,从而实现 Java 与本地代码之间的数据传递。