Android ICS 4.0 NDK NewStringUTF正在崩溃App

时间:2012-08-26 05:24:06

标签: android android-ndk java-native-interface android-4.0-ice-cream-sandwich android-ndk-r7

我在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正在向c char *字节添加一些垃圾值。

  1. 任何关于为什么会发生这种情况的想法
  2. 欢迎任何实现上述目标的替代解决方案
  3. 如果你们中的一个人可以帮助我,我真的很感激。提前致谢

    regds 我

10 个答案:

答案 0 :(得分:20)

此问题的原因与known UTF-8 bug in the NDK/JNI GetStringUTFChars() function(可能与NewStringUTF等相关函数)直接相关。这些NDK函数不能正确转换supplementary Unicode characters(即,值为U + 10000及以上的Unicode字符)。这导致错误的UTF-8和随后的崩溃。

我在处理包含表情符号(see the corresponding Unicode chart)的用户输入文本时遇到了崩溃。表情符号位于补充Unicode字符范围内。

问题分析

  1. Java客户端传递包含补充Unicode的字符串 JNI / NDK的角色。
  2. JNI使用NDK函数GetStringUTFChars()来提取Java字符串的内容。
  3. GetStringUTFChars()将字符串数据作为错误且无效的UTF-8返回。
  4. There is a known NDK bug,GetStringUTFChars()错误地转换了补充的Unicode字符,产生了错误且无效的UTF-8序列。

    在我的例子中,结果字符串是一个JSON缓冲区。当缓冲区传递给JSON解析器时,解析器立即失败,因为解压缩的UTF-8中的一个UTF-8字符具有无效的UTF-8前缀字节。

    可能的解决方法

    我使用的解决方案可归纳如下:

    1. 目标是阻止GetStringUTFChars()执行 补充Unicode字符的UTF-8编码不正确。
    2. 这是由将客户端编码为请求字符串的Java客户端完成的 BASE64。
    3. 将Base64编码的请求传递给JNI。
    4. JNI调用GetStringUTFChars(),它提取Base64编码的 字符串,不执行任何UTF-8编码。
    5. 然后,JNI代码对Base-64数据进行解码,生成原始数据 UTF-16(宽字符)请求字符串,包括补充 Unicode字符。
    6. 通过这种方式,我们避免了从Java字符串中提取补充Unicode字符的问题。相反,我们在调用GetStringUTFChars()之前将数据转换为Base-64 ASCII,使用GetStringUTFChars()提取Base-64 ASCII字符,并将Base-64数据转换回宽字符。

答案 1 :(得分:12)

这就是我这样做的方式。

1- Char Array到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的警告警告:输入无效修改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;
}

答案 2 :(得分:10)

我通过返回字节数组而不是String来解决了这个问题。在Java方面,我现在将Byte数组转换为字符串.Works很好!因为已经在Google Android NDK上报告了一个错误,所以不要使用适用于Android 4.0及更高版本的NewStringUTF()。

答案 3 :(得分:2)

当我更改文件Application.mk

时出现此问题

从这一行:

APP_STL := stlport_static

要:

APP_STL := gnustl_static

一旦我再次将其更改,它就解决了问题。

答案 4 :(得分:1)

传递给NewStringUTF()的字符串需要有效修改UTF-8。看起来你的start_Inauthroot()函数返回的字符串是在其他一些编码中,或者只是返回一个无效的字符串。在将字符串传递给JNI函数之前,需要将字符串转换为UTF-8。或者您可以使用其中一个charset-aware String constructors来构建String对象。

答案 5 :(得分:1)

在我看来,它不是一个错误。

NewStringUTF从修改后的UTF-8 编码中的字符数组构造一个新的java.lang.String对象。

修改后的UTF-8不是标准的UTF-8。见Modified UTF-8

在大多数情况下,UTF-8编码的字符串是有效的修改的UTF-8。因为Modified UTF-8和UTF-8非常相似。但是,当涉及超出基本多语言平面的Unicode字符串时,它们是不兼容的。

<强>解决方案: 将UTF-8字节传递给Java层和新的String(字节,“UTF-8”)然后将jstring传递给JNI。

答案 6 :(得分:1)

对我来说,解决方案是将内容放在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);

答案 7 :(得分:0)

这适用于我的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());
}

答案 8 :(得分:0)

c android ndk 的工作原理如下

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);
}

答案 9 :(得分:0)

我也在最后一天遇到同样的问题。终于在一天之后想出了一个解决方案..我希望这个回复可以拯救某人的一天......

问题是我在本机函数中调用另一个函数,直接使用返回的字符串,导致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