0

データのブロックを解凍したいAndroid用のネイティブコードを書いています。ネイティブ JNI 関数から Java メソッドを呼び出しています。この Java メソッドは BitmapFactory を呼び出し、次のようにメモリを割り当てようとします。

int[] pixels = new int[width * height];

プログラムがクラッシュしたり、停止したりしたように見えて、「一時停止中のスピン」が発生し、その後、VM がシャットダウンする前に logcat に多くのメッセージが表示されます。

詳細に入る前に全体像を示すために、圧縮された画像のバイト配列を圧縮されていwidth*height*bppない画像に変換して、ネイティブ コードに戻そうとしています。Android Java BitmapFactory を使用して解凍を実行しようとしていますが、正常に動作しているようです。圧縮されたバイト ブロックは、以前にネイティブ コードに読み込まれた JPG または PNG 画像のものです (私のシステムではディスクから読み取れません)。

シーケンスは少し複雑です。何が関連するのかわからないので、すべて言います。 class MyRenderer implements GLSurfaceView.Rendererメソッドは、 クラスで定義されているonSurfaceCreatedネイティブを呼び出します。init()MyRenderer

static {System.loadLibrary("glprog");
}
public native void init(int dummy_variable);

ネイティブ コード内では、次の関数が呼び出されます。

JNIEXPORT void JNICALL Java_com_pitransviewersingleimageres_MyRenderer_init(JNIEnv * env, jobject obj,  jint dummy_variable) {
glprog_init(dummy_variable);
}

char glprog_init(int dummy_variable)次に、それ自体がネイティブ wrapper_uncompress_image_by_os(overlay_compressed_data,overlay_compressed_data_len,... を呼び出す別のネイティブ関数を呼び出します... ネイティブ関数は次のとおりです。

char wrapper_uncompress_image_by_os(unsigned char *compressed_data, int compressed_len,                     //input compressed data
                                char *data_name_string, char *suffix,                                   //name and type
                                int slot_num)  {
jstring jnamestring = (*preset_env)->NewStringUTF(preset_env,data_name_string);
jstring jsuffix = (*preset_env)->NewStringUTF(preset_env,suffix);
//create byte[] and copy data in
jbyteArray retArray = (*preset_env)->NewByteArray(preset_env, compressed_len);
if(retArray==NULL) {__android_log_write(ANDROID_LOG_INFO, "NATIVE","ERROR wrapper_uncompress_image_by_os: calling NewByteArray()");return-1;}
jbyte *javaptr = (*preset_env)->GetPrimitiveArrayCritical(preset_env, (jarray)retArray, 0);
if(javaptr==NULL) {__android_log_write(ANDROID_LOG_INFO, "NATIVE","ERROR wrapper_uncompress_image_by_os: calling GetPrimitiveArrayCritical()");return-1;}
memcpy(javaptr,compressed_data,compressed_len); 
(*preset_env)->ReleasePrimitiveArrayCritical(preset_env, retArray, javaptr, 0);

if((preset_javaobject!=NULL)&&(preset_method_id_convertimagefromnative!=NULL))
   {
   jthrowable exception;
   //call java function
   (*preset_env)->CallVoidMethod(preset_env,     preset_javaobject,preset_method_id_convertimagefromnative, 
                             retArray,compressed_len,
                             jnamestring,jsuffix,slot_num);

nativeを呼び出したconvertImageFromNative()のと同じ Java クラスのメソッドを呼び出します。 MyRendererinit()

   public void convertImageFromNative(final byte[] data, int data_len, final String data_name_string, final String suffix, final int slot_num) {
    //writeFile(data,data_name_string);
    Bitmap bitmapqq=bytes2bitmap(data);

}

コメントを外すwriteFile()と、すべてのデータが SD カードに正常に書き込まれます。bytes2bitmap()私が問題を抱えているのは内部です。

このbytes2bitmap()クラスは「通常の」Java から呼び出すことができますが、ネイティブから呼び出される this から呼び出すと、そのint[] pixels = new int[width * height];行でクラッシュします。

最後に、クラッシュを引き起こすコードは次のとおりです。

Bitmap bytes2bitmap(byte[] bytes) {
    Log.d("startup", "Entered MyRenderer.bytes2bitmap()");
    BitmapFactory.Options opt = new BitmapFactory.Options(); 
    opt.inDither = true; 
    opt.inPreferredConfig = Bitmap.Config.ARGB_8888; 
    Bitmap bitmap=BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt);
    if(bitmap==null) Log.d("running", "ERROR: GLESActivity.java: BitmapFactory() returns null");
    int width = bitmap.getWidth(); 
    int height = bitmap.getHeight(); 
    bytes=null;   //not sure if I should be freeing memory here, but it seems to work
    int[] pixels = new int[width * height];    //crash happens here
    //crashes before doing the following
    bitmap.getPixels(pixels, 0, width, 0, 0, width, height); 
    pixels=null;
    return bitmap;
}

圧縮されていないデータを適切にネイティブ コードに戻す部分にはまだ到達していませんが、最初にこれに対処する必要があると考えました。

ネイティブコードで配列を作成しint[]て渡すことができましたが、問題は、圧縮されていない画像のサイズがわからないことです。

前もってありがとう、マーク

4

1 に答える 1

1

問題を修正しました。表示されていないコードの間違いでした。

問題は、(*preset_env)->CallVoidMethod(preset_env, preset_javaobject,preset_method_id_convertimagefromnative,ネイティブ コードでの呼び出しにありました。変数 preset_env、_javaobject、_method_id_ が正しく設定されていませんでした。ネイティブ関数 _setupnative2java() を使用して後で再利用できるように、起動時に一度設定します。私のプロジェクトには 3 つのクラスがあります。私の間違いは、Java メソッド bytes2bitmap() を実際に含んでいた MyRenderer クラスではなく、最初の Activity クラスから _setupnative2java() を呼び出すことにありました。デバッグ中に、この関数の別のコピーを最初の Activity クラスに同じ名前で配置しました。そのため、ある程度は機能しましたが、ポインターが間違っていました。MyRenderer への変更と logcat 呼び出しが機能するという点で奇妙でした。

実際には MyRenderer クラスから呼び出しましたが、instancename.MyRenderer.setupnative2java のスタートアップ Activity クラスから呼び出しました。しかし、おそらくこの別のクラスから呼び出されたため、MyRenderer のポインターを取得していませんでした。さらに、MyRenderer が実際にどのように「初期化」されたのかわかりません。あなたがこれに頭脳を費やしたなら、私の謝罪。

void Java_com_pitransviewersingleimageres_MyRenderer_setupnative2java(JNIEnv* env, jobject,javaThis, jint unused_variable) 
    {
    jthrowable exception;
    jclass class_id;
    preset_env=env;
    preset_javaobject=javaThis;
    class_id = (*env)->GetObjectClass(env, javaThis);
    if(class_id!=NULL)
       {
       preset_method_id_convertimagefromnative = (*env)->GetMethodID(env, class_id, "convertImageFromNative", "([BILjava/lang/String;Ljava/lang/String;I)V");  
       }
...
于 2012-09-15T19:04:18.347 に答える