web-dev-qa-db-ja.com

C ++文字列をJNI経由でJava

Androidアプリケーションを構築しているプロジェクトのC++側で作業しています。Javaアプリケーション(JNI経由)。私はこれまでこれを行ったことがなく、逆方向で作業している人々はC++の経験がなく、本当に役に立たないことを認めています。

私は次のコードを見つけました( ここ から)

_ #include <jni.h>  
    #include "ArrayHandler.h"  

    JNIEXPORT jobjectArray JNICALL Java_ArrayHandler_returnArray (JNIEnv *env, jobject jobj){        
      jobjectArray ret;  
      int i;  
      char *message[5]= {"first","second","third","fourth","fifth"};  
      ret= (jobjectArray)env->NewObjectArray(5,env->FindClass("Java/lang/String"),env->NewStringUTF(""));  

      for(i=0;i<5;i++) {  
        env->SetObjectArrayElement(ret,i,env->NewStringUTF(message[i]));  
      }  
      return(ret);  
    }  
_

しかし、これは私には意味がありません。ほとんどの場合、これをプログラムのC++側に組み込む方法がわからず、これがどのように機能するかを正確に理解できていません。コードはreturn(ret);行の実行時にメッセージを送信していますか?または、forループ内の行の実行中に?

理想的には、文字列/文字列配列を関数の最後ではなく「ライブ」で送信して、新しい関数を組み込む必要がないようにします。

私が見つけたコードは、私が望むもの(いくつかの適応を伴う)で機能しますか?私が探しているものは可能ですか?もしそうなら、どうすればそれを行うことができますか?

EDIT/UPDATE:JNIと用語を調べて一日を過ごしたので、ここと両方で達成しようとしていることを正しく伝えることができなかったと思います。 @jogabonitoの回答/返信へのコメントとして。

そうは言っても。私が取り組んでいるコードは、メッセージとプレゼンスの更新をAndroid Javaアプリケーション(JNI経由)にプッシュする必要があるIMクライアント用です。 Androidアプリケーションは更新をポーリングしません。情報を要求するために呼び出すJavaコードの関数を設定する方法を学ぶことができました。しかし、私はそうします新しいメッセージまたはプレゼンス情報(Jabberスタンザ文字列)をJavaコードに入ったときにプッシュする方法がわかりません。これを行う方法について私が見たすべてのコード(以下を参照)たとえば)Javaコード(env、class、methodidなど)から情報を取得する必要があるようです。

関数を呼び出すJavaコードではなく、私のc ++コードである場合にこれがどのように可能であるかは私には意味がありません。説明/ヘルプをいただければ幸いです。

_#include <string.h>
#include <stdio.h>
#include <jni.h>

jstring Java_the_package_MainActivity_getJniString( JNIEnv* env, jobject obj){

    jstring jstr = (*env)->NewStringUTF(env, "This comes from jni.");
    jclass clazz = (*env)->FindClass(env, "com/inceptix/Android/t3d/MainActivity");
    jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
    jobject result = (*env)->CallObjectMethod(env, obj, messageMe, jstr);

    const char* str = (*env)->GetStringUTFChars(env,(jstring) result, NULL); // should be released but what a heck, it's a tutorial :)
    printf("%s\n", str);

    return (*env)->NewStringUTF(env, str);
}
_
17
AeroBuffalo

共有した関数では、c ++コードで、NewObjectArrayを使用してオブジェクト配列を作成しています。次に、forループで、NewStringUTFを使用して文字列を作成し、SetObjectArrayElementを使用して配列のインデックスに格納します。これまで、オブジェクト配列はc ++コードにのみ認識され、Javaコードには認識されません。オブジェクト配列を返す場合にのみ、Javaアプリはにアクセスできます。それ。
文字列をc ++からJavaに送信する方法はいくつか考えられますが、意図したとおりではない場合があります。

  1. 文字列配列をネイティブ関数に渡します。ネイティブコードでは、GetObjectArrayElementを使用して各要素にアクセスし、SetObjectArrayElementを使用して要素を更新できます。不要だと思われる関数を呼び出さなければならないので、これはおそらく無意味です。

  2. Javaコードのフィールドとして定義された文字列がすでにある場合は、ネイティブからGetFieldIDおよびGetObjectFieldを使用してその文字列にアクセスし、更新できます。 SetObjectFieldを使用します。フィールドが更新されたことをJavaコードに通知する方法がわかりません(必要な場合))

[〜#〜]編集[〜#〜]
あなたが書いた更新された関数は、Javaレイヤーから呼び出されることを意図しています。これの手がかりは関数の名前ですJava_the_package_MainActivity_getJniString。ネイティブコンテキストからJavaコードを呼び出すには、Javaからenvおよびobjへの参照が必要になります。 方法これを取得するためのアプローチとして、独自のJavaクラスをAndroidのCで? ロードします。JNIでグローバル参照を使用する方法も調べる必要があります。

4
iamme

@Samの要求に応じて、変更されたUTF-8の使用を回避する方法を次に示します。これは、安全であるかどうかわからないためです。

NewStringUTFは、変更されたUTF-8エンコーディングから文字列を作成します。ユーザーデータで使用するのは正しくありません。変更されたUTF-8でエンコードされる可能性はほとんどありません。互換性を保つために、データ内の文字が制限されていることを期待できます。代わりに、適切に変換できます。

JNIは、API全体で変更されたUTF-8文字列を使用します。互換性があることがわかっている文字列、特にJava識別子(すべての通貨記号を除く)のリテラルを使用できます。

以下は、2つのネイティブメソッドの実装です。 2番目はほとんどの点で優れています。

このネイティブメソッドの場合:

private static native String getJniString();

実装は次のとおりです。

JNIEXPORT jstring JNICALL 
Java_the_Package_MainActivity_getJniString(JNIEnv *env, jclass)
{   
    std::string message = "Would you prefer €20 once "
                          "or ₹10 every day for a year?";

    int byteCount = message.length();
    jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
    jbyteArray bytes = env->NewByteArray(byteCount);
    env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);

    // find the Charset.forName method:
    //   javap -s Java.nio.charset.Charset | egrep -A2 "forName"
    jclass charsetClass = env->FindClass("Java/nio/charset/Charset");
    jmethodID forName = env->GetStaticMethodID(
      charsetClass, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
    jstring utf8 = env->NewStringUTF("UTF-8");
    jobject charset = env->CallStaticObjectMethod(charsetClass, forName, utf8);

    // find a String constructor that takes a Charset:
    //   javap -s Java.lang.String | egrep -A2 "String\(.*charset"
    jclass stringClass = env->FindClass("Java/lang/String");
    jmethodID ctor = env->GetMethodID(
       stringClass, "<init>", "([BLjava/nio/charset/Charset;)V");

    jstring jMessage = reinterpret_cast<jstring>(
      env->NewObject(stringClass, ctor, bytes, charset));

    return jMessage;
}

JNIは厄介です。したがって、ネイティブ文字列が「UTF-8」であるという知識をJava側に移動できる場合は、次のように実行できます。

private static String getJniString2()
{
    return new String(getJniStringBytes(), Charset.forName("UTF-8"));
}
private static native byte[] getJniStringBytes();

そして、はるかに単純な実装:

JNIEXPORT jbyteArray JNICALL Java_the_Package_MainActivity_getJniStringBytes(JNIEnv *env, jclass)
{   
    std::string message = "Would you prefer €20 once "
                          "or ₹10 every day for a year?";

    int byteCount = message.length();
    jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
    jbyteArray bytes = env->NewByteArray(byteCount);
    env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);

    return bytes;
}
14
Tom Blodget

通常、JNIでは、呼び出しはJVMからCコードに送られます。通常のパラダイムは次のとおりです。

  1. Javaプログラマーは、classとして宣言されたいくつかのメソッドを使用してJava nativeを作成します(実装なし)
  2. Javaプログラマーは、classjavacでコンパイルします。
  3. Javaプログラマーはコンパイルされた.classファイルに対してjavahを実行します。これにより、.hヘッダーファイルが生成されます。
  4. Cプログラマー#include新しいヘッダーファイルとインターフェースの実装

これを逆方向(Javaとの接続を開始するCコード)で行うことについて私が見た唯一の例は、Cコードに実際にJVMを作成させることです。

コードサンプルに関する質問に答えるために、作成中のJava文字列は、コード実行の最後にreturnステートメントとともに返されます。論理的には、これは、その実行スレッドのプログラムフローが返されるときです。 JVMに戻ります。

1
Tim Bender

C-stringをjstringに変換して返すことができます。例は次の線に沿って何かを見るでしょう:

JNIEXPORT jstring JNICALL Java_Class_Method(jstring data) 
{

    // jstring to char *
    const char *cStr = (*env)->GetStringUTFChars(env, data, NULL);

    // convert char * to jstring and return it
    return ((*env)->NewStringUTF(env, cStr));
}
0
murfito