【问题标题】:How to access arrays within an object with JNI?如何使用 JNI 访问对象内的数组?
【发布时间】:2026-02-11 13:10:02
【问题描述】:

JNI 教程,例如 this 一,很好地介绍了如何访问对象中的原始字段,以及如何访问作为显式函数参数提供的数组(即作为 jarray 的子类)。但是如何访问位于jobject 中的字段 的Java(原始)数组?例如,我想对以下 Java 对象的字节数组进行操作:

class JavaClass {
  ...
  int i;
  byte[] a;
}

主程序可能是这样的:

class Test {

  public static void main(String[] args) {
    JavaClass jc = new JavaClass();
    jc.a = new byte[100];
    ...
    process(jc);
  }

  public static native void process(JavaClass jc);
}

相应的 C++ 端将是:

JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {

  jclass jcClass = env->GetObjectClass(jc);
  jfieldID iId = env->GetFieldID(jcClass, "i", "I");

  // This way we can get and set the "i" field. Let's double it:
  jint i = env->GetIntField(jc, iId);
  env->SetIntField(jc, iId, i * 2);

  // The jfieldID of the "a" field (byte array) can be got like this:
  jfieldID aId = env->GetFieldID(jcClass, "a", "[B");

  // But how do we operate on the array???
}

我想使用GetByteArrayElements,但它想要一个ArrayType 作为它的参数。显然我错过了一些东西。有没有办法解决这个问题?

【问题讨论】:

    标签: java-native-interface


    【解决方案1】:

    希望对您有所帮助(也请查看JNI Struct reference):

    // Get the class
    jclass mvclass = env->GetObjectClass( *cls );
    // Get method ID for method getSomeDoubleArray that returns a double array
    jmethodID mid = env->GetMethodID( mvclass, "getSomeDoubleArray", "()[D");
    // Call the method, returns JObject (because Array is instance of Object)
    jobject mvdata = env->CallObjectMethod( *base, mid);
    // Cast it to a jdoublearray
    jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)
    // Get the elements (you probably have to fetch the length of the array as well
    double * data = env->GetDoubleArrayElements(*arr, NULL);
    // Don't forget to release it 
    env->ReleaseDoubleArrayElements(*arr, data, 0); 
    

    好的,我在这里使用方法而不是字段(我考虑调用 Java getter 清理器)但您可能也可以为字段重写它。不要忘记发布,如评论中所述,您可能仍需要获取长度。

    编辑:重写您的示例以获取它的字段。基本上用 GetObjectField 替换 CallObjectMethod。

    JNIEXPORT void JNICALL Java_Test_process(JNIEnv * env, jclass c, jobject jc) {
    
      jclass jcClass = env->GetObjectClass(jc);
      jfieldID iId = env->GetFieldID(jcClass, "i", "I");
    
      // This way we can get and set the "i" field. Let's double it:
      jint i = env->GetIntField(jc, iId);
      env->SetIntField(jc, iId, i * 2);
    
      // The jfieldID of the "a" field (byte array) can be got like this:
      jfieldID aId = env->GetFieldID(jcClass, "a", "[B");
    
      // Get the object field, returns JObject (because Array is instance of Object)
      jobject mvdata = env->GetObjectField (jc, aID);
    
      // Cast it to a jdoublearray
      jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)
    
      // Get the elements (you probably have to fetch the length of the array as well  
      double * data = env->GetDoubleArrayElements(*arr, NULL);
    
      // Don't forget to release it 
      env->ReleaseDoubleArrayElements(*arr, data, 0);
    }
    

    【讨论】:

    • 谢谢;使用吸气剂很聪明(甚至可能更清洁)。除非有人指出如何以类似 GetXXXField 的方式直接获取数组字段,否则我将不得不这样做。
    • 好的,我添加了该字段的示例(基本上只使用 GetObjectField 而不是 CallObjectMethod)。虽然我当然不能保证它会开箱即用,但我希望你能得到大致的想法:)
    • 对!不知何故,我期待找到一种更直接的方法来做到这一点,所以我不愿意回到定义(“数组是一个对象”:-) 编程心理学......再次感谢!
    • 我在其他答案中也看到了同样的情况。为什么double * data?为什么不jdouble * data
    • 为什么使用 reinterpret_cast 而不是静态转换?
    【解决方案2】:

    在 gcc 6.3 中,我收到一条警告,说“取消引用类型双关指针会破坏严格的别名规则”,如下所示:

    jdoubleArray arr = *reinterpret_cast<jdoubleArray*>(&mvdata);
    

    但是由于 jdoubleArray 本身是一个指向类 _jdoubleArray 的指针,所以在转换之前不需要获取地址,并且这个 static_cast 可以在没有警告的情况下工作:

    jdoubleArray arr = static_cast<jdoubleArray>(mvdata);
    

    【讨论】: