Как передать структуры 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 и понятия не имею, в чем может быть проблема.

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

4 59

4 ответа:

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

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

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

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

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

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

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

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

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