accessing objects in JNI

S

stixwix

Most of the examples I've seen for JNI seem to allow access to 'top
level' instance variables like this:

public class HelloNative{

private String str;
private native void setObjLong();

public static void main(String[] args) {
HelloNative test = new HelloNative();
test.setObjLong();
System.out.println("The java s is: "+ test.str);
}
}

The string is created in the c code:
JNIEXPORT void JNICALL
Java_HelloNative_setObjLong (JNIEnv* env, jobject obj) {

/* get the class object */
jclass cls = (*env)->GetObjectClass(env, obj);
/* get the field id */
jfieldID fid = (*env)->GetFieldID(env, cls, "str",
"Ljava/lang/String;");
/* make new string */
jstring myStr = (*env)->NewStringUTF(env,"1234");
/* set the value */
(*env)->SetObjectField(env, obj, fid, myStr);
return 0;
}

This all works fine, the question is how do you get to user-defined
object's instanace variables?
So if I change the java bit thus:
public class HelloNative {

private MyClass myObj = new MyClass();
private native void setObjLong();

class MyClass{
private long address;
// getters and setters omitted
}

public static void main(String[] args) {
HelloNative test = new HelloNative();
test.setObjLong();
System.out.println("The java long is: "+
test.myObj.getAddress());
}
}

How can I set the address field of myObj?

If I try:
jfieldID fid = (*env)->GetFieldID(env, cls, "myObj",
"Ljava/lang/Object;");

I get:
java.lang.NoSuchFieldError: HelloNative.myObj

....and anyway this would only get me to the object and not to the
address instance which I wish to set.

Thanks,
Andy
 
G

Gordon Beaton

the question is how do you get to user-defined
object's instanace variables?

So if I change the java bit thus:
public class HelloNative {

private MyClass myObj = new MyClass();
private native void setObjLong();

class MyClass{
private long address;
// getters and setters omitted
}

public static void main(String[] args) {
HelloNative test = new HelloNative();
test.setObjLong();
System.out.println("The java long is: "+
test.myObj.getAddress());
}
}

How can I set the address field of myObj?

If I try:
jfieldID fid = (*env)->GetFieldID(env, cls, "myObj",
"Ljava/lang/Object;");

I get:
java.lang.NoSuchFieldError: HelloNative.myObj

Your call to GetFieldId() says "look in HelloNative for a field called
myObj whose type is java.lang.Object". But myObj in HelloNative is
clearly of type HelloNative$MyClass (because MyClass is an inner class
to HelloNative), not java.lang.Object.

"javap -s HelloNative" will show you the correct signatures to use for
all of the fields and methods in that class.
...and anyway this would only get me to the object and not to the
address instance which I wish to set.

To create an instance of the inner class, look up that class, then
look up one of its constructors (realizing that there is a hidden
first argument, an object of the enclosing class) and use that to
invoke NewObject().

/gordon
 
S

stixwix

Gordon said:
To create an instance of the inner class, look up that class, then
look up one of its constructors (realizing that there is a hidden
first argument, an object of the enclosing class) and use that to
invoke NewObject().
Thanks for that. I now get a little further. With regard to the clip
above, do i need to do this if I am passing an (instantiated) object
into the c code from the java code?

To get to the address field (String) in the MyClass, I was thinking the
following should work but it crashes nastily. (Note: I have now changed
this field in MyClass from long to String).

/* get the class object */
jclass cls = (*env)->GetObjectClass(env, obj);

/* get the myObj instance from HelloNative */
jfieldID fid = (*env)->GetFieldID(env, cls, "myObj",
"LHelloNative$MyClass;");

/* get the address field from MyClass */
jfieldID fidStr = (*env)->GetFieldID(env, fid, "address",
"Ljava/lang/String;");

It crashes when I introduce the last line.
There is a compiler warning:
"passing arg 2 of pointer to function from incompatible pointer type"

but I've no idea what I can pass to that function instead.

Thanks,
Andy
 
G

Gordon Beaton

With regard to the clip above, do i need to do this if I am passing
an (instantiated) object into the c code from the java code?

Yes, it's perfectly reasonable to use the enclosing object ("this")
when you create instances of its inner class.
/* get the address field from MyClass */
jfieldID fidStr = (*env)->GetFieldID(env, fid, "address",
"Ljava/lang/String;");

It crashes when I introduce the last line.

You're attempting to find the fieldID by looking it up in another
fieldID, but you should be looking in a class instead.

Start with the class HelloNative$MyClass, and from there you can look
up its fields:

jclass my_cls = (*env)->FindClass(env,"HelloNative$MyClass");
jfieldID addr_fid = (*env)->GetFieldID(env,my_cls,"address",
"Ljava/lang/String;");

Now to actually get or change the *value* associated with this field,
you need an *object* of that type (i.e. one that holds the field you
just looked up), and you choose one of the Get/Set<type>Field()
functions based on the type of the field itself (i.e. one of the
primitive types, or Object for all others).

/gordon
 
S

stixwix

Gordon said:
Start with the class HelloNative$MyClass, and from there you can look
up its fields:

jclass my_cls = (*env)->FindClass(env,"HelloNative$MyClass");
jfieldID addr_fid = (*env)->GetFieldID(env,my_cls,"address",
"Ljava/lang/String;");

Now to actually get or change the *value* associated with this field,
you need an *object* of that type (i.e. one that holds the field you
just looked up), and you choose one of the Get/Set<type>Field()
functions based on the type of the field itself (i.e. one of the
primitive types, or Object for all others).

When I try the above with the following additional code - jstr is
NULL.:
jstring jstr = (*env)->GetObjectField(env, obj, addr_fid);
const char *str = (*env)->GetStringUTFChars(env, jstr, NULL);

All the examples I see use GetObjectClass instead of FindClass. I
can't find/understand any docs relating to FindClass, but it doesn't
imply object instance scope.
 
G

Gordon Beaton

When I try the above with the following additional code - jstr is
NULL.:
jstring jstr = (*env)->GetObjectField(env, obj, addr_fid);
const char *str = (*env)->GetStringUTFChars(env, jstr, NULL);

It's hard to tell from these code fragments, but is "obj" really an
instance of the class containing the field you are looking up?

I.e. you looked up up a field ID in HelloNative$MyClass, so "obj" must
be a HelloNative$MyClass object (or it won't have that field).

/gordon
 
Joined
Jan 10, 2012
Messages
1
Reaction score
0
How to access an object of another class in native code

Hi,
I need to access instance fields of a java class which is not the class from which i am calling the native function.So i cannot use the obj that is passed by default as second argument to the native function and use getfieldid(). This is the code structure:

class data
{
int a;
}
class ReadFile
{
private native void loadFile(data d);
static
{
System.loadLibrary("ReadFile");
}
public static void main(String args[])
{
data d= new data();
ReadFile mappedFile=new ReadFile();
mappedFile.loadFile(d);
}
}

I now want to access 'a' from native code.How can i do that. I have used findclass but it does not work, the field value shows 0 evenafter setting it to some value in native code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top