Управление памятью Android, вызывающее класс Java из собственного JNI и объявляющее данные (для преобразования изображений)

Я пишу собственный код для Android, где хочу распаковать блок данных. Я вызываю метод Java из собственной функции JNI. Этот метод Java вызывает BitmapFactory, а затем пытается выделить часть памяти с помощью:

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

когда кажется, что программа дает сбой или зависает или что-то в этом роде, что дает мне "вращение при приостановке", за которым следует много извержений в 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)которая сама вызывает нативную обертку_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);

Который вызывает метод convertImageFromNative() в том же классе Java MyRenderer, который вызывал родной init().

   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, но при вызове из этого, который вызывается из нативного, происходит сбой в строке 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[] вниз в собственном коде, чтобы отказаться от него, проблема в том, что я не знаю размер несжатого изображения.

Заранее спасибо, Марк


person Mark3.14159265359    schedule 14.09.2012    source источник


Ответы (1)


Я исправил свою проблему, это была ошибка в коде, который я не показывал.

Проблема заключалась в вызове (*preset_env)->CallVoidMethod(preset_env, preset_javaobject,preset_method_id_convertimagefromnative, в нативном коде. Неправильно заданы переменные preset_env,_javaobject,_method_id_. Я установил их при запуске один раз для повторного использования позже, используя встроенную функцию _setupnative2java(). У меня есть три класса в моем проекте. Моя ошибка заключалась в вызове _setupnative2java() из моего первого класса Activity, а не из класса MyRenderer, который на самом деле содержал java-метод bytes2bitmap(). И во время отладки я поместил еще одну копию этой функции в первый класс Activity с тем же именем. Так что это вроде сработало, но его указатели были неправильными. Было странно, что модификации и вызовы logcat для MyRenderer работали.

На самом деле я вызывал его из класса MyRenderer, но из класса запуска Activity с instancename.MyRenderer.setupnative2java. Но, возможно, поскольку он был вызван из этого другого класса, он не принял указатели для 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");  
       }
...
person Mark3.14159265359    schedule 15.09.2012