问题
Simple question. How can I get a jstring out of a unicode const char*, using JNI and C++?
Here's my issue, and what I have already tried:
const char* value = (some value from server);
(*env)->NewStringUTF(value);
The issue here is that NewStringUTF returns a UTF string, and it does not like some of the non-UTF8 characters (kind of obvious, but worth a simple try).
Attempt 2, using NewString:
const char* value = (some value from server);
(*env)->NewString(value, strlen(value));
While NewString accepts and returns a unicode string, the strlen(value) method does not work because it requires a jsize param instead of just a good ol' size_t or length.
How do we get a jsize? According to the (very very small amount of) documentation and online examples, you can get the jsize out of a jIntArray. I can't find information on how to convert a const char* to jarray of some sort, and this might be a bad idea anyways.
The other option would be to get the jsize out of the int in size_t, which I haven't succeed at yet either.
Has anyone come across this issue, or has suggestions on how to get around it? It seems like jsize is the key I'm missing for the unicode conversion. Also, I'm using JNI and the Android NDK, in case it helps anyone.
Thanks.
Edit I just realized NewString is also expecting a jchar*, so its signature is (jchar*, jsize). This means that even with the jsize the const char* does not compile.
Edit 2 Here's the exception being thrown at runtime when using the NewStringUTF method. This is related to what @fadden is talking about:
JNI WARNING: NewStringUTF input is not valid Modified UTF-8: illegal start byte 0xb7 03string: ' : Method(d6, us-dev1-api, 0), , 訩�x�m�P)
回答1:
As the error message shows, your char* is not a valid Modifed-utf8, so the JVM aborted.
You got two methods to avoid them.
- check char* content to avoid a crash.
the check logic in android ART check_jni.cc is as following https://android.googlesource.com/platform/art/+/35e827a/runtime/check_jni.cc#1273
jstring toJString(JNIEnv* env, const char* bytes) {
const char* error = nullptr;
auto utf8 = CheckUtfBytes(bytes, &error);
if (error) {
std::ostringstream msg;
msg << error << " 0x" << std::hex << static_cast<int>(utf8);
throw std::system_error(-1, std::generic_category(), msg.str());
} else {
return env->NewStringUTF(bytes);
}
This way, you always get a valid jstring
.
- Using String constructor to build from a
jbyteArray
.
jstring toJString(JNIEnv *env, const char *pat) {
int len = strlen(pat);
jbyteArray bytes = env->NewByteArray(len);
env->SetByteArrayRegion(bytes, 0, len, (jbyte *) pat);
jstring encoding = env->NewStringUTF("utf-8");
jstring jstr = (jstring) env->NewObject(java_lang_String_class,
java_lang_String_init, bytes, encoding);
env->DeleteLocalRef(encoding);
env->DeleteLocalRef(bytes);
return jstr;
}
This way, you just avoid the crash, but the string may be still not valid, and you copy memory twice, which performs badly.
来源:https://stackoverflow.com/questions/28990038/unicode-const-char-to-jstring-using-jni-and-c