Call JNI static method to return a string from C ++

I am trying to call the following java method in Android

public static String getLevelFile(String levelName) { /*body*/} 

from c ++ using the following jni code

 JniMethodInfoJavaApi methodInfo; if (! getStaticMethodInfo(methodInfo, "getLevelFile", "(Ljava/lang/String;)Ljava/lang/String;")) { return std::string(""); } LOGD("calling getLevelFile"); jstring returnString = (jstring) methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, levelName.c_str()); LOGD("returned from getLevelFile"); methodInfo.env->DeleteLocalRef(methodInfo.classID); const char *js = methodInfo.env->GetStringUTFChars(returnString, NULL); std::string cs(js); methodInfo.env->ReleaseStringUTFChars(returnString, js); LOGD("returning Level data"); 

When CallStaticMethodObject() executed, the application crashes. I verified that the method signature is correct using javap . And LOGD("calling getLevelFile"); prints fine and then it crashes. I can make other CallStaticVoidMethod() from the same class, but not from this. Any ideas what I'm doing wrong?

+6
source share
2 answers

You cannot directly pass a null-terminated string (returned from c_str() ) as a parameter to the Java / JNI method.

To pass it to a method, create a jstring from a string with a terminated zero (for example, using NewStringUTF ) and pass this instead.

+3
source

You're lucky Java and Android use the Unicode character set. However, Android (by default) uses UTF-8 encoding, which JNI does not support internally. However, Java classes are fully capable of converting character set encodings. The constructors lang.java.String allow you to specify a character set / encoding or use OS-default, which on Android, of course, is encoded as UTF-8.

To simplify (I prefer coding in Java, minimizing the code that calls the JNI library), create an overload of your method and do some implementation in Java:

 private static byte[] getLevelFile(byte[] levelName) { return getLevelFile(new String(levelName)).getBytes(); } 

Now the JNI code should only deal with jbytearray, both for the parameter and for the return value:

 JniMethodInfoJavaApi methodInfo; if (! getStaticMethodInfo(methodInfo, "getLevelFile", "([B)[B")) { return std::string(""); } LOGD("calling getLevelFile"); int nameLength = levelName.length(); jbyteArray nameBytes = methodInfo.env->NewByteArray(nameLength); methodInfo.env->SetByteArrayRegion(nameBytes, 0, nameLength, reinterpret_cast<const jbyte*>(levelName.c_str())); jbyteArray returnString = (jbyteArray) methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, nameBytes); LOGD("returned from getLevelFile"); methodInfo.env->DeleteLocalRef(methodInfo.classID); methodInfo.env->DeleteLocalRef(nameBytes); int returnLength = methodInfo.env->GetArrayLength(returnString); std::string data; data.reserve(returnLength); methodInfo.env->GetByteArrayRegion(returnString, 0, returnLength, reinterpret_cast<jbyte*>(&data[0])); methodInfo.env->DeleteLocalRef(returnString); LOGD("returning Level data"); return data; 
+4
source

All Articles