JNI 访问Java的成员变量

在JNI中访问Java对象的属性,通常需要先获取属性的 jfieldID,然后使用相应的函数进行读写操作。

 

1、访问非静态属性

例1:

//Java代码
public
class MyClass { private int mValue; public MyClass(int value) { mValue = value; } }
#include <jni.h>
#include <iostream>
 
// 假设JNIEnv* env和jobject obj已经被正确设置
 
// 1. 加载Java类
jclass clazz = env->FindClass("com/example/MyClass");
 
// 2. 获取属性的jfieldID
jfieldID fieldID = env->GetFieldID(clazz, "mValue", "I");
 
// 3. 读取属性值
jint value = env->GetIntField(obj, fieldID);
std::cout << "Read value: " << value << std::endl;
 
// 4. 设置属性值
jint newValue = value + 1;
env->SetIntField(obj, fieldID, newValue);
std::cout << "New value set: " << newValue << std::endl;
 
// 5. 释放本地引用
env->DeleteLocalRef(clazz);

例2:

//Java代码
public String key = "key";
public native String accessField();
JNIEXPORT jstring JNICALL Java_JniMain_accessField(JNIEnv * env, jobject jobj) 
{
//1.获取jclasss jclass jclz = (*env)->GetObjectClass(env,jobj); //2.fieldId key:属性名称, Ljava/lang/String:属性签名 jfieldID fid = (*env)->GetFieldID(env,jclz,"key","Ljava/lang/String;"); //3.得到key 对应的值 //GetXXXField 因为String 是引用类型 所以使用GetObjectField jstring jstr = (*env)->GetObjectField(env,jobj,fid); //4.将jni的string 转换成C的char char * c_str = (*env)->GetStringUTFChars(env,jstr,NULL); //5.生成新的字符串 HBin key char text[30] = "HBin"; stract(text,c_str); //6.C->jni jstring new_str = (*env)->NewStringUTF(env,text); //7.使用set方法 (env)-SetObjectField(env,jobj,fid,new_str); //释放 (*env)->ReleaseStringChars(env,new_str,c_str); return new_str; }

 2、访问静态属性

例1:

//Java代码
public static int count = 9;
public native void accessStaticField();
//访问静态域还是使用jobject的参数 范围静态函数才是使用jclass的参数
JNIEXPORT void JNICALL Java_JniMain_accessStaticField(JNIEnv * env, jobject jobj) 
{
  //1.获取jclasss
  jclass jclz = (*env)->GetObjectClass(env, jobj);
                    
  //2.得到fieldId  count:属性名称, I:属性签名
  jfieldID fid = (*env)->GetFieldID(env, jclz, "count", "I");
        
  jint count = (*env)->GetStaticIntField(env, jclz, fid);
        
  //int 不需要转换可以直接使用
  count++;
        
  (env)->SetStaticIntField(env, jclass, fid, count);
  
}

 3、访问数组

//Java代码
public class MyClass {
    public int[] myIntArray;

    public MyClass(int[] array) {
        this.myIntArray = array;
    }
}

 

例1:

JNIEXPORT void JNICALL Java_com_example_MyClass_accessArray(JNIEnv *env, jobject obj) 
{
    //获取jclass
    jclass clz = env->GetObjectClass(obj);

    //获取成员变量的Id
    jfieldID fid = env->GetFieldID(clz, "myIntArray", "[I");
    jintArray jArray = env->GetObjectField(obj, fid);

    // 获取数组长度
    jsize len = env->GetArrayLength(jArray);

    //将JNI数组复制到C数组
    jint* buf = new jint[len];
    env->GetIntArrayRegion(jArray, 0, len, buf);

    //更新C数组
    buf[0] = 25;

    //将修改提交到JNI数组中
    env->SetIntArrayRegion(jArray, 0, len, buf);

    //释放C数组
    delete [] buf;
}

 

例2:

JNIEXPORT void JNICALL Java_com_example_MyClass_accessArray(JNIEnv *env, jobject obj) 
{
    //获取jclass
    jclass clz = env->GetObjectClass(obj);

    //获取成员变量的Id
    jfieldID fid = env->GetFieldID(clz, "myIntArray", "[I");
    jintArray jArray = env->GetObjectField(obj, fid);

    jboolean isCopy;
    //直接获取指针,C++指针
    jint* pArray = env->GetIntArrayElements(jArray, &isCopy);

    //更新C数组
    pArray[0] = 25;//释放数组
    //mode:0 - 将数组内容拷贝回JNI数组,并释放C数组
    //JNI_COMMIT - 将数组内容拷贝回JNI数组,但不释放C数组
    //JNI_ABORT - 不拷贝数组内容,只释放C数组
    env->ReleaseIntArrayElements(jArray , pArray, 0);
}

 

访问数组小结:

(1)GetIntArrayRegion:把Java的数组拷贝到 C++ 新建的数组内存区域中。

优点:C++中的改变不会影响Java中的数值

缺点:需要在C++中开辟额外内存空间。数组比较大时,复制元素可能会出现性能问题。

适用场景:适用于需要在C++中处理Java数组的特定区域数据,且不需要修改原始Java数组的场景。这种方式减少了内存分配和释放的开销。

 

(2)GetIntArrayElements:获取指向数组元素的直接指针,C++代码通过这个指针可以直接操作Java数组的数据。这个函数的最后一个参数是isCopy,让调用者确定返回的c指针是指向副本,还是指向堆中的固定对象。

优点:不用拷贝占用额外的内存,Java和C++中的改版是同步的。

缺点:

适用场景:适用于需要直接操作Java数组内容,且不需要在C++中修改原始Java数组的场景。如果需要在C++中修改数组并同步更新Java中的数据并释放掉C++指针,应使用ReleaseIntArrayElements方法并设置Release_mode为 0;

 

posted @ 2025-01-14 10:01  Plus301  阅读(77)  评论(0)    收藏  举报