Как передать 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 с моей стороны.

Ответ 1

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

  • Сделайте класс на обеих сторонах Java и С++, просто вставив переменные-члены. Структуры С++ - это действительно просто классы с общедоступными элементами данных. Если вы действительно в чистом C, прекратите читать сейчас.
  • Используйте свои IDE (ы), чтобы автоматически создавать сеттеры и получатели для переменных-членов.
  • Используйте javah для создания файла заголовка C из класса Java.
  • Сделайте некоторое редактирование на стороне С++, чтобы сделать сеттеры и геттеры соответствующими сгенерированным заголовочным файлом.
  • Введите код JNI.

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