Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I access return value of a Java method returning java.lang.String from C++ in JNI?

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;
like image 608
Juan Macek Avatar asked Aug 03 '10 14:08

Juan Macek


People also ask

Can you return a method in Java?

You declare a method's return type in its method declaration. Within the body of the method, you use the return statement to return the value. Any method declared void doesn't return a value.


4 Answers

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);
like image 89
Marimuthu Madasamy Avatar answered Oct 13 '22 00:10

Marimuthu Madasamy


The complete working solution is as below:

Java Side

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

Native Side

jclass cls;
jmethodID mid;
jstring rv;


cls = jniEnv->FindClass("ClassifierWrapper"); //plase also consider your package name as package\name\classname

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

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

rv = (jstring)(jniEnv->CallObjectMethod(classifierObj, getStringMethod));
const char *strReturn = jniEnv->GetStringUTFChars( rv, 0);


jniEnv->ReleaseStringUTFChars(rv, strReturn);
like image 20
cahit beyaz Avatar answered Oct 12 '22 22:10

cahit beyaz


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

like image 29
aweisberg Avatar answered Oct 12 '22 23:10

aweisberg


The signature ()Ljava/lang/String is wrong, due that a class name into JVM must terminate with ;, then in this case signature must be ()Ljava/lang/String;

like image 30
Carlos Pineda Avatar answered Oct 12 '22 22:10

Carlos Pineda