Передача списка / массива объектов ссылочного типа, содержащихся в классе, в JNI / C ++ - PullRequest
0 голосов
/ 22 января 2020

Я написал фрагмент кода, который манипулирует объектами ссылочного типа, содержащимися внутри класса, теперь у меня возникают проблемы при преобразовании этого экземпляра в список объектов.

Вот фрагмент кода;

 classes.java
=================
public class Leg {  
    public int id;
    public String name;
    // a few other primitive data types 
    ....
}    
public class Line {
    public int id;
    ....
    //public Leg mLeg; this worked well
    public List<Leg> mLegList; 
}
Driver.java
============
public native void setLine(Line jobj);

Line line = new Line();
line.id =200;

line.mLegList = new ArrayList<Leg>(1); // using only one for brevity     
Leg obj = new Leg(200, "test", ...);
line.mLegList.add(obj);

Native CPP function
==================
JNIEXPORT void JNICALL Java_Driver_setLine (JNIEnv * env, jobject jobj1, jobject jobj)
{
    jclass cls = env->GetObjectClass(jobj);
    if (cls == NULL)
        return;     
    jmethodID ctorID = env->GetMethodID(cls, "<init>","()V");
    if (!ctorID)
       return ;

    // I did for one instance like this 
    /*************************************************************
    *  jfieldID fid_legID = env->GetFieldID(cls, "mLeg", "LLeg;");
    *  if (!fid_legID)
    *     return;
    *  jobject legObject = env->GetObjectField(jobj, fid_legID);
    *  jclass clsObject = env->GetObjectClass(legObject);
    *  if (clsObject) {
    *   // Get all fields of Leg inside Line ....
    *************************************************************/
    // Now as i changed it to list/arraylist of Legs, it isn't Working
    jfieldID fid_legID = env->GetFieldID(cls, "mLegList", "[LLeg;");

    if (!fid_legID)
        env->ExceptionDescribe();

    // Exception in thread "main" java.lang.NoSuchFieldError: mLegList      
}

Мои вопросы:

  1. как заставить его работать со списком / ArrayList здесь. Мне нужно получить набор полей, содержащихся в этом списке.
  2. Почему в сигнатуру родной функции не входит jobjectarray (или что-то подобное для jobj1?) Это вызывает проблемы? Спасибо за прочтение поста.

Обновление Мне удалось построить объект успешно, так как при использовании Arraylist / List требуется использовать fieldID fid_legID = env->GetFieldID(cls, "mLegList", "Ljava/util/List;");. но теперь мне интересно, как перебрать этот список и прочитать все поля.

Ответы [ 2 ]

1 голос
/ 23 января 2020

Допустим, ваше дерево исходного кода выглядит следующим образом

|-- c
|   `-- Driver.c
|-- lib
|-- mypackage
|   |-- Driver.java
|   |-- Leg.java
|   `-- Line.java
`-- target

, а затем у вас есть следующие файлы:

mypackage / Driver. java

package mypackage;

import java.util.ArrayList;

public class Driver {

  static {
    System.loadLibrary("Driver");
  }

  public void list(ArrayList<Leg> list) {

  }

  public native void setLine(Line line);

  public static void main(String [] arg) {

    Driver d = new Driver();

    Line line = new Line();
    line.id   = 200;
    line.mLegList = new ArrayList<Leg>(1);

    Leg obj_1 = new Leg(200, "test_1");
    Leg obj_2 = new Leg(300, "test_2");
    Leg obj_3 = new Leg(400, "test_2");

    line.mLegList.add(obj_1);
    line.mLegList.add(obj_2);
    line.mLegList.add(obj_3);

    d.setLine( line );

  }
}

mypackage / Leg. java

package mypackage;

class Leg {
    public int id;
    public String name;

    public Leg(int id, String name) {
      this.id = id;
      this.name = name;
    }
}

mypackage / Line. java

package mypackage;

import java.util.List;

public class Line {
    public int id;
    public List<Leg> mLegList;
}

Вы можете получить доступ к элементам List следующим образом

c / Driver. c

#include "mypackage_Driver.h"

JNIEXPORT void JNICALL Java_mypackage_Driver_setLine(JNIEnv * env, jobject jobj, jobject line)
{
  jclass   cls_Line     = (*env)->GetObjectClass (env, line);

  jfieldID fid_mLegList = (*env)->GetFieldID (env, cls_Line, "mLegList", "Ljava/util/List;");

  jobject  list         = (*env)->GetObjectField (env, line, fid_mLegList);
  jclass   cls_list     = (*env)->GetObjectClass (env, list);

  int      listSize     = (*env)->GetArrayLength (env, list);

  for (int i = 0; i < listSize; i++) {

    jmethodID midGet   = (*env)->GetMethodID (env, cls_list, "get",
                                            "(I)Ljava/lang/Object;");

    jstring   obj      = (*env)->CallObjectMethod (env, list, midGet, i);

    jclass    cls_Leg  = (*env)->GetObjectClass(env, obj);

    jfieldID  fid_name = (*env)->GetFieldID( env, cls_Leg, "name", 
                                             "Ljava/lang/String;");

    jobject   name     = (*env)->GetObjectField (env, obj, fid_name);

    const char *c_string = (*env)->GetStringUTFChars (env, name, 0);

    printf ("[value] = %s\n", c_string);

    (*env)->ReleaseStringUTFChars (env, obj, c_string);
  }

}

Если вы хотите скомпилировать все вещи, вы можно использовать следующий скрипт

#!/bin/bash

mkdir -p target
mkdir -p lib

ARCH=`uname -s | tr '[:upper:]' '[:lower:]'`
EXT=

if [[ "${ARCH}" == "darwin" ]]; then
  EXT=dylib
else
  EXT=so
fi

echo $ARCH

javac -h c -d target mypackage/*.java

cc -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/${ARCH} c/Driver.c -o lib/libDriver.${EXT}

${JAVA_HOME}/bin/java -Djava.library.path=${LD_LIBRARY_PATH}:./lib -cp target mypackage.Driver

После его запуска вы получите то, что ищете:)

> ./compile.sh
darwin
[value] = test_1
[value] = test_2
[value] = test_2

Веселитесь с JNI:)

Обновление

  1. доступ к элементам массива с использованием get метод: recipeNo067
  2. доступ к элементам массива с использованием Iterator: recipeNo068
  3. передача ArrayList как Object []: recipeNo069

Использование

> git clone https://github.com/mkowsiak/jnicookbook.git
> export JAVA_HOME=your_JDK_installation
> cd jnicookbook/recipes/recipeNo067
> make
> make test
1 голос
/ 23 января 2020

Итерирование по List в JNI такое же, как и в Java, оно просто больше печатает.

jobject list = env->GetObjectField(line, fid_legID);

// Iterator iterator = list.iterator();
jclass cls_List = env->FindClass("java/util/List");
jmethodID mid_List_iterator = env->GetMethodID(cls_List, "iterator", "()Ljava/util/Iterator;");
jobject iterator = env->CallObjectMethod(list, mid_List_iterator);

jclass cls_Iterator = env->FindClass("java/util/Iterator");
jmethodID mid_Iterator_hasNext = env->GetMethodID(cls_Iterator, "hasNext", "()Z");
jmethodID mid_Iterator_next = env->GetMethodID(cls_Iterator, "next", "()Ljava/lang/Object");

while (true) {
    // if !iterator.hasNext() break
    jboolean hasNext = env->CallBooleanMethod(iterator, mid_Iterator_hasNext);
    if (!hasNext)
        break;

    jobject v = env->CallObjectMethod(iterator, mid_Iterator_next);
    // Do something with `v`

    // Avoid overflowing the local reference table if legList gets large
    env->DeleteLocalRef(v);
}

Обратите внимание, что в этом примере все еще требуется проверка ошибок.

...