java.lang.OutOfMemoryError: размер растрового изображения превышает бюджет VM – Android

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

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

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

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

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

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

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

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

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

    http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html

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

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

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

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

        ... 

    Затем в 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. Удаляет детей из каждой группы просмотра

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

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

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

     setMyBitmap(null); setMyBitmap(anotherBitmap); 

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

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

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

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

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

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

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

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

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

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

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

    1. Используйте контекст приложения (а не activity.this), где это возможно.
    2. Остановите и отпустите свои streamи в onpause () методе деятельности
    3. Отпустите свои виды / обратные вызовы в onDestroy () методе деятельности

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

       

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

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

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

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

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

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

    Примечание: нет необходимости менять фон на черный.

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

    Ну, я пробовал все, что нашел в Интернете, и никто из них не работал. Вызов System.gc () только уменьшает скорость приложения. Утилизация растровых изображений в onDestroy тоже не работала для меня.

    Единственное, что работает сейчас, это иметь статический список всех растровых изображений, чтобы битмапы выживали после перезапуска. И просто используйте сохраненные растровые изображения вместо создания новых при каждом повторном запуске.

    В моем случае код выглядит так:

     private static BitmapDrawable currentBGDrawable; if (new File(uriString).exists()) { if (!uriString.equals(currentBGUri)) { freeBackground(); bg = BitmapFactory.decodeFile(uriString); currentBGUri = uriString; bgDrawable = new BitmapDrawable(bg); currentBGDrawable = bgDrawable; } else { bgDrawable = currentBGDrawable; } } 

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

     ImageView ivBg = (ImageView) findViewById(R.id.main_backgroundImage); ivBg.setImageDrawable(null); ivBg.setImageDrawable(getResources().getDrawable(R.drawable.new_picture)); 

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

     /** * Lightweight cache for Bitmap objects. * * There is no thread-safety built into this class. * * Note: you may wish to create bitmaps using the application-context, rather than the activity-context. * I believe the activity-context has a reference to the Activity object. * So for as long as the bitmap exists, it will have an indirect link to the activity, * and prevent the garbaage collector from disposing the activity object, leading to memory leaks. */ public class BitmapCache { private Hashtable> hashtable = new Hashtable>(); private StringBuilder sb = new StringBuilder(); public BitmapCache() { } /** * A Bitmap with the given width and height will be returned. * It is removed from the cache. * * An attempt is made to return the correct config, but for unusual configs (as at 30may13) this might not happen. * * Note that thread-safety is the caller's responsibility. */ public Bitmap get(int width, int height, Bitmap.Config config) { String key = getKey(width, height, config); ArrayList list = getList(key); int listSize = list.size(); if (listSize>0) { return list.remove(listSize-1); } else { try { return Bitmap.createBitmap(width, height, config); } catch (RuntimeException e) { // TODO: Test appendHockeyApp() works. App.appendHockeyApp("BitmapCache has "+hashtable.size()+":"+listSize+" request "+width+"x"+height); throw e ; } } } /** * Puts a Bitmap object into the cache. * * Note that thread-safety is the caller's responsibility. */ public void put(Bitmap bitmap) { if (bitmap==null) return ; String key = getKey(bitmap); ArrayList list = getList(key); list.add(bitmap); } private ArrayList getList(String key) { ArrayList list = hashtable.get(key); if (list==null) { list = new ArrayList(); hashtable.put(key, list); } return list; } private String getKey(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); Config config = bitmap.getConfig(); return getKey(width, height, config); } private String getKey(int width, int height, Config config) { sb.setLength(0); sb.append(width); sb.append("x"); sb.append(height); sb.append(" "); switch (config) { case ALPHA_8: sb.append("ALPHA_8"); break; case ARGB_4444: sb.append("ARGB_4444"); break; case ARGB_8888: sb.append("ARGB_8888"); break; case RGB_565: sb.append("RGB_565"); break; default: sb.append("unknown"); break; } return sb.toString(); } } 
    Interesting Posts

    о chrome.tabs.executeScript (id, details, callback)

    Выделение несмежного свободного пространства в существующий раздел

    Обнаружение флагов компиляции GCC двоичного кода

    Исключение исключений нарушения доступа?

    Отменить список фреймов данных

    Как вертикально разделить широкоэкранный экран на два виртуальных рабочих пространства на Ubuntu / Gnome?

    Какие подходы доступны для фиктивных данных времени разработки в WPF?

    Преобразование DisplayPort и / или HDMI в DVI-D?

    Изменение размера изображения до полной ширины и переменной высоты с помощью Picasso

    Dm-crypt + luks: Можно ли создать отдельный заголовок без сохранения его на зашифрованном устройстве luks?

    Есть ли на C # String Tokenizer, например, Java?

    Как создать DVD-диск из файла MP4 с помощью Windows DVD Maker?

    Откройте Dns на моем маршрутизаторе, давая проблемы

    Сколько места я должен зарезервировать для резервных копий Time Machine?

    Как уменьшить зону прокрутки в области трекпада Windows 8?

    Давайте будем гением компьютера.