Android ICS 4.0 NDK NewStringUTF 使应用程序崩溃

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12127817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 09:49:11  来源:igfitidea点击:

Android ICS 4.0 NDK NewStringUTF is crashing down the App

androidandroid-ndkjava-native-interfaceandroid-4.0-ice-cream-sandwichandroid-ndk-r7

提问by rana

I have a method in JNI C/C++ which takes jstring and returns back jstring some thing like as below,

我在 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;
  }

The code works perfectly with prior android 4.0 versions like 2.2,2.3 and so on. With ICS 4.0 check JNI is on by default and because of it the app crashes throwing the following error

该代码与之前的 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)

I am clueless as to where i am wrong. If you see above NewStringUTF is adding some garbage value to the c Char* bytes .

我不知道我错在哪里。如果您在上面看到 NewStringUTF 正在向 c Char* bytes 添加一些垃圾值。

  1. Any idea about why this is happening
  2. Any alternative solution to achieve the above is welcome
  1. 关于为什么会发生这种情况的任何想法
  2. 欢迎任何实现上述目标的替代解决方案

I really appreciate if one of you can help me in . Thanks in advance

如果你们中的一个能帮助我,我真的很感激。提前致谢

regds me

注册我

采纳答案by rana

I resolved this issue by returning byte array instead of String. On the Java side i am now converting the Byte array to Strings .Works fine! Stay away from using NewStringUTF() for Android 4.0 and above as there is already a bug reported on Google Android NDK.

我通过返回字节数组而不是字符串解决了这个问题。在 Java 方面,我现在将 Byte 数组转换为 Strings 。工作正常!避免在 Android 4.0 及更高版本中使用 NewStringUTF(),因为 Google Android NDK 上已经报告了一个错误。

回答by Moshe Rubin

The cause of this problem is directly related to a known UTF-8 bug in the NDK/JNI GetStringUTFChars() function(and probably related functions like NewStringUTF). These NDK functions do not convert supplementary Unicode characters(i.e., Unicode characters with a value of U+10000 and above) correctly. This leads to incorrect UTF-8 and subsequent crashes.

此问题的原因与NDK/JNI GetStringUTFChars() 函数(可能还有 NewStringUTF 等相关函数)中已知的 UTF-8 错误直接相关。这些 NDK 函数无法正确转换补充 Unicode 字符(即,值为 U+10000 及以上的 Unicode 字符)。这会导致错误的 UTF-8 和随后的崩溃。

I encountered the crash when handling user input text that contained emoticon characters (see the corresponding Unicode chart). Emoticon characters lie in the Supplementary Unicode character range.

我在处理包含表情符号的用户输入文本时遇到了崩溃(请参阅相应的 Unicode 图表)。图释字符位于补充 Unicode 字符范围内。

Analysis of the Problem

问题分析

  1. The Java client passes a string containing a supplementary Unicode character to JNI/NDK.
  2. JNI uses the NDK function GetStringUTFChars() to extract the contents of the Java string.
  3. GetStringUTFChars() returns the string data as incorrect and invalid UTF-8.
  1. Java 客户端将包含补充 Unicode 字符的字符串传递给 JNI/NDK。
  2. JNI 使用 NDK 函数 GetStringUTFChars() 来提取 Java 字符串的内容。
  3. GetStringUTFChars() 将字符串数据返回为不正确且无效的 UTF-8。

There is a known NDK bugwhereby GetStringUTFChars() incorrectly converts supplementary Unicode characters, producing an incorrect and invalid UTF-8 sequence.

有一个已知的 NDK 错误,即 GetStringUTFChars() 错误地转换补充 Unicode 字符,产生不正确和无效的 UTF-8 序列。

In my case, the resulting string was a JSON buffer. When the buffer was passed to the JSON parser, the parser promptly failed because one of the UTF-8 characters of the extracted UTF-8 had an invalid UTF-8 prefix byte.

就我而言,结果字符串是一个 JSON 缓冲区。当缓冲区传递给 JSON 解析器时,解析器立即失败,因为提取的 UTF-8 的 UTF-8 字符之一具有无效的 UTF-8 前缀字节。

Possible Workaround

可能的解决方法

The solution I've used can be summarized as follows:

我使用的解决方案可以总结如下:

  1. The goal is to prevent GetStringUTFChars() from performing the incorrect UTF-8 encoding of the supplementary Unicode character.
  2. This is done by the Java client encoding the request string as Base64.
  3. The Base64-encoded request is passed to JNI.
  4. JNI calls GetStringUTFChars(), which extracts the Base64-encoded string without performing any UTF-8 encoding.
  5. The JNI code then decodes the Base-64 data, producing the original UTF-16 (wide char) request string, including the supplementary Unicode character.
  1. 目标是防止 GetStringUTFChars() 对补充 Unicode 字符执行不正确的 UTF-8 编码。
  2. 这是由 Java 客户端将请求字符串编码为 Base64 来完成的。
  3. Base64 编码的请求被传递给 JNI。
  4. JNI 调用 GetStringUTFChars(),它提取 Base64 编码的字符串而不执行任何 UTF-8 编码。
  5. 然后 JNI 代码对 Base-64 数据进行解码,生成原始 UTF-16(宽字符)请求字符串,包括补充 Unicode 字符。

In this way we circumvent the problem of extracting supplementary Unicode characters from the Java string. Instead, we convert the data to Base-64 ASCII before calling GetStringUTFChars(), extract the Base-64 ASCII characters using GetStringUTFChars(), and convert the Base-64 data back to wide characters.

通过这种方式,我们规避了从 Java 字符串中提取补充 Unicode 字符的问题。相反,我们在调用 GetStringUTFChars() 之前将数据转换为 Base-64 ASCII,使用 GetStringUTFChars() 提取 Base-64 ASCII 字符,并将 Base-64 数据转换回宽字符。

回答by Berkay Turanc?

This is how I done this.

我就是这样做的。

1- Char Array to JByteArray.

1- 字符数组到 JByteArray。

2- JByteArray to JString.

2- JByteArray 到 JString。

3- Return jstring to java side.

3- 将 jstring 返回到 java 端。

JNI Code; (.c) format

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 Code;

Java代码;

native String methodName();

Other Approach Not Works For Me;

其他方法对我不起作用;

I also tried return (*env)->NewStringUTF(env, r)but returns some characters that are not in the char array, at the end of the string where with the warning of JNI WARNING: input is not valid Modified UTF-8: illegal continuation byte 0x40.

我也尝试过return (*env)->NewStringUTF(env, r)但返回一些不在 char 数组中的字符,在字符串的末尾,警告JNI WARNING: input is not valid Modified UTF-8: wrong continuation byte 0x40

Example; PKdhtXMmr18n2L9K???????-DL

例子; PKdhtXMmr18n2L9K???????-DL

Edit:

编辑:

C++ version

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

回答by Daniel Ryan

I had this problem when I change the file Application.mk

我在更改文件 Application.mk 时遇到了这个问题

From this line:

从这一行:

APP_STL := stlport_static

To:

到:

APP_STL := gnustl_static

Once I changed it back again it fixed the issue.

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

回答by kelnos

Strings that you pass to NewStringUTF() need to be valid Modified UTF-8. It looks like the string returned by your start_Inauthroot() function is in some other encoding, or is just returning an invalid string. You need to convert the string to UTF-8 before passing it to JNI functions. Or you could use one of the charset-aware String constructorsto build the String object instead.

您传递给 NewStringUTF() 的字符串需要是有效的 Modified UTF-8。看起来您的 start_Inauthroot() 函数返回的字符串采用其他编码,或者只是返回无效字符串。您需要先将字符串转换为 UTF-8,然后再将其传递给 JNI 函数。或者,您可以使用支持字符集的 String 构造函数之一来构建 String 对象。

回答by Shaw

In my opinion, its not a bug.

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

NewStringUTF Constructs a new java.lang.String object from an array of characters in modified UTF-8encoding.

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

Modified UTF-8 is not standard UTF-8. See Modified UTF-8

修改后的 UTF-8 不是标准的 UTF-8。请参阅修改后的 UTF-8

In most cases, UTF-8 encoded string is valid Modified UTF-8. Because Modified UTF-8 and UTF-8 are quite similar. However when it comes to Unicode string beyond Basic Multilingual Plane, they are not compatible.

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

Solution:pass UTF-8 bytes to Java layer and new String(bytes, "UTF-8") then pass jstring to JNI.

解决方案:将 UTF-8 字节传递给 Java 层和 new String(bytes, "UTF-8") 然后将 jstring 传递给 JNI。

回答by J0nh1dd3n

For me, the solution was to place the content on a const char*:

对我来说,解决方案是将内容放在 const char* 上:

const char* string = name_sin.c_str();
jstring utf8 = env_r->NewStringUTF(string);

and the function:

和功能:

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

回答by Afjalur Rahman Rana

I also struggled with the same problem from the last day. Finally figured out a solution after a day .. I hope this reply may save someone's day..

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

The problem was I was calling another function within the native function, used the returned string directly and which caused crash in android older versions

问题是我在本机函数中调用了另一个函数,直接使用了返回的字符串,这导致了 android 旧版本的崩溃

So firstly I saved the string returned from another function to a variable then used it, and the problem gone :D

所以首先我将从另一个函数返回的字符串保存到一个变量然后使用它,问题就消失了:D

The below example may clear your concept

下面的例子可能会清除你的概念

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

And here is how I solved this error

这是我解决这个错误的方法

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

Happy coding :D

快乐编码:D

回答by Arshid KV

candroid ndkis working as follows

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

回答by hB0

This works for me in c++

这在 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());
}