Reputation: 407
I wrote a piece of code that manipulates reference type objects contained inside a class, now am having trouble while converting this instance to a list of objects.
Here is the code snippet;
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
}
My questions are:
Update
I was able to construct the object successfully as using Arraylist/List require to use
fieldID fid_legID = env->GetFieldID(cls, "mLegList", "Ljava/util/List;");
instead. but now I'm wondering how to iterate through this list and read through all the fields.
Upvotes: 0
Views: 1850
Reputation: 13375
Let's say your source code tree looks like this
|-- c
| `-- Driver.c
|-- lib
|-- mypackage
| |-- Driver.java
| |-- Leg.java
| `-- Line.java
`-- target
and then, you have following files:
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;
}
You can access elements of the List
following way
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);
}
}
If you want to compile all the stuff, you can use following script
#!/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
Once you run it, you will get what you are looking for :)
> ./compile.sh
darwin
[value] = test_1
[value] = test_2
[value] = test_2
Have fun with JNI
:)
Update
get
method: recipeNo067Iterator
: recipeNo068ArrayList
as Object []
: recipeNo069Usage
> git clone https://github.com/mkowsiak/jnicookbook.git
> export JAVA_HOME=your_JDK_installation
> cd jnicookbook/recipes/recipeNo067
> make
> make test
Upvotes: 1
Reputation: 30830
Iterating across a List
in JNI is the same as in Java, it is just more typing.
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);
}
Note that this example still needs error checking.
Upvotes: 1