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


68

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

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

编辑:我应该注意,C代码非常原始,经过了充分的测试,并且是从运行中的iPhone项目移植而来的。另外,该项目使用的是Android NDK框架,该框架使您可以从JNI中的Android项目运行本机C代码。但是,我认为这并不是严格意义上的NDK问题……我看来这似乎是JNI安装/初始化错误。


关于错误的更具体的信息?有任何错误讯息吗?
Petar Minchev 2010年

不,它只是使JRE崩溃。这是与JNI打交道的一个陷阱...
Nik Reiman 2010年

1
CheckJNI功能已添加,可以在常见的代码错误转变为Heisenbugs之前发现它们。默认情况下,它在模拟器中处于启用状态。有关在设备上启用它的信息,请参见android.git.kernel.org/?p=platform/…上的jni-tips和Embedded-vm-control文档。
2010年

看一下JNA(另请参阅参考资料部分)。
dma_k 2012年

您还可以通过JNI传递nio.DirectByteBuffers并使用例如github.com/marc-christian-schulze/structs4java
Marc-Christian Schulze

Answers:


42

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


15
您能粘贴一些这种集成的简单代码示例吗?
Centurion

4
@Centurion我知道网址时一般不赞成,但是这是一个评论,我发现这个网页非常有帮助在搞清楚了这东西出来:www3.ntu.edu.sg/home/ehchua/programming/java/...
詹姆斯

10

由于Java_com_myorg_MyJavaClass_createNewMyStruct声明要返回jobject而崩溃,但实际上是在返回struct MyStruct。如果在启用CheckJNI的情况下运行此程序,则VM将大声抱怨并中止。您的processData()功能也将对其交付的内容感到不安arguments

一种 jobject是托管堆上的对象。它可以在声明的字段之前或之后包含其他内容,并且不必按任何特定顺序将这些字段布置在内存中。因此,您不能将C结构映射到Java类的顶部。

解决此问题的最直接方法是在较早的答案中:jobject使用JNI函数进行操作。分配从Java或对象NewObjectGet/Set用适当的调用该对象的字段。

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


7

C结构是变量的集合(有些是函数指针)。传递给Java不是一个好主意。通常,如何将更复杂的类型(如指针)传递给java是一个问题。

在JNI书中,建议将指针/结构保持在本机状态,并将输出导出到java。您可以阅读一些有用的文章。我已经阅读了JavaTM Native Interface程序员指南和规范9.5对等类具有解决方案。


4
链接已死-看起来Oracle删除了内容。有几个网站有镜子,或者您可以使用archive.org:web.archive.org/web/20070101174413/http://java.sun.com/docs/...
法登

0
  1. 仅在成员变量中放入Java和C ++方面的类。C ++结构实际上只是具有公共数据成员的类。如果您真的是纯C语言,请立即停止阅读。
  2. 使用您的IDE自动为成员变量创建setter和getter。
  3. 使用javah从Java类生成C头文件。
  4. 在C ++端进行一些编辑,以使setter和getter与生成的头文件匹配。
  5. 放入JNI代码。

这不是理想的解决方案,但是它可以节省您一些时间,并且至少会为您提供一个可以编辑的框架。该功能可以添加到IDE中,但是没有很大的需求,它可能不会发生。大多数IDE甚至都不支持混合语言项目,更不用说让它们彼此对话了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.