tags:

views:

42

answers:

2

I am trying to pass back a string from a Java method called from C++. I am not able to find out what JNI function should I call to access the method and be returned a jstring value.

My code follows:

C++ part

main() {
    jclass cls;
    jmethodID mid;
    jstring rv;

/** ... omitted code ... */

    cls = env->FindClass("ClassifierWrapper");
    mid = env->GetMethodID(cls, "getString","()Ljava/lang/String");

    rv = env->CallStatic<TYPE>Method(cls, mid, 0);
    const char *strReturn = env->GetStringUTFChars(env, rv, 0);

    env->ReleaseStringUTFChars(rv, strReturn);
}

Java Code

public class ClassifierWrapper {
    public String getString() { return "TEST";}
}

The Method Signature (from "javap -s Class")

public java.lang.String getString();
  Signature: ()Ljava/lang/String;
A: 

Hi,

The first problem is that ClassifierWrapper.getString() is not static. You will need to make it static or instantiate ClassifierWrapper.

The second problem is that you are using GetMethodId instead of GetStaticMethodId.

To invoke a method that returns an Object (such as a String) you would call CallStaticObjectMethod(). That will return a jobject local reference to the String that the method returned. You can safely cast the jobject to a jstring (see http://java.sun.com/docs/books/jni/html/types.html) and use GetStringUTFChars to retrieve the characters and GetStringUTFLength to get the number of characters.

JNI is very tricky. You need to check the error code for everything (use ExceptionCheck() when there is no error code). If you don't check for errors it will fail silently in most cases and usually not at the point where the actual bug is.

You also need to understand the difference between local and global references (and what methods generate new references) in order to not leak memory and run into the reference limit. For instance, FindClass returns a local reference to a class object, but GetMethodId returns a MethodID.

Good luck

aweisberg
+1  A: 

You should have

cls = env->FindClass("ClassifierWrapper"); 

Then you need to invoke the constructor to get a new object:

jmethodID classifierConstructor = env->GetMethodID(cls,"<init>", "()V"); 
if (classifierConstructor == NULL) {
  return NULL; /* exception thrown */
}

jobject classifierObj = env->NewObject( cls, classifierConstructor);

You are getting static method (even though the method name is wrong). But you need to get the instance method since getString() is not static.

jmethodID getStringMethod = env->GetMethodID(cls, "getString", "()Ljava/lang/String"); 

Now invoke the method:

rv = env->CallObjectMethod(classifierObj, getStringMethod, 0); 
const char *strReturn = env->GetStringUTFChars(env, rv, 0);
Marimuthu Madasamy