java.lang.OutOfMemoryError: размер растрового изображения превышает бюджет виртуальной машины - Android - PullRequest
157 голосов
/ 22 декабря 2009

Я разработал приложение, которое использует много изображений на Android.

Приложение запускается один раз, заполняет информацию на экране (Layouts, Listviews, Textviews, ImageViews и т. Д.), И пользователь читает информацию.

Нет анимации, никаких спецэффектов или чего-либо, что может заполнить память. Иногда ничья может измениться. Некоторые из них - ресурсы Android, а некоторые - файлы, сохраненные в папке на SDCARD.

Затем пользователь завершает работу (выполняется метод onDestroy, и приложение остается в памяти виртуальной машиной), а затем в какой-то момент пользователь снова входит.

Каждый раз, когда пользователь входит в приложение, я могу видеть, как увеличивается и увеличивается объем памяти, пока пользователь не получит java.lang.OutOfMemoryError.

Так, каков наилучший / правильный способ обработки множества изображений?

Должен ли я поместить их в статические методы, чтобы они не загружались все время? Нужно ли чистить макет или изображения, используемые в макете, особым образом?

Ответы [ 13 ]

96 голосов
/ 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() вашей деятельности вызовите метод unbindDrawables(), передав ссылку на родительский вид, а затем выполните 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. Удаляет детей в каждой группе просмотра
70 голосов
/ 23 декабря 2009

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

Трудно сказать, почему это происходит, не глядя на ваш код. Однако в этой статье есть несколько советов, которые могут помочь:

http://android -developers.blogspot.de / 2009/01 / избегая-памяти leaks.html

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

10 голосов
/ 12 июня 2012

Чтобы избежать этой проблемы, вы можете использовать собственный метод Bitmap.recycle() перед null -ing Bitmap объектом (или установкой другого значения). Пример:

public final void setMyBitmap(Bitmap bitmap) {
  if (this.myBitmap != null) {
    this.myBitmap.recycle();
  }
  this.myBitmap = bitmap;
}

И затем вы можете изменить myBitmap без вызова System.gc(), например:

setMyBitmap(null);    
setMyBitmap(anotherBitmap);
7 голосов
/ 11 апреля 2012

Это объяснение может помочь: http://code.google.com/p/android/issues/detail?id=8488#c80

"Быстрые советы:

1) НИКОГДА не вызывайте System.gc () самостоятельно. Это было распространено как исправление здесь, и это не работает. Не делай этого. Если вы заметили в моем объяснении, перед тем как получить OutOfMemoryError, JVM уже запускает сборку мусора, поэтому нет причин делать это снова (это замедляет работу вашей программы). Выполнение одного в конце вашей деятельности просто скрывает проблему. Это может привести к тому, что растровое изображение будет помещено в очередь финализатора быстрее, но нет причины, по которой вы не могли бы просто вызвать recycle для каждого растрового изображения.

2) Всегда вызывайте recycle () для растровых изображений, которые вам больше не нужны. По крайней мере, в onDestroy вашей деятельности выполните и переработайте все растровые изображения, которые вы использовали. Кроме того, если вы хотите, чтобы экземпляры растрового изображения собирались из кучи dalvik быстрее, не повредит очистить любые ссылки на растровое изображение.

3) Вызов recycle (), а затем System.gc () по-прежнему может не удалить растровое изображение из кучи Dalvik. НЕ ЗАБУДЬТЕ об этом. recycle () выполнил свою работу и освободил родную память, потребуется лишь некоторое время, чтобы пройти шаги, которые я описал ранее, чтобы фактически удалить растровое изображение из кучи Dalvik. Это не имеет большого значения, потому что большой кусок собственной памяти уже свободен!

4) Всегда предполагайте, что в фреймворке в последний раз есть ошибка. Dalvik делает именно то, что должен делать. Это может быть не то, что вы ожидаете или чего вы хотите, а то, как это работает. «

7 голосов
/ 23 декабря 2009

Я столкнулся с этой проблемой. Куча довольно маленькая, поэтому эти изображения могут довольно быстро выйти из-под контроля в отношении памяти. Один из способов - дать сборщику мусора подсказку собирать память на растровом изображении, вызвав его метод recycle .

Кроме того, метод onDestroy не гарантированно вызывается. Возможно, вы захотите перенести эту логику / очистить в действие onPause. Ознакомьтесь с диаграммой / таблицей жизненного цикла активности на этой странице для получения дополнительной информации.

5 голосов
/ 07 мая 2012

Следующие пункты действительно очень мне помогли. Могут быть и другие моменты, но они очень важны:

  1. По возможности используйте контекст приложения (вместо activity.this).
  2. Остановите и отпустите ваши темы в методе действия onPause ()
  3. Отпустите ваши представления / обратные вызовы в методе действия onDestroy ()
5 голосов
/ 20 декабря 2010

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

P.S. Сначала я попытался уменьшить размер изображения, не уменьшая его. Это не остановило ошибку.

4 голосов
/ 16 августа 2011

Предлагаю удобный способ решения этой проблемы. Просто присвойте атрибуту «android: configChanges» значение, как указано в файле Mainfest.xml, для вашей операции с ошибками. как это:

<activity android:name=".main.MainActivity"
              android:label="mainActivity"
              android:configChanges="orientation|keyboardHidden|navigation">
</activity>

первое решение, которое я выдал, действительно снизило частоту ошибок OOM до низкого уровня. Но это не решило проблему полностью. И тогда я выдам 2-е решение:

Как подробно описал OOM, я использовал слишком много оперативной памяти. Поэтому я уменьшаю размер изображения в ~ / res / drawable моего проекта. Например, переоцененное изображение, имеющее разрешение 128х128, может быть изменено до 64х64, что также подходит для моего приложения. И после того, как я сделал это с кучей картинок, ошибка OOM больше не возникает.

3 голосов
/ 24 апреля 2012

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

Мое приложение будет выдавать ошибку вне памяти всякий раз, когда пользователь переходит от одного действия к другому. Установка для моих drawables значения null и вызов System.gc () не сработали, равно как и переработка моих bitmapDrawables с getBitMap (). Recycle (). Android будет продолжать выдавать ошибку «outofmemory» при первом подходе и будет выдавать сообщение об ошибке «холст» всякий раз, когда он пытается использовать переработанный растровый рисунок со вторым подходом.

Я выбрал даже третий подход. Я установил все виды на ноль, а фон - на черный. Я делаю эту очистку в моем методе onStop (). Этот метод вызывается, как только действие перестает быть видимым. Если вы сделаете это в методе onPause (), пользователи увидят черный фон. Не идеально. Что касается этого в методе onDestroy (), нет гарантии, что он будет вызван.

Чтобы предотвратить появление черного экрана, если пользователь нажимает кнопку «Назад» на устройстве, я перезагружаю действие в методе onRestart (), вызывая методы startActivity (getIntent ()) и затем finish ().

Примечание: не обязательно менять фон на черный.

1 голос
/ 07 мая 2012

Методы BitmapFactory.decode *, описанные в уроке Загрузка больших растровых изображений эффективно , не должны выполняться в главном потоке пользовательского интерфейса, если исходные данные считываются с диска или из сетевого расположения (или вообще любого другого). источник, отличный от памяти). Время загрузки этих данных непредсказуемо и зависит от множества факторов (скорость чтения с диска или сети, размер изображения, мощность процессора и т. Д.). Если одна из этих задач блокирует поток пользовательского интерфейса, система помечает ваше приложение как неотвечающее, и пользователь может закрыть его (для получения дополнительной информации см. Проектирование для отзывчивости).

...