2013-08-06 7 views
7

C++で書かれたJNI関数を使用して、バイト配列を入力としてセグメント化し、バイト配列の配列をJavaに返します。C++でbyte [] []を作成し、JNIを使​​用してJavaに戻す

JNIEXPORT jobjectArray JNICALL Java_class_method(JNIEnv * env、jobject o、jbyteArray dataToSegment); Java側で

、それはような単純なものです:

byte[] arg = getRandomByteArray(); 
Object[] retVal = x.method(arg); 

、私は少しトリッキーなことJNIの一部を見つけることです。私はオブジェクトの配列を作成しようとしています。それぞれの配列はバイト配列です。これは、JNIが限られた数のJavaタイプしか定義していないためです。 jbyteArray型とjobjectArray型がありますが、jarrayOfByteArrays型はありません。

だから私は、各オブジェクトが新しいバイト[1024]のように初期化され、オブジェクトの私の配列を作成します。

jbyteArray tmp = (jbyteArray) env->GetObjectArrayElement(retVal, i); 
env->SetByteArrayRegion(tmp, 0, size, (jbyte*) sourceBuffer); 
env->SetObjectArrayElement(retVal, i, (jobject) tmp); // <--- Questionable line 
:ような何かをやって、私は、この配列内のすべてのインデックスを反復

jobjectArray retVal = env->NewObjectArray(numSegs, env->FindClass("[Ljava/lang/Object;"), env->NewByteArray(1024)); 

など、すべてがうまく機能します。しかし、各バイト配列を可変長にしたい場合はどうすればよいですか?つまり、バイト配列の配列を「ギザギザ」にしたいのです。 NewObjectArray()の最後のパラメータとして初期値として渡すものは何ですか? jobjectArrayの作成時に初期化を防ぐために初期値として0を渡してみましたが、新しいjbyteArrayオブジェクトをSetObjectArrayElement()に渡すように割り当てていましたが、SetObjectArrayElementを呼び出そうとするたびにArrayStoreExceptionをスローしてしまいました。実際、GetObjectArrayElement()の代わりにtmpオブジェクトに新しいjbyteArrayを割り当てても、SetObjectArrayElement()が呼び出されたときに同じ例外がスローされます。最後のコード行が問題になる理由はありますか? jbyteArrayをパラメータとしてSetObjectArrayElement()を呼び出すことはできませんか?

さらに調査すると、「疑問のある行」は何もしていないようです。私がコメントアウトすると、tmpに加えられたすべての変更がretValに反映されます。これはSetByteArrayRegionの呼び出しがcopyではなくjobjectArrayの "中"のバイト配列を扱っているからです。すべての行(むしろすべての一次元のバイト配列)が同じ長さであれば、それで十分です。しかし、彼らはそうではありません。このオブジェクト配列の行の1つに新しいバイト配列を割り当てるにはどうすればよいですか?

TL; DR: JNIではjbyteArraysのjobjectArrayがあります。 jbyteArraysの1つを、NewByteArrayで作成した新しいものに置き換えるにはどうすればよいですか?ヒント:env-> SetObjectArrayElement(retVal、i、(jobject)env-> NewByteArray(size)); //動作しません。

答えて

2

あなたの代わりにJNIのJNAを使用するオプションを持っている場合、あなたはJNAのAPIドキュメントページで提供された例と同様のものを使用することができるはず:ネイティブをマップするには

http://jna.java.net/javadoc/overview-summary.html#arrays

」多次元配列では、完全なネイティブ配列に相当する要素数を持つ1次元Java配列を使用してください。 "

+0

ありがとうございますが、残念ながら私はここでJNIに固執しています。 –

+0

@SzymonSmakolskiはJNIコードからNewDirectByteBufferを使用してバッファを割り当てます。 – Eugene

+0

私の[限られた]理解に基づいて、NewDirectByteBufferはネイティブ側で割り当てられたメモリで動作します。私は、JVMがObject []に詰めることができるたくさんのJava byte []オブジェクトを割り当てるようにしています。あなたが提案するアプローチでは、JVMではなくC++の新しい演算子を使ってバイト配列のそれぞれを割り当てる必要があります。私はむしろJVMが実際にJava側で終わるすべてのデータの割り当てを処理させるでしょう。 –

3

JNIでバイト配列の配列を正常に返しました。

static jbyteArray NewJavaStringBytes(JNIEnv* env, const char *src) { 
    jbyteArray retVal = (*env)->NewByteArray(env, strlen(src)); 
    jbyte *buf = (*env)->GetByteArrayElements(env, retVal, NULL); 
    strcpy(buf, src); 
    printf(" NewJavaStringBytes: Created java byte array: %s.\n", buf); 
    (*env)->ReleaseByteArrayElements(env, retVal, buf, 0); 

    return retVal; 
} 

JNIEXPORT jobjectArray JNICALL Java_TestJniString_printStringArray 
    (JNIEnv *env, jobject thisObj, jobjectArray jObjArr) { 
    int numStr = (*env)->GetArrayLength(env, jObjArr); 
    int idx = 0; 
    jobjectArray strArr = NULL; 
    jbyte *curStr = NULL; 
    jclass arrayElemType = (*env)->FindClass(env, "[B"); 

    const char *retStrs[] = {"one", "two", "three", "twenty-five", "TESTING!!"}; 
    const int RET_LEN = sizeof(retStrs)/sizeof(char *); 

    printf("Creating java object array of %d items.\n", RET_LEN); 
    //Create new array of byte array 
    jobjectArray testArray = (*env)->NewObjectArray(env, 
                RET_LEN, 
                arrayElemType, 
                (*env)->NewByteArray(env, 1)); 

    for (idx = 0; idx < RET_LEN; ++idx) { 
     printf("Creating java byte array %d from str: %s.\n", idx, retStrs[idx]); 
     jbyteArray str = NewJavaStringBytes(env, retStrs[idx]); 
     (*env)->SetObjectArrayElement(env, testArray, idx, str); 
     (*env)->DeleteLocalRef(env, str); 
    } 

    printf("printStringArray: Printing %d strings:\n", numStr); 
    for (idx = 0; idx < numStr; ++idx) { 
     strArr = (*env)->GetObjectArrayElement(env, jObjArr, idx); 
     curStr = (*env)->GetByteArrayElements(env, strArr, NULL); 
     printf(" %s.\n", curStr); 
     (*env)->ReleaseByteArrayElements(env, (jbyteArray)strArr, curStr, 0); 
    } 

    (*env)->DeleteGlobalRef(env, arrayElemType); 

    return testArray; 
} 

この例では、バイト配列の配列を受け取り、バイト配列の配列を返します:あなたはjオブジェクトの配列を取り込むよう、新しいものと交換するので、初期サイズはバイト配列の問題ではありません。これはC言語(C++ではなく)であるので、jni呼び出しは(* env) - >(env、...);です。 C++ではラッパーが呼び出しを単純化します。また、これは、Javaコードがネイティブレイヤーに送信する前に文字列のバイト配列バージョンにヌルターミネーターを追加することを前提としています。あなたがそれを信じることができないならば、C/C++コードにヌル項を手動で追加する必要があります。なぜならJavaはStringから変換するときにbyte []に​​対してこれをしないからです。

希望に役立ちます。

関連する問題