Android: исключение из памяти в галерее

Мое приложение отображает список из 9 категорий, и каждая категория отображает обложку на основе галереи (любезно предложенную Нилом Дэвисом здесь ) с изображениями выбранной категории.
Изображения извлекаются из Интернета, каждый размером от 300 К до 500 КБ и хранится в массиве списков Drawables. Эти данные привязаны к потоку с использованием BaseAdapter (код ниже).
Каждый раз, когда я выхожу из обложки и возвращаюсь к списку категорий, я очищаю arrayList (опять же, код ниже).
В сценарии 1 мой массивList содержит 5 Drawables. В этом случае я могу свободно просматривать все категории и показывать их изображения. Во время моего теста я циклически перебирал все категории в 5 раз, что кажется достаточным, чтобы определить, что проблем нет.
В сценарии 2 мой массивList содержит 10 рисунков. В этом случае я получаю исключение OutOfMemoryError при просмотре изображений в 5-м или 6-м категеоре:

 07-13 08: 38: 21.266: ERROR / dalvikvm-heap (2133): внешнее выделение 819840 байтов слишком велико для этого процесса.
 07-13 08: 38: 21.266: ERROR / (2133): VM не позволит нам выделить 819840 байт
 07-13 08: 38: 21.277: DEBUG / skia (2133): --- decoder-> decode возвращен false
 07-13 08: 38: 21.287: WARN / dalvikvm (2133): threadid = 25: выход из потока с неперехваченным исключением (группа = 0x4001b188)
 07-13 08: 38: 21.296: ERROR / AndroidRuntime (2133): обработчик невозврата: поток Thread-64, выходящий из-за неперехваченного исключения
 07-13 08: 38: 21.308: ERROR / AndroidRuntime (2133): java.lang.OutOfMemoryError: размер растрового изображения превышает бюджет VM
 07-13 08: 38: 21.308: ERROR / AndroidRuntime (2133): at android.graphics.BitmapFactory.nativeDecodeStream (собственный метод)
 07-13 08: 38: 21.308: ERROR / AndroidRuntime (2133): at android.graphics.BitmapFactory.decodeStream (BitmapFactory.java:459)
 07-13 08: 38: 21.308: ERROR / AndroidRuntime (2133): at android.graphics.BitmapFactory.decodeResourceStream (BitmapFactory.java:323)
 07-13 08: 38: 21.308: ERROR / AndroidRuntime (2133): at android.graphics.drawable.Drawable.createFromResourceStream (Drawable.java:697)
 07-13 08: 38: 21.308: ERROR / AndroidRuntime (2133): at android.graphics.drawable.Drawable.createFromStream (Drawable.java:657)

Для меня это не имеет смысла. Если я пропущу память, я бы ожидал, что в какой-то момент в сценарии 1 произойдет сбой, но я прошел через все категории значительное количество раз и не разбился. Я также использовал плагин Memory Analyzer для Eclipse, который не представлял потенциальных преступников.
Если система не смогла обработать 10 изображений, например, в сценарии 2, я бы ожидал сбой в первой категории, но я разбился только после 5 или 6 категорий.
Некоторый код:

Функции адаптера крышки:

public int getCount() { return DataManager.getInstance().getImageBufferInstance().getImageArraySize(); } public Object getItem(int position) { return DataManager.getInstance().getImagesBuffer().get(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ImageView i; if (convertView == null) i = new ImageView(mContext); else i = (ImageView)convertView; Drawable bufferedImage = (Drawable)getItem(position); Log.v("getView", "position: " + position); i.setImageDrawable(bufferedImage); i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth() / 2, Utils.getInstance().getScreenHeight() / 2)); i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); try{ //Make sure we set anti-aliasing otherwise we get jaggies BitmapDrawable drawable = (BitmapDrawable) i.getDrawable(); drawable.setAntiAlias(true); } catch (Exception e) { Log.v("getView", "Exception: " + e.toString()); } return i; } 

Заполнение источника данных при входе в категорию:

 for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++) { String imageUrl = ImageBuffer.getInstance().getImageUrl(i); Log.v("Initial", imageUrl); Drawable fullImage = AsyncImageLoader.getInstance().loadImageByUrl(imageUrl); ImageBuffer.getInstance().getImages().add(i, fullImage); } 

Очистка источника данных при выходе из категории (в финише ()):

 for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++) { if (ImageBuffer.getInstance().images.get(i) != null) { ImageBuffer.getInstance().images.get(i).setCallback(null); ImageBuffer.getInstance().images.set(i, null); } } 

РЕДАКТИРОВАТЬ:

Хорошо, я применил функцию LogHeap Mathias на моем потоке покрытия, и вот несколько выходов. Перед загрузкой первой галереи:

 DEBUG / Приложение (5221): debug.  =================================
 DEBUG / Application (5221): debug.heap native: выделено 6.20MB из 6.28MB (0.07MB бесплатно) в [com.example.Coverflow]
 DEBUG / Application (5221): debug.memory: выделено: 4.00MB 24.00MB (0.00MB бесплатно)
 DEBUG / dalvikvm (5221): GC освободил 4558 объектов / 638152 байт за 84 мс
 DEBUG / dalvikvm (5221): GC освободил 17 объектов / 808 байт в 67 мс

После входа в первую галерею:

 DEBUG / Приложение (5221): debug.  =================================
 DEBUG / Application (5221): debug.heap native: выделено 14.90MB из 16.89MB (0.07MB бесплатно) в [com.example.Coverflow]
 DEBUG / Приложение (5221): debug.memory: выделено: 4.00MB из 24.00MB (1.00MB бесплатно)
 DEBUG / dalvikvm (5221): GC освободил 357 объектов / 50080 байт в 68 мс
 DEBUG / dalvikvm (5221): GC освободил 353 объекта / 27312 байт в 67 мс

После создания первой галереи:

 DEBUG / Приложение (5221): debug.  =================================
 DEBUG / Application (5221): debug.heap native: выделено 14.83MB из 16.89MB (0.11MB бесплатно) в [com.example.Coverflow]
 DEBUG / Приложение (5221): debug.memory: выделено: 4.00MB из 24.00MB (1.00MB бесплатно)
 DEBUG / dalvikvm (5221): GC освободил 330 объектов / 17920 байт за 77 мс
 DEBUG / dalvikvm (5221): GC освободил 13 объектов / 760 байт в 67 мс

После входа в пятую галерею:

 DEBUG / Приложение (5221): debug.  =================================
 DEBUG / Application (5221): debug.heap native: выделено 16.80MB из 23.32MB (0.08MB бесплатно) в [com.example.Coverflow]
 DEBUG / Приложение (5221): debug.memory: выделено: 4.00MB из 24.00MB (1.00MB бесплатно)
 DEBUG / dalvikvm (5221): GC освободил 842 объекта / 99256 байт в 73 мс
 DEBUG / dalvikvm (5221): GC освободил 306 объектов / 24896 байт в 69 мс

После выхода из пятой галереи:

 DEBUG / Приложение (5221): debug.  =================================
 DEBUG / Application (5221): debug.heap native: выделено 16.74MB из 23.32MB (0.11MB бесплатно) в [com.example.Coverlow]
 DEBUG / Приложение (5221): debug.memory: выделено: 4.00MB из 24.00MB (1.00MB бесплатно)
 DEBUG / dalvikvm (5221): GC освободил 331 объект / 18184 байта в 68 мс
 DEBUG / dalvikvm (5221): GC освободил 60 объектов / 3128 байт в 68 мс

Кажется, что при входе в галерею выделяется все больше и больше памяти, но после выхода из нее очень мало. Неужели я не правильно очищаю свои чертежи? Для каждого элемента в моем массиве списка drawables я вызываю setCallBack (null) и устанавливаю для элемента значение null. Этого недостаточно?
Отчаяние для любого понимания.
благодаря

Solutions Collecting From Web of "Android: исключение из памяти в галерее"

Изображения извлекаются из Интернета, каждый размером от 300 К до 500 КБ и хранится в массиве списков Drawables.

Размер файла kb для изображения, загружаемого из Интернета, не имеет прямого отношения. Поскольку они преобразуются в растровые изображения, вам нужно рассчитать ширину * высоту * 4 байта на изображение для обычных изображений ARGB. (Ширина и высота в px).

Растровые изображения потребляют нативную кучу, которая обычно не отображается в hprof. Hprof должен показывать только количество объектов, то есть BitmapDrawables или Bitmaps, которые остались.

Я использую этот код в своем приложении для вывода текущей используемой памяти, используемой приложением и встроенной кучей:

 public static void logHeap(Class clazz) { Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576)); Double available = new Double(Debug.getNativeHeapSize())/1048576.0); Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0); DecimalFormat df = new DecimalFormat(); df.setMaximumFractionDigits(2); df.setMinimumFractionDigits(2); Log.d(APP, "debug. ================================="); Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]"); Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)"); System.gc(); System.gc(); // don't need to add the following lines, it's just an app specific handling in my app if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) { android.os.Process.killProcess(android.os.Process.myPid()); } } 

Которые я называю при запуске или завершении деятельности во время разработки.

 logHeap(this.getClass()); 

Вот некоторые информативные ссылки – как правило, здесь есть много потоков по этой теме.

  • Растровые изображения на Android
  • Android: Eclipse MAT не показывает все объекты моего приложения

Здесь также полезный слайд Ромен Гай (разработчик Android Framework) о мягких ссылках, слабых ссылках, простых кешах, обработке изображений: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

Вот несколько советов:

  1. Вы используете параметр inSampleSize? Это уменьшает потребление памяти при масштабировании изображений. Странная проблема с памятью при загрузке изображения в объект Bitmap

  2. Вы должны вызывать Bitmap.recycle (), когда вам больше не нужны изображения. Я думаю, это важно в вашем случае. Android: OutofMemoryError: размер растрового изображения превышает бюджет VM без причины, я могу видеть

Изображение, которое вы загружаете в галерею 5 или 6, может быть слишком большим для загрузки, и оно превышает максимальный размер, разрешенный виртуальной машиной.

Лучше вам хорошо знать, что список параметров convertView в параметрах getView всегда равен null . То есть, галерея не повторно использует старый вид внутри.