web-dev-qa-db-ja.com

Android ICS 4.0 NDKNewStringUTFがアプリをクラッシュさせています

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. 上記を達成するための代替ソリューションは大歓迎です

あなたの一人が私を助けてくれたら本当に感謝しています。前もって感謝します

私をregds

14
rana

文字列の代わりにバイト配列を返すことで、この問題を解決しました。 Java側では、バイト配列を文字列に変換しています。正常に動作します。Android 4.0以降ではNewStringUTF()を使用しないでください。すでにGoogleで報告されているバグAndroid NDK。

10
rana

この問題の原因は、 NDK/JNI GetStringUTFChars()関数の既知のUTF-8バグ (およびおそらくNewStringUTFなどの関連関数)に直接関連しています。これらのNDK関数は、 補足Unicode文字 (つまり、値がU + 10000以上のUnicode文字)を正しく変換しません。これにより、UTF-8が正しくなくなり、その後クラッシュします。

絵文字を含むユーザー入力テキストを処理するときにクラッシュが発生しました( 対応するUnicodeチャートを参照 )。絵文字は、補足のUnicode文字範囲にあります。

問題の分析

  1. Javaクライアントは、補足のUnicode文字を含む文字列をJNI/NDKに渡します。
  2. JNIは、NDK関数GetStringUTFChars()を使用して、Java文字列の内容を抽出します。
  3. GetStringUTFChars()は、文字列データを正しくない無効なUTF-8として返します。

既知のNDKバグがあります これにより、GetStringUTFChars()が補足Unicode文字を誤って変換し、誤った無効なUTF-8シーケンスを生成します。

私の場合、結果の文字列はJSONバッファーでした。バッファーがJSONパーサーに渡されたとき、抽出されたUTF-8のUTF-8文字の1つに無効なUTF-8プレフィックスバイトがあったため、パーサーはすぐに失敗しました。

考えられる回避策

私が使用したソリューションは、次のように要約できます。

  1. 目標は、GetStringUTFChars()が補足Unicode文字の誤ったUTF-8エンコーディングを実行するのを防ぐことです。
  2. これは、リクエスト文字列をBase64としてエンコードするJavaクライアントによって行われます。
  3. Base64でエンコードされたリクエストがJNIに渡されます。
  4. JNIはGetStringUTFChars()を呼び出します。これは、UTF-8エンコーディングを実行せずにBase64でエンコードされた文字列を抽出します。
  5. 次に、JNIコードはBase-64データをデコードし、補足のUnicode文字を含む元のUTF-16(ワイド文字)要求文字列を生成します。

このようにして、Java文字列から補足Unicode文字を抽出する問題を回避します。代わりに、GetStringUTFChars(を呼び出す前に、データをBase-64 ASCII )、GetStringUTFChars()を使用してBase-64 ASCII文字を抽出し、Base-64データを幅の広い文字に変換し直します。

21
Moshe Rubin

これが私がこれをした方法です。

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警告:入力が無効です変更されたUTF-8:不正です継続バイト0x4

例; 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;
}
_
12
Berkay Turancı

Application.mkファイルを変更したときにこの問題が発生しました

この行から:

APP_STL := stlport_static

に:

APP_STL := gnustl_static

もう一度元に戻すと、問題が修正されました。

2
Daniel Ryan

私の意見では、それはバグではありません。

NewStringUTF変更されたUTF-8エンコーディングの文字の配列から新しいJava.lang.Stringオブジェクトを構築します。

変更されたUTF-8は標準のUTF-8ではありません。 変更されたUTF-8 を参照してください

ほとんどの場合、UTF-8でエンコードされた文字列は有効なModifiedUTF-8です。変更されたUTF-8とUTF-8は非常に似ているためです。ただし、Basic Multilingual Planeを超えるUnicode文字列に関しては、互換性がありません。

解決策:UTF-8バイトをJavaレイヤーと新しいString(bytes、 "UTF-8")に渡しますjstringをJNIに渡します。

1
Shaw

NewStringUTF()に渡す文字列は、有効である必要があります。Modified TF-8 。 start_Inauthroot()関数によって返される文字列が他のエンコーディングであるか、無効な文字列を返しているようです。文字列をJNI関数に渡す前に、文字列をUTF-8に変換する必要があります。または、 charset-aware Stringコンストラクター のいずれかを使用して、代わりにStringオブジェクトをビルドすることもできます。

1
kelnos

私も昨日から同じ問題に苦しんでいました。一日の後にようやく解決策を見つけました..この返信が誰かの日を救うことを願っています..

問題は、ネイティブ関数内で別の関数を呼び出していて、返された文字列を直接使用していたため、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

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

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);
}
0
Arshid KV

これは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());
}
0
hB0