15

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

このコードは、2.2、2.3などの以前のAndroid4.0バージョンで完全に機能します。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*バイトにガベージ値を追加しています。

  1. なぜこれが起こっているのかについての考え
  2. 上記を達成するための代替ソリューションは大歓迎です

誰かが私を助けてくれたら本当にありがたいです。前もって感謝します

私をregds

4

10 に答える 10

22

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

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

問題の分析

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

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

私の場合、結果の文字列は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データをワイド文字に変換し直します。

于 2014-10-23T06:55:20.790 に答える
13

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

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:不正な継続バイト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;
}
于 2015-12-11T08:14:09.197 に答える
11

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

于 2012-09-04T19:40:53.000 に答える
2

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

この行から:

APP_STL := stlport_static

に:

APP_STL := gnustl_static

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

于 2013-07-04T05:30:51.710 に答える
1

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

于 2012-08-26T22:28:22.973 に答える
1

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

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

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

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

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

于 2017-08-22T13:55:17.810 に答える
1

私にとっての解決策は、コンテンツをconstchar*に配置することでした。

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);
于 2017-09-15T01:09:07.217 に答える
1

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

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

于 2018-05-27T16:27:54.457 に答える
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());
}
于 2013-11-04T23:10:12.817 に答える
0

candroidndkは次のように動作しています

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);
}
于 2018-04-24T13:28:34.647 に答える