如何在 JNI 中将 C 结构来回传递给 Java 代码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3923299/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to pass C structs back and forth to Java code in JNI?
提问by Nik Reiman
I've got some C functions which I am calling through JNI which take a pointer to a structure, and some other functions which will allocate/free a pointer to the same type of structure so that it is a bit easier to deal with my wrapper. Surprisingly, the JNI documentation says very little about how to deal with C structures.
我有一些我通过 JNI 调用的 C 函数,它们接受一个指向结构的指针,还有一些其他函数将分配/释放一个指向相同类型结构的指针,以便处理我的包装器更容易. 令人惊讶的是,JNI 文档很少提及如何处理 C 结构。
My C header file looks like so:
我的 C 头文件如下所示:
typedef struct _MyStruct {
float member;
} MyStruct;
MyStruct* createNewMyStruct();
void processData(int *data, int numObjects, MyStruct *arguments);
The corresponding JNI C wrapper file contains:
相应的 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);
}
...and finally, the corresponding Java class:
...最后是相应的 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);
}
}
Unfortunately, this code crashes the JVM right after hitting createNewMyStruct()
. I'm a bit new to JNI and have no idea what the problem could be.
不幸的是,此代码在点击 后立即使 JVM 崩溃createNewMyStruct()
。我对 JNI 有点陌生,不知道可能是什么问题。
Edit: I should note that the C code is very vanilla C, is well-tested and was ported from a working iPhone project. Also, this project is using the Android NDK framework, which lets you run native C code from an Android project from within JNI. However, I don't think that this is strictly an NDK issue... it seems like a JNI setup/initialization error on my part.
编辑:我应该注意到 C 代码是非常普通的 C,经过充分测试并且是从一个有效的 iPhone 项目移植过来的。此外,该项目使用了 Android NDK 框架,该框架允许您从 JNI 内的 Android 项目运行本机 C 代码。但是,我不认为这严格来说是 NDK 问题……对我而言,这似乎是 JNI 设置/初始化错误。
采纳答案by iirekm
You need to create a Java class with the same members as C struct, and 'map' them in the C code via methods env->GetIntField, env->SetIntField, env->GetFloatField, env->SetFloatField, and so on - in short, lots of manual labor, hopefully there already exist programs that do it automatically: JNAerator (http://code.google.com/p/jnaerator) and SWIG (http://www.swig.org/). Both have their pros and cons, the choice is up to you.
您需要创建一个与 C 结构具有相同成员的 Java 类,并通过方法 env->GetIntField、env->SetIntField、env->GetFloatField、env->SetFloatField 等在 C 代码中“映射”它们 -简而言之,大量的体力劳动,希望已经存在自动完成的程序:JNAerator ( http://code.google.com/p/jnaerator) 和 SWIG ( http://www.swig.org/)。两者各有优缺点,选择权在你。
回答by fadden
It's crashing because Java_com_myorg_MyJavaClass_createNewMyStruct
is declared to return jobject
, but is actually returning struct MyStruct
. If you ran this with CheckJNI enabled, the VM would complain loudly and abort. Your processData()
function is also going to be fairly upset about what it gets handed in arguments
.
它崩溃了,因为Java_com_myorg_MyJavaClass_createNewMyStruct
被声明为 return jobject
,但实际上正在返回 struct MyStruct
。如果你在启用 CheckJNI 的情况下运行它,VM 会大声抱怨并中止。您的processData()
职能部门也会对提交的内容感到相当不安arguments
。
A jobject
is an object on the managed heap. It can have extra stuff before or after the declared fields, and the fields don't have to be laid out in memory in any particular order. So you can't map a C struct on top of a Java class.
Ajobject
是托管堆上的对象。它可以在声明的字段之前或之后有额外的东西,并且这些字段不必以任何特定的顺序排列在内存中。所以你不能在 Java 类之上映射 C 结构。
The most straightforward way to deal with this was identified in an earlier answer: manipulate the jobject
with JNI functions. Allocate the objects from Java or with NewObject
, Get
/Set
the object fields with appropriate calls.
处理此问题的最直接方法已在较早的答案中确定:jobject
使用 JNI 函数操作。从 Java 或使用NewObject
, Get
/Set
适当调用的对象字段分配对象。
There are various ways to "cheat" here. For example, you could include a byte[]
in your Java object that holds sizeof(struct MyStruct)
bytes and then use GetByteArrayElements
to get a pointer to it. A bit ugly, especially if you want to access the fields from the Java side as well.
这里有各种各样的“作弊”方法。例如,您可以byte[]
在包含sizeof(struct MyStruct)
字节的Java 对象中包含 a ,然后使用它GetByteArrayElements
来获取指向它的指针。有点难看,特别是如果您还想从 Java 端访问字段。
回答by qrtt1
C structure is the collection of variables (some are function pointer). Pass to java is not a good idea. In general, it is the problem how to pass more complex type to java, like pointer.
C 结构体是变量的集合(有些是函数指针)。传递给 java 不是一个好主意。一般来说,问题是如何将更复杂的类型传递给 java,比如指针。
In JNI book, to keep the pointer/structure in native and export manipulation to java is recommended. You can read some useful articles. The JavaTM Native Interface Programmer's Guide and Specification,I have read. 9.5 Peer Classeshave a solution to deal with it.
在 JNI 书中,建议将指针/结构保持在本机并将操作导出到 java。您可以阅读一些有用的文章。JavaTM 本机接口程序员指南和规范,我已阅读。9.5 Peer Classes有解决方案。
回答by Bill
- Make the class on both the Java and C++ sides, just putting in the member variables. C++ structs are really just classes with public data members. If you are really in pure C, stop reading now.
- Use your IDE(s) to make setters and getters for the member variables automatically.
- Use javah to generate the C header file from the Java class.
- Do some editing on the C++ side to make the setters and getters match the generated header file.
- Put in the JNI code.
- 在 Java 和 C++ 端创建类,只需放入成员变量。C++ 结构实际上只是具有公共数据成员的类。如果你真的是纯 C 语言,现在停止阅读。
- 使用您的 IDE 自动为成员变量创建 setter 和 getter。
- 使用 javah 从 Java 类生成 C 头文件。
- 在 C++ 端做一些编辑,使 setter 和 getter 匹配生成的头文件。
- 放入JNI代码。
This is not an ideal solution, but it may save you a little time, and it will at least give you a skeleton that you can edit. This functionality could be added to an IDE, but without a big demand, it probably won't happen. Most IDEs don't even support mixed language projects, let alone having them talk to each other.
这不是一个理想的解决方案,但它可能会为您节省一点时间,并且至少会给您一个可以编辑的骨架。这个功能可以添加到 IDE 中,但如果没有很大的需求,它可能不会发生。大多数 IDE 甚至不支持混合语言项目,更不用说让它们相互通信了。