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

如何在JNI中将C结构来回传递给Java代码?,java,c,java-native-interface,android-ndk,Java,C,Java Native Interface,Android Ndk,我有一些通过JNI调用的C函数,这些函数使用指向结构的指针,还有一些其他函数将分配/释放指向相同类型结构的指针,这样处理包装器就更容易了。令人惊讶的是,JNI文档很少涉及如何处理C结构 我的C头文件如下所示: typedef struct _MyStruct { float member; } MyStruct; MyStruct* createNewMyStruct(); void processData(int *data, int numObjects, MyStruct *argu

我有一些通过JNI调用的C函数,这些函数使用指向结构的指针,还有一些其他函数将分配/释放指向相同类型结构的指针,这样处理包装器就更容易了。令人惊讶的是,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项目移植而来的。此外,该项目使用的是AndroidNDK框架,它允许您从JNI内的Android项目运行本机C代码。然而,我不认为这是严格意义上的NDK问题。。。就我而言,这似乎是一个JNI设置/初始化错误。

您需要创建一个与C struct具有相同成员的Java类,并通过方法env->GetIntField、env->SetIntField、env->GetFloatField、env->SetFloatField等在C代码中“映射”它们-简而言之,需要大量的手工劳动,希望已经有一些程序可以自动完成这项工作:jnavigator()和SWIG()。两者都有各自的优点和缺点,选择取决于您。

它崩溃了,因为
Java\u com\u myorg\u MyJavaClass\u createNewMyStruct
声明返回
jobject
,但实际上返回struct
MyStruct
。如果在启用CheckJNI的情况下运行此操作,VM将大声抱怨并中止。您的
processData()
函数也会对它所交的
参数感到相当不安

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

处理这个问题最直接的方法是在前面的回答中确定的:使用JNI函数操纵
jobject
。从Java或使用
NewObject
Get
/
Set
通过适当的调用分配对象字段


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

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

在JNI书中,建议将指针/结构保持为本机,并将操作导出到java。你可以读一些有用的文章。我读过。有一个解决办法

在java和C++边上制作类,只需输入成员变量。C++结构实际上是公共数据成员的类。如果你真的是纯C语言,现在就停止阅读
  • 使用IDE自动生成成员变量的setter和getter
  • 使用javah从Java类生成C头文件
  • 在C++侧做一些编辑,使SET和GETTES与生成的头文件匹配。
  • 输入JNI代码

  • 这不是一个理想的解决方案,但它可能会节省您一点时间,而且它至少会为您提供一个可以编辑的框架。这个功能可以添加到IDE中,但是如果没有很大的需求,它可能不会实现。大多数IDE甚至不支持混合语言项目,更不用说让它们相互交流了。

    关于错误的更具体的信息?有错误消息吗?没有,它只是使JRE崩溃。这是处理JNI的一个陷阱……添加CheckJNI功能是为了在常见代码错误变成Heisenbug之前找到它们。默认情况下,它在emulator中启用。有关在设备上启用它的信息,请参阅的jni提示和嵌入式vm控制文档。查看JNA(和)。您还可以通过jni传递nio.DirectByteBuffers,并在Java端解析内存。例如,您可以粘贴一些此类集成的简单代码示例吗?@Centurion我知道URL通常不受欢迎,但这是一条评论,我发现这个网页非常有助于弄清楚这件事:链接死了——看起来甲骨文把内容取了下来。有几个站点有镜像,或者您可以使用archive.org: