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

时间:2010-10-13 11:53:01

标签: java c java-native-interface android-ndk

我有一些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);
  }
}

不幸的是,这个代码在点击createNewMyStruct()后立即崩溃了JVM。我对JNI有点新意,不知道问题可能是什么。

编辑:我应该注意到C代码非常香草C,经过充分测试并从一个正在运行的iPhone项目移植过来。此外,该项目使用的是Android NDK框架,它允许您从JNI内部的Android项目中运行本机C代码。但是,我认为这不是严格意义上的NDK问题......这似乎是我的JNI设置/初始化错误。

4 个答案:

答案 0 :(得分:40)

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

答案 1 :(得分:10)

崩溃是因为Java_com_myorg_MyJavaClass_createNewMyStruct被声明为返回jobject,但实际上正在返回struct MyStruct。如果您在启用CheckJNI的情况下运行此操作,则VM会大声抱怨并中止。您的processData()函数对于arguments传递的内容也会感到非常不满。

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

在前面的答案中确定了处理此问题的最简单方法:使用JNI函数操纵jobject。通过适当的调用从Java或对NewObjectGet / Set对象字段分配对象。

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

答案 2 :(得分:6)

C结构是变量的集合(有些是函数指针)。传递给java并不是一个好主意。一般来说,问题是如何将更复杂的类型传递给j​​ava,比如指针。

在JNI书中,建议将指针/结构保留在本机和导出操作中。你可以阅读一些有用的文章。 The JavaTM Native Interface Programmer's Guide and Specification,我读过。 9.5 Peer Classes有解决方案来处理它。

答案 3 :(得分:0)

  1. 在Java和C ++方面创建类,只需添加成员变量。 C ++结构实际上只是具有公共数据成员的类。如果你真的是纯C,请立即停止阅读。
  2. 使用您的IDE自动为成员变量创建setter和getter。
  3. 使用javah从Java类生成C头文件。
  4. 在C ++端进行一些编辑,使setter和getter与生成的头文件匹配。
  5. 输入JNI代码。
  6. 这不是一个理想的解决方案,但它可以为您节省一点时间,它至少会为您提供一个可以编辑的骨架。此功能可以添加到IDE中,但没有大的需求,它可能不会发生。大多数IDE甚至不支持混合语言项目,更不用说让它们相互交谈了。