Исключение OutOfMemory при загрузке растрового изображения из внешнего хранилища - PullRequest
17 голосов
/ 05 ноября 2010

В моем приложении я загружаю пару изображений из файлов JPEG и PNG.Когда я помещаю все эти файлы в каталог ресурсов и загружаю его таким образом, все в порядке:

InputStream stream = getAssets().open(path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);

Но когда я пытаюсь загрузить те же самые изображения с SD-карты, я получаю исключение OutOfMemory!

InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);

Вот что я получаю в журнале:

11-05 00:53:31.003: ERROR/dalvikvm-heap(13183): 827200-byte external allocation too large for this process.
11-05 00:53:31.003: ERROR/GraphicsJNI(13183): VM won't let us allocate 827200 bytes
...
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-05 00:53:31.053: ERROR/AndroidRuntime(13183):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
...

Почему это может произойти?

ОБНОВЛЕНИЕ: Опробовал оба из них на реальном устройстве - кажетсяЯ не могу загрузить более 12 МБ растровых изображений в то, что называется «внешней памятью» (это не SD-карта).

Ответы [ 13 ]

0 голосов
/ 21 июля 2011

Одной из самых распространенных ошибок, которые я обнаружил при разработке Android-приложений, является ошибка «java.lang.OutOfMemoryError: Размер растрового изображения превышает бюджет виртуальной машины».Я часто обнаруживал эту ошибку в действиях, использующих множество растровых изображений после изменения ориентации: действие уничтожается, создается снова и макеты «раздуваются» из XML, потребляя память виртуальной машины, доступную для растровых изображений.

растровые изображения на предыдущемсборщик действий неправильно освобожден сборщиком мусора, потому что он пересек ссылки на свою деятельность.После многих экспериментов я нашел довольно хорошее решение для этой проблемы.

Сначала установите атрибут «id» в родительском представлении вашего макета XML:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Затем в onDestroy() вашего Activity, вызовите метод unbindDrawables (), передав ссылку на родительский View, а затем выполните System.gc ()

    @Override
    protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
    }

    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        }
    }

Этот метод unbindDrawables () рекурсивно исследует дерево представлений и:

  1. Удаляет обратные вызовы для всех фоновых элементов рисования
  2. Удаляет дочерние элементы в каждой группе видов
0 голосов
/ 10 ноября 2010

Вы не должны зависеть от GC для переработки вашей растровой памяти.Вы должны четко переработать растровое изображение, когда оно не нужно.

См. Метод «Растровое изображение»:

void recycle () Освободите память, связанную с пикселями этого растрового изображения, и пометьте растровое изображение как «мертвое»", то есть вызовет исключение, если getPixels () или setPixels () будет вызван, и ничего не будет рисовать.

0 голосов
/ 09 ноября 2010

Попробуйте по-другому ...

Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path");
...