如何在JNI中来回传递C结构到Java代码?

2022-08-31 14:03:03

我有一些C函数,我通过JNI调用它们,它们需要一个指向结构的指针,以及其他一些函数,它们将分配/释放指向相同类型结构的指针,以便更容易处理我的包装器。令人惊讶的是,JNI文档很少说明如何处理C结构。

我的 C 头文件如下所示:

typedef struct _MyStruct {
  float member;
} MyStruct;

MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);

相应的 JNI C 包装器文件包含:

JNIEXPORT jobject JNICALL
Java_com_myorg_MyJavaClass_createNewMyStruct(JNIEnv *env, jobject this) {
  return createNewMyStruct();
}

JNIEXPORT void JNICALL
Java_com_myorg_MyJavaClass_processData(JNIEnv *env, jobject this, jintArray data,
                                       jint numObjects, jobject arguments) {
  int *actualData = (*env)->GetIntArrayElements(env, data, NULL);
  processData(actualData, numObjects, arguments);
  (*env)->ReleaseIntArrayElements(env, data, actualData, NULL);
}

...最后,相应的 Java 类:

public class MyJavaClass {
  static { System.loadLibrary("MyJniLibrary"); }

  private native MyStruct createNewMyStruct();
  private native void processData(int[] data, int numObjects, MyStruct arguments);

  private class MyStruct {
    float member;
  }

  public void test() {
    MyStruct foo = createNewMyStruct();
    foo.member = 3.14159f;
    int[] testData = new int[10];
    processData(testData, 10, foo);
  }
}

不幸的是,此代码在点击后立即使JVM崩溃。我对JNI有点陌生,不知道问题可能是什么。createNewMyStruct()

编辑:我应该注意的是,C代码非常普通C,经过了充分的测试,并且是从一个工作的iPhone项目中移植过来的。此外,此项目还使用 Android NDK 框架,该框架允许您从 JNI 中运行 Android 项目中的本机 C 代码。但是,我不认为这完全是NDK问题...这似乎是我的JNI设置/初始化错误。


答案 1

你需要创建一个与C结构具有相同成员的Java类,并通过env->GetIntField,env->SetIntField,env->GetFloatField,env->SetFloatField等方法在C代码中“映射”它们 - 简而言之,大量的手工劳动,希望已经存在自动执行此操作的程序:JNAerator(http://code.google.com/p/jnaerator)和SWIG(http://www.swig.org/)。两者都有其优点和缺点,选择取决于您。


答案 2

它正在崩溃,因为被声明返回,但实际上正在返回结构。如果在启用 CheckJNI 的情况下运行此程序,VM 将大声抱怨并中止。您的函数也会对它所交的内容感到相当不安。Java_com_myorg_MyJavaClass_createNewMyStructjobjectMyStructprocessData()arguments

A 是托管堆上的一个对象。它可以在声明的字段之前或之后有额外的东西,并且字段不必以任何特定顺序在内存中布局。因此,您无法在 Java 类之上映射 C 结构。jobject

处理这个问题的最直接方法在前面的答案中已经确定:使用JNI函数操作。通过适当的调用从 Java 或 、 / 对象字段分配对象。jobjectNewObjectGetSet

这里有各种“作弊”的方法。例如,您可以在 Java 对象中包含一个保存字节的内容,然后用于获取指向它的指针。有点难看,特别是如果你想从Java端访问字段。byte[]sizeof(struct MyStruct)GetByteArrayElements


推荐