Передача, возврат и преобразование в векторный список списков через JNI

Мне нужно перейти с Java

List< List<MyPoint> > points;

через jni в C++ и преобразовать в

std::vector< std::vector<MyPoint> >

Обработайте эти векторы и верните

List< List<MyPoint> >
  1. Как правильно передать и вернуть список списков?
  2. Как преобразовать список списков объектов в вектор векторов объектов и обратно?

person George    schedule 30.05.2012    source источник
comment
Пробовали ли вы Swig, особенно Swig с STL? Он сгенерирует для вас необходимый связующий код JNI.   -  person G. Martinek    schedule 30.05.2012
comment
Нет, использование Swig в этой задаче недопустимо — только стандартные средства.   -  person George    schedule 30.05.2012
comment
@WdF: Почему это неприемлемо? По крайней мере, вы можете попробовать Swig и посмотреть, какой код Java/JNI он генерирует. Все, что Swig делает автоматически, можно сделать и с помощью написанного от руки кода.   -  person In silico    schedule 02.06.2012
comment
Swig это не голый JNI - это дополнительный инструмент. Изначально используйте минимально необходимые сторонние инструменты.   -  person George    schedule 02.06.2012


Ответы (3)


Я решил эту проблему стандартными средствами.

  1. Создать в классе Java как объекты (O) контейнер (C)
  2. Передать массив объектов (O) из кода Java в нативную часть
  3. Создать из вектора массива в коде C++
  4. Вычислить новые векторы
  5. Создайте массив контейнеров (C) и вставьте в объекты (O)
  6. Возврат массива контейнеров (C)

Реализация кода:

В части Java:

1 — Создать массив из списка точек

В части С++:

2 — построить входной вектор

std::vector<CurvePoint> src_line;

jclass java_points_cls = env->FindClass("myPointClass");
jmethodID java_mid = env->GetMethodID(java_points_cls, "<init>", "(II)V");    
jfieldID fidX = env->GetFieldID(java_points_cls, "x", "I");
jfieldID fidY = env->GetFieldID(java_points_cls, "y", "I");

int srcCount = env->GetArrayLength(srcLines);

for (int i=0; i < srcCount; i++) 
{
    jobject cur_pnt =  env->GetObjectArrayElement(srcLines, i); 

    LinePoint src_point;        

    src_point.x = env->GetIntField(cur_pnt, fidX); 
    src_point.y = env->GetIntField(cur_pnt, fidY);    

    src_line.push_back(src_point);
}

3 – расчет lines

4 — построить выходной массив

jclass java_line_cls = env->FindClass("myLinesClass");

jmethodID java_line_add = env->GetMethodID(java_line_cls, "addPoint", "(II)V");  
jmethodID java_line_init = env->GetMethodID(java_line_cls, "<init>", "()V");

jobjectArray resLines = (jobjectArray) env->NewObjectArray(lines.size(),     java_line_cls, 0); 

for(int i = 0; i < lines.size(); ++i)
{
    jobject cur_line =  env->NewObject(java_line_cls, java_line_init);
    for(int j = 0; j < lines[i].size(); ++j)
        env->CallVoidMethod(cur_line, java_line_add, 
                                lines[i][j].x,
                                lines[i][j].y);
    env->SetObjectArrayElement(resLines, i, cur_line);
}

return resLines;

Java-часть

5 — Создать список строк из возвращенного массива

person George    schedule 07.06.2012
comment
Хорошо, чем это лучше, чем JavaCPP? - person Samuel Audet; 16.06.2012
comment
Должны использоваться только стандартные инструменты. Это требование к проекту. В другом случае может использоваться JavaCpp или SWIG. Я считаю JavaCpp лучшим вариантом) - person George; 26.06.2012
comment
А, понятно... Хотя использование вывода SWIG или JavaCPP в качестве справки все же может быть полезным, я думаю :) - person Samuel Audet; 26.06.2012
comment
Привет, @George, у меня тот же сценарий, я должен вернуться из JNI Vector‹ Vector‹Point› › указывает на класс java. Как я могу это сделать ? - person DcodeChef; 05.06.2014
comment
@DcodeChef, см. шаг (4). У меня есть класс myLinesClass в части Java. Я сделал массив экземпляров этого класса. И этот класс содержит массив точек. Нажимаю туда точки методом addPoint этого класса. Итак, у нас есть вектор‹Point› в экземпляре класса и вектор‹vector‹Point›› в массиве экземпляров. - person George; 16.06.2014
comment
@ Джордж Спасибо за ваш ответ. Если возможно, не могли бы вы включить код Java для myLinesClass? Для хранения точек (x,y) вы используете массивы с примитивами (например: int[n]) или списки (например: List<Integer>)? Мне трудно понять, как определить функцию addPoint: S - person Jaime Ivan Cervantes; 06.06.2017
comment
@ Хайме Иван Сервантес, к сожалению, я давно потерял этот код. Неважно, как вы реализуете хранение точек в классе Line. Так что это может быть просто еще один класс Point с полями x и y. При этом Line может иметь только одно поле с массивом Points и функцию addPoint, обеспечивающую добавление новой точки в массив. - person George; 13.06.2017

Насколько я понял из справочника JNI, JNI может работать только с одномерными массивами примитивных типов или объектов.

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

Пока проблема полностью не решена, т.к. JNI не может выделить память для существующего элемента в нативной части. Поэтому приходится извлекать данные частично, выделять под них память на стороне Java, и заливать нативную.

Возможным решением может быть использование связующих, таких как SWIG или JavaCpp.

person George    schedule 02.06.2012
comment
+1 для JavaCPP :) Кстати, мы можем делать в JNI все, что можно сделать в Java, это просто PITA и очень неэффективно. - person Samuel Audet; 02.06.2012
comment
@SamuelAudet хорошо работает с JavaCPP - обычно я работаю с SWIG, но JavaCPP действительно выглядит красиво. Надо смотреть глубже :) Спасибо! - person G. Martinek; 02.06.2012

Вы также можете использовать этот проект. Это позволит использовать java-классы с JNI как родной.

person Sergey Vystoropskyi    schedule 05.08.2012