Как передать структуры C и обратно в код Java в JNI? - PullRequest
61 голосов
/ 13 октября 2010

У меня есть некоторые функции 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 ]

40 голосов
/ 13 октября 2010

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

10 голосов
/ 14 октября 2010

Это сбой, потому что 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.

6 голосов
/ 23 октября 2010

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

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

0 голосов
/ 08 мая 2013
  1. Создайте класс на стороне Java и C ++, просто добавив переменные-члены.Структуры C ++ на самом деле являются просто классами с открытыми членами данных.Если вы действительно находитесь в чистом C, прекратите чтение сейчас.
  2. Используйте ваши IDE, чтобы автоматически создавать установщики и получатели для переменных-членов.
  3. Используйте javah для создания файла заголовка Cкласс Java.
  4. Выполните некоторое редактирование на стороне C ++, чтобы заставить установщики и получатели соответствовать сгенерированному файлу заголовка.
  5. Вставьте код JNI.Это не идеальное решение, но оно может сэкономить вам немного времени и, по крайней мере, даст вам скелет, который вы можете редактировать.Эта функциональность может быть добавлена ​​в IDE, но без большого спроса это, вероятно, не произойдет.Большинство IDE даже не поддерживают проекты на разных языках, не говоря уже о том, чтобы они общались друг с другом.
...