JNIEnv *env 和 jobject obj

27 阅读3分钟

JNIEnv *env 和 jobject obj

JNIEnv *envjobject obj 是JNI函数中的两个重要参数,它们各自有不同的作用:

JNIEnv *env 参数

作用

JNIEnv 是JNI环境指针,提供了访问JVM功能的接口。

主要功能

  1. 调用Java方法

    • Call<type>Method系列函数调用“Java实例”的方法
    • CallStatic<type>Method系列函数调用Java静态方法
  2. 操作Java对象

    • 创建Java对象实例
    • 获取Java对象的类
  3. 异常处理

    • 抛出Java异常
    • 检查和处理JNI异常
  4. 字符串操作

    • Java字符串和C字符串之间的转换
    • NewStringUTFGetStringUTFChars
  5. 类型转换

    • 各种JNI类型之间的转换操作 以上都是通过访问JVM功能的接口来实现的。

示例用法

// 获取Java对象的类
jclass clazz = env->GetObjectClass(obj);

// 访问Java字段
jfieldID fieldId = env->GetFieldID(clazz, "fieldName", "I");
// 获取字段值
jint value = env->GetIntField(obj, fieldId);

jobject obj 参数

作用

代表调用该native方法的Java对象实例。

主要功能

  1. 访问调用对象

    • 可以访问调用native方法的那个Java对象
    • 获取和修改该对象的字段
  2. 调用对象方法

    • 调用该对象的其他Java方法
  3. 对象状态管理

    • 维护与特定Java对象实例关联的状态

示例用法

// 获取对象的类
jclass clazz = env->GetObjectClass(obj);

// 获取对象字段
jfieldID fieldId = env->GetFieldID(clazz, "result", "I");
jint fieldValue = env->GetIntField(obj, fieldId);

// 修改对象字段
env->SetIntField(obj, fieldId, newValue);

Java代码示例

// CalculatorWithField.java
public class CalculatorWithField {
    // 实例字段,用于存储计算结果
    private int lastResult = 0;
    
    // 静态字段,用于统计计算次数
    private static int calculationCount = 0;
    
    static {
        System.loadLibrary("calculator_field");
    }
    
    // 声明native方法
    public native int addWithField(int a, int b);
    public native int getCalculationCount();
    
    // 获取最后结果的Java方法
    public int getLastResult() {
        return lastResult;
    }
    
    public static void main(String[] args) {
        CalculatorWithField calc = new CalculatorWithField();
        
        System.out.println("初始lastResult: " + calc.getLastResult());
        System.out.println("初始calculationCount: " + calc.getCalculationCount());
        
        int result1 = calc.addWithField(10, 5);
        System.out.println("10 + 5 = " + result1);
        System.out.println("调用后lastResult: " + calc.getLastResult());
        System.out.println("调用后calculationCount: " + calc.getCalculationCount());
        
        int result2 = calc.addWithField(20, 30);
        System.out.println("20 + 30 = " + result2);
        System.out.println("再次调用后lastResult: " + calc.getLastResult());
        System.out.println("再次调用后calculationCount: " + calc.getCalculationCount());
    }
}

对应的JNI实现

// CalculatorWithFieldJNI.cpp
#include "CalculatorWithField.h"
#include <iostream>

JNIEXPORT jint JNICALL Java_CalculatorWithField_addWithField
  (JNIEnv *env, jobject obj, jint a, jint b) {
    
    // 1. 执行计算
    int result = a + b;
    std::cout << "执行加法: " << a << " + " << b << " = " << result << std::endl;
    
    // 2. 使用env和obj访问Java对象的字段
    // 获取对象的类
    jclass clazz = env->GetObjectClass(obj);
    
    // 获取实例字段ID
    jfieldID lastResultFieldID = env->GetFieldID(clazz, "lastResult", "I");
    
    // 设置实例字段值
    env->SetIntField(obj, lastResultFieldID, result);
    
    // 3. 访问和更新静态字段
    jfieldID countFieldID = env->GetStaticFieldID(clazz, "calculationCount", "I");
    jint currentCount = env->GetStaticIntField(clazz, countFieldID);
    env->SetStaticIntField(clazz, countFieldID, currentCount + 1);
    
    return result;
}

JNIEXPORT jint JNICALL Java_CalculatorWithField_getCalculationCount
  (JNIEnv *env, jobject obj) {
    
    // 获取对象的类
    jclass clazz = env->GetObjectClass(obj);
    
    // 获取静态字段值
    jfieldID countFieldID = env->GetStaticFieldID(clazz, "calculationCount", "I");
    return env->GetStaticIntField(clazz, countFieldID);
}

关键点说明

1. 访问实例字段

// 获取对象类
jclass clazz = env->GetObjectClass(obj);

// 获取字段ID
jfieldID lastResultFieldID = env->GetFieldID(clazz, "lastResult", "I");

// 设置字段值
env->SetIntField(obj, lastResultFieldID, result);

2. 访问静态字段

// 获取静态字段ID
jfieldID countFieldID = env->GetStaticFieldID(clazz, "calculationCount", "I");

// 获取和设置静态字段值
jint currentCount = env->GetStaticIntField(clazz, countFieldID);
env->SetStaticIntField(clazz, countFieldID, currentCount + 1);

3. 调用Java方法示例

// 如果需要调用Java方法
jmethodID methodID = env->GetMethodID(clazz, "getLastResult", "()I");
jint result = env->CallIntMethod(obj, methodID);

预期输出

初始lastResult: 0
初始calculationCount: 0
执行加法: 10 + 5 = 15
调用后lastResult: 15
调用后calculationCount: 1
执行加法: 20 + 30 = 50
再次调用后lastResult: 50
再次调用后calculationCount: 2