Как передать структуры C взад и вперед в Java-код в JNI?

у меня есть некоторые функции 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 сразу после нажатия createNewMyStruct(). Я немного новичок в JNI и понятия не имею, в чем проблема.

редактировать: я должен отметить, что код C очень ванильный C, хорошо протестирован и был перенесен из рабочего проекта iPhone. Кроме того, этот проект использует платформу Android NDK, которая позволяет запускать собственный код C из проекта Android из JNI. Однако Я не думайте, что это строго проблема NDK... это похоже на ошибку настройки/инициализации JNI с моей стороны.

4 ответов


нужно создать Java-класс с теми же элементами как C структуры, и "карта" их в код на C с помощью методов ОКР->GetIntField, ОКР->SetIntField, ОКР->GetFloatField, ОКР->SetFloatField, и так далее - короче, много ручного труда, надеюсь, уже существуют программы, которые делают это автоматически: JNAerator (http://code.google.com/p/jnaerator) и бухла (http://www.swig.org/). Оба имеют свои плюсы и минусы, выбор остается за вами.


это сбой, потому что Java_com_myorg_MyJavaClass_createNewMyStruct объявляется return jobject, но на самом деле возвращает struct MyStruct. Если вы запустили это с включенным CheckJNI, VM будет громко жаловаться и прерываться. Ваш processData() функция также будет довольно расстроена тем, что она получает arguments.

A jobject объект в управляемой куче. Он может иметь дополнительный материал до или после объявленных полей, и поля не должны быть выложены в памяти в определенном порядке. Так вы не можете сопоставить структуру C поверх класса Java.

самый простой способ справиться с этим был определен в более раннем ответе: манипулировать jobject с функциями JNI. Выделите объекты из Java или с помощью NewObject, Get/Set поля объекта с соответствующими вызовами.

существуют различные способы "накрутки" здесь. Например, вы можете включить byte[] в вашем объекте Java, который содержит sizeof(struct MyStruct) байты, а затем используйте GetByteArrayElements получить указатель на он. Немного уродливо, особенно если вы хотите получить доступ к полям со стороны Java.


структура C представляет собой набор переменных (некоторые из них являются указателем функции). Переход на java-не очень хорошая идея. В общем, проблема в том, как передать более сложный тип java, например pointer.

в книге JNI рекомендуется сохранить указатель/структуру в native и экспортировать манипуляции на java. Вы можете прочитать несколько полезных статей. руководство и спецификация программиста родного интерфейса JavaTM, Я читал. 9.5 Сверстников Классы есть решение справиться с этим.


  1. сделайте класс на обеих сторонах Java и c++, просто поместив переменные-члены. Структуры C++ - это просто классы с открытыми членами данных. Если вы действительно в чистом C, прекратите читать теперь.
  2. используйте IDE(Ы), чтобы автоматически создавать сеттеры и геттеры для переменных-членов.
  3. используйте javah для создания файла заголовка C из класса Java.
  4. сделайте некоторое редактирование на стороне C++, чтобы сеттеры и геттеры соответствовали сгенерированному заголовку файл.
  5. введите код JNI.

это не идеальное решение, но это может сэкономить вам немного времени, и это, по крайней мере, даст вам скелет, который вы можете редактировать. Эта функциональность может быть добавлена в IDE, но без большого спроса это, вероятно, не произойдет. Большинство IDE даже не поддерживают смешанные языковые проекты, не говоря уже о том, чтобы они разговаривали друг с другом.