我在JNI C/C++中有一个方法,它接受jstring并返回jstring,如下所示,
NATIVE_CALL(jstring, method)(JNIEnv * env, jobject obj, jstring filename)
{
// Get jstring into C string format.
const char* cs = env->GetStringUTFChars (filename, NULL);
char *file_path = new char [strlen (cs) + 1]; // +1 for null terminator
sprintf (file_path, "%s", cs);
env->ReleaseStringUTFChars (filename, cs);
reason_code = INTERNAL_FAILURE;
char* info = start_module(file_path);
jstring jinfo ;
if(info==NULL)
{
jinfo = env->NewStringUTF(NULL);
}
else
{
jinfo = env->NewStringUTF(info);
}
delete info;
info = NULL;
return jinfo;
}
该代码与之前的android 4.0版本(如2.2、2.3等)完美配合。使用ICS 4.0检查JNI默认情况下处于打开状态,因此应用程序崩溃,引发以下错误
08-25 22:16:35.480: W/dalvikvm(24027): **JNI WARNING: input is not valid Modified UTF-8: illegal continuation byte 0x40**
08-25 22:16:35.480: W/dalvikvm(24027):
08-25 22:16:35.480: W/dalvikvm(24027): ==========
08-25 22:16:35.480: W/dalvikvm(24027): /tmp/create
08-25 22:16:35.480: W/dalvikvm(24027): ==========
08-25 22:16:35.480: W/dalvikvm(24027): databytes,indoorgames,drop
08-25 22:16:35.480: W/dalvikvm(24027): ==========���c_ag����ϋ@�ډ@�����@'
08-25 22:16:35.480: W/dalvikvm(24027): in Lincom/inter /ndk/comNDK;.rootNDK:(Ljava/lang/String;)Ljava/lang/String; **(NewStringUTF)**
08-25 22:16:35.480: I/dalvikvm(24027): "main" prio=5 tid=1 NATIVE
08-25 22:16:35.480: I/dalvikvm(24027): | group="main" sCount=0 dsCount=0 obj=0x40a4b460 self=0x1be1850
08-25 22:16:35.480: I/dalvikvm(24027): | sysTid=24027 nice=0 sched=0/0 cgrp=default handle=1074255080
08-25 22:16:35.490: I/dalvikvm(24027): | schedstat=( 49658000 26700000 48 ) utm=1 stm=3 core=1
08-25 22:16:35.490: I/dalvikvm(24027): at comrootNDK(Native Method)
我不知道自己错在哪里了。如果您看到上面的内容,NewStringUTF正在向cChar*字节添加一些垃圾值。
- 知道为什么会发生这种情况吗
- 欢迎任何实现上述目标的替代解决方案
如果你们中有人能帮我,我真的很感激。提前感谢
regdsme
此问题的原因与NDK/JNI GetStringUTFChars()函数(可能还有NewStringUTF等相关函数)中的一个已知UTF-8错误直接相关。这些NDK函数无法正确转换补充Unicode字符(即值为U+10000及以上的Unicode字符)。这会导致不正确的UTF-8和随后的崩溃。
我在处理包含表情符号的用户输入文本时遇到了崩溃(请参阅相应的Unicode图表)。表情符号字符位于补充Unicode字符范围内。
问题分析
- Java客户端传递一个包含补充Unicode的字符串字符转换为JNI/NDK
- JNI使用NDK函数GetStringUTFChars()来提取Java字符串的内容
- GetStringUTFChars()将字符串数据返回为不正确和无效的UTF-8
存在一个已知的NDK错误,GetStringUTFChars()错误地转换补充Unicode字符,从而产生不正确且无效的UTF-8序列。
在我的例子中,得到的字符串是一个JSON缓冲区。当缓冲区被传递给JSON解析器时,解析器立即失败,因为提取的UTF-8中的一个UTF-8字符具有无效的UTF-8前缀字节。
可能的解决方案
我使用的解决方案可以总结如下:
- 目标是防止GetStringUTFChars()执行补充Unicode字符的UTF-8编码不正确
- 这是通过Java客户端将请求字符串编码为Base64
- Base64编码的请求被传递给JNI
- JNI调用GetStringUTFChars(),它提取Base64编码的字符串,而不执行任何UTF-8编码
- 然后,JNI代码对Base-64数据进行解码,生成原始数据UTF-16(宽字符)请求字符串,包括补充Unicode字符
通过这种方式,我们可以避免从Java字符串中提取补充Unicode字符的问题。相反,我们在调用GetStringUTFChars()之前将数据转换为Base-64 ASCII,使用GetStringUTFChars(,提取Base-64的ASCII字符,并将Base-64数据转换回宽字符。
我就是这样做的。
1字符数组到JByteArray。
2-JByteArray到JString。
3-将jstring返回到java端。
JNI代码;(.c)格式
jstring Java_com_x_y_z_methodName(JNIEnv *env, jobject thiz) {
int size = 16;
char r[] = {'P', 'K', 'd', 'h', 't', 'X', 'M', 'm', 'r', '1', '8', 'n', '2', 'L', '9', 'K'};
jbyteArray array = (*env)->NewByteArray(env, size);
(*env)->SetByteArrayRegion(env, array, 0, size, r);
jstring strEncode = (*env)->NewStringUTF(env, "UTF-8");
jclass cls = (*env)->FindClass(env, "java/lang/String");
jmethodID ctor = (*env)->GetMethodID(env, cls, "<init>", "([BLjava/lang/String;)V");
jstring object = (jstring) (*env)->NewObject(env, cls, ctor, array, strEncode);
return object;
}
Java代码
native String methodName();
其他方法对我不起作用
我也尝试了return (*env)->NewStringUTF(env, r)
,但在字符串末尾返回了一些不在char数组中的字符,其中带有JNI warning:输入无效修改的UTF-8:非法连续字节0x40的警告。
示例;PKdhtXMmr18n2L9K�ؾ�����-DL-
编辑:
C++版本
jstring clientStringFromStdString(JNIEnv *env,const std::string &str){
// return env->NewStringUTF(str.c_str());
jbyteArray array = env->NewByteArray(str.size());
env->SetByteArrayRegion(array, 0, str.size(), (const jbyte*)str.c_str());
jstring strEncode = env->NewStringUTF("UTF-8");
jclass cls = env->FindClass("java/lang/String");
jmethodID ctor = env->GetMethodID(cls, "<init>", "([BLjava/lang/String;)V");
jstring object = (jstring) env->NewObject(cls, ctor, array, strEncode);
return object;
}
我通过返回字节数组而不是字符串来解决这个问题。在Java方面,我现在正在将Byte数组转换为Strings。工作正常!不要在Android 4.0及以上版本中使用NewStringUTF(),因为谷歌Android NDK上已经报告了一个错误。
我在更改文件Application.mk 时遇到了这个问题
从此行:
APP_STL := stlport_static
收件人:
APP_STL := gnustl_static
一旦我再次把它改回来,它就解决了这个问题。
传递给NewStringUTF()的字符串需要是有效的Modified UTF-8。看起来start_Inauthroot()函数返回的字符串是其他编码的,或者只是返回了一个无效字符串。在将字符串传递给JNI函数之前,需要将其转换为UTF-8。或者,您可以使用一个具有字符集意识的String构造函数来构建String对象。
在我看来,这不是一个bug。
NewStringUTF从修改后的UTF-8编码中的字符数组构造一个新的java.lang.String对象。
修改后的UTF-8不是标准的UTF-8。请参阅修改的UTF-8
在大多数情况下,UTF-8编码的字符串是有效的Modified UTF-8。因为Modified UTF-8和UTF-8非常相似。然而,当涉及到基本多语言平面之外的Unicode字符串时,它们是不兼容的。
解决方案:将UTF-8字节传递给Java层和新的String(字节,"UTF-8"),然后将jstring传递给JNI。
对我来说,解决方案是将内容放在const char*上:
const char* string = name_sin.c_str();
jstring utf8 = env_r->NewStringUTF(string);
以及功能:
jclass cls_Env = env_r->FindClass(CLASS_ACTIVITY_NAME);
jmethodID mid = env_r->GetMethodID(cls_Env, "Delegate",
"(Ljava/lang/String;)V");
//todo importante hacerlo asi, si pasas directamente c_str a veces da error de carater no UTF 8
const char* string = name_sin.c_str();
jstring utf8 = env_r->NewStringUTF(string);
env_r->CallVoidMethod(*object_r, mid, utf8);
env_r->DeleteLocalRef(utf8);
从最后一天起,我也遇到了同样的问题。一天后终于想出了解决办法。。我希望这个回复能挽救别人的一天。。
问题是我在本机函数中调用了另一个函数,直接使用了返回的字符串,这导致了android旧版本的崩溃
因此,首先我将另一个函数返回的字符串保存到一个变量中,然后使用它,问题就解决了:D
下面的例子可能会澄清你的概念
//older code with error
//here key_ is the string from java code
const char *key = env->GetStringUTFChars(key_, 0);
const char *keyx = getkey(key).c_str();
return env->NewStringUTF(keyx);
以下是我如何解决这个错误
//newer code which is working
//here key_ is the string from java code
const char *key = env->GetStringUTFChars(key_, 0);
string k = getkey(key);
const char *keyx = k.c_str();
return env->NewStringUTF(keyx);
快乐编码:D
这对我在c++中有效
extern "C" JNIEXPORT
jstring Java_com_example_ndktest_MainActivity_TalkToJNI(JNIEnv* env, jobject javaThis, jstring strFromJava)
{
jboolean isCopy;
const char* szHTML = env->GetStringUTFChars(strFromJava, &isCopy);
std::string strMine;
strMine = szHTML;
strMine += " --- Hello from the JNI!!";
env->ReleaseStringUTFChars(strFromJava, szHTML);
return env->NewStringUTF(strMine.c_str());
}
c
androidndk的工作方式如下
JNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz,jstring str )
{
jboolean isCopy;
const char* szHTML = (*env)->GetStringUTFChars(env, str, &isCopy);
return (*env)->NewStringUTF(env, szHTML);
}