JNI 本地方法调用 Java 静态方法 和 实例方法对比;通过本地方法创建 Java 对象;本地方法访问 Java 数组元素;本地方法错误返回给 Java
以下是针对 Java JNI 的详细代码示例和对比分析:
一、调用 Java 静态方法 vs 实例方法
Java 示例类
public class JNIExample {
public static void staticMethod(int value) {
System.out.println("Static Method: " + value);
}
public void instanceMethod(String msg) {
System.out.println("Instance Method: " + msg);
}
}
JNI 实现代码
extern "C" JNIEXPORT void JNICALL Java_JNIExample_callMethods(JNIEnv *env, jobject obj) {
// 1. 调用静态方法
jclass cls = env->GetObjectClass(obj); // 或直接 FindClass("JNIExample")
jmethodID staticMid = env->GetStaticMethodID(cls, "staticMethod", "(I)V");
env->CallStaticVoidMethod(cls, staticMid, 42); // 参数为 int 42
// 2. 调用实例方法
jmethodID instanceMid = env->GetMethodID(cls, "instanceMethod", "(Ljava/lang/String;)V");
jstring jmsg = env->NewStringUTF("Hello from JNI");
env->CallVoidMethod(obj, instanceMid, jmsg); // 需要对象实例 obj
}
对比表格
对比项 | 静态方法调用 | 实例方法调用 |
---|---|---|
方法ID获取 | GetStaticMethodID | GetMethodID |
调用函数 | CallStaticVoidMethod 等 | CallVoidMethod 等 |
参数 | 需要类对象(cls ) | 需要对象实例(obj ) |
适用场景 | 调用类的静态方法 | 调用特定对象的实例方法 |
注意事项 | 无需对象实例 | 必须确保对象实例有效 |
二、通过本地方法创建 Java 对象
Java 类(带构造器)
public class Person {
public String name;
public Person(String name) {
this.name = name;
}
}
JNI 实现代码
extern "C" JNIEXPORT jobject JNICALL Java_JNIExample_createObject(JNIEnv *env, jobject obj) {
// 1. 获取类对象和构造方法ID
jclass personCls = env->FindClass("Person");
jmethodID constructor = env->GetMethodID(personCls, "<init>", "(Ljava/lang/String;)V");
// 2. 创建新对象并调用构造器
jstring jname = env->NewStringUTF("Alice");
jobject personObj = env->NewObject(personCls, constructor, jname);
return personObj; // 返回新对象给Java层
}
三、访问 Java 数组元素
Java 示例
public class JNIArrayExample {
public native void processArray(int[] arr);
}
JNI 实现代码
extern "C" JNIEXPORT void JNICALL Java_JNIArrayExample_processArray(JNIEnv *env, jobject obj, jintArray arr) {
// 1. 获取数组长度和元素指针
jsize length = env->GetArrayLength(arr);
jint* elements = env->GetIntArrayElements(arr, nullptr);
// 2. 访问/修改元素(示例:打印所有元素)
for (jsize i = 0; i < length; i++) {
printf("Element %d: %d\n", i, elements[i]);
}
// 3. 释放资源(避免内存泄漏)
env->ReleaseIntArrayElements(arr, elements, 0); // 0表示不复制回Java数组
}
四、JNI 异常处理(错误返回给 Java)
Java 示例
public class JNIExceptionExample {
public native void riskyMethod();
}
JNI 实现代码
extern "C" JNIEXPORT void JNICALL Java_JNIExceptionExample_riskyMethod(JNIEnv *env, jobject obj) {
// 1. 检查异常发生
if (/* 某些错误条件 */) {
// 2. 抛出Java异常(需提前注册异常类)
jclass exceptionCls = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(exceptionCls, "JNI Error: Something went wrong!");
}
}
Java 异常捕获
try {
JNIExceptionExample example = new JNIExceptionExample();
example.riskyMethod();
} catch (RuntimeException e) {
System.out.println("Caught exception: " + e.getMessage());
}
关键总结
操作类型 | JNI 函数/方法 | 注意事项 |
---|---|---|
调用静态方法 | GetStaticMethodID + CallStatic... | 需要类对象 cls |
调用实例方法 | GetMethodID + Call... | 需要对象实例 obj |
创建对象 | GetMethodID (构造器) + NewObject | 构造器方法名是 <init> |
访问数组 | GetIntArrayElements 等 | 必须调用 Release... 避免内存泄漏 |
抛出异常 | ThrowNew | 需要提前查找异常类 FindClass |
通过以上示例和对比,可以清晰理解 JNI 中不同操作的实现方式和关键差异。