2013-06-10 8 views
2

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

Ниже приведен мой код получения изображения с SD-карты, установка его и последующее уничтожение. Что я делаю неправильно?

WeakReference<Bitmap> newImageRef; 
    public void setImageFromFile(File source){ 
     if(source.exists()){ 

      Bitmap newImage = BitmapFactory.decodeFile(source.getAbsolutePath()); 
      newImageRef = new WeakReference<Bitmap>(newImage); 
      if(newImage != null){ 
       this.setImageBitmap(newImage); 
      } 
     } 
    } 

    @Override 
    protected void onDetachedFromWindow() { 
     Bitmap newImage = newImageRef.get(); 
     if (newImage != null) { 
     newImage.recycle(); 
     newImage = null; 
     } 


     Drawable drawable = getDrawable(); 
     if (drawable instanceof BitmapDrawable) { 
      BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 
      Bitmap bitmap = bitmapDrawable.getBitmap(); 
      if (bitmap != null){ 
      bitmap.recycle(); 
      } 
     } 
     this.setImageResource(0); 
     newImage = null; 
     newImageRef = null; 
     System.gc(); 


     super.onDetachedFromWindow(); 
    } 
+0

Я удивлен, что он не падает. Вы по существу перерабатываете одно и то же растровое изображение дважды (один раз, как newImageRef, затем как вытягиваемый). Вы уверены, что вызывается onDetachedFromWindow? Вы проследили это? – msh

+0

@msh Ну, у меня есть 'if (bitmap! = Null) {' во втором цикле, поэтому он не падает. И да, этот метод называется. Я попытался получить кучу кучи, и он говорит, что Bitmap и BitmapDrawable занимают больше всего памяти. –

+0

Bitmap.recycle() не делает его нулевым, он освобождает внутреннее растровое хранилище. Если вы освободите его дважды, вы можете потерпеть крах. – msh

ответ

2

Если вы используете Android версии 3.0> Вы не должны вызывать recycle() как дс будет убирать растровые изображения на свой собственный в конце концов, пока нет никаких ссылок на него. Таким образом, можно безопасно удалять вызовы рециркуляции. Здесь они ничего не делают.

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

Удачи.

1

Попробуйте использовать Drawable.setCallback(null);. В Android 3.0 или новее вам даже не нужно recycle из-за более автоматического управления памятью или сбора мусора, чем в более ранних версиях. См. Также this. Он имеет хорошую информацию об управлении растровой памятью в Android.

0

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

Основная проблема заключается в том, что GC, похоже, немного странен при работе с растровыми изображениями. Если вы просто удалите жесткие ссылки, они будут иногда висеть на битмапах еще немного, возможно, из-за того, как выделяются объекты Bitmap. Как говорилось, прежде чем переработка не потребуется на Android 3+. Поэтому, если вы добавляете большое количество растровых изображений, может потребоваться некоторое время, пока эта память не станет свободной. Или утечка памяти может быть в части вашего кода. Для сложных проблем, подобных этому, необходимо проверить уже проверенные решения, прежде чем повторять их.

Это приводит ко второму вопросу: использование слабых рефренсов. Это не может предназначаться основную проблему, но, как правило, не очень хорошая картина, чтобы использовать для изображения кэшей в Android 2.3+, как написанные android doc:

Примечание: В прошлом популярный реализация кэш-памяти был SoftReference или Кэш растрового изображения WeakReference, однако это не рекомендуется. Начиная с Android 2.3 (API уровня 9) сборщик мусора более агрессивен, собирая мягкие/слабые ссылки, что делает их довольно неэффективными. Кроме того, до Android 3.0 (уровень API 11) данные резервного копирования растрового изображения были сохранены в собственной памяти, которая не была выпущена предсказуемым образом, что потенциально может привести к кратковременному превышению пределов памяти и сбоя.

Теперь можно использовать LRU Caches, что подробно объясняется в ссылке, приведенной около caching.

Смежные вопросы