Использование кэширования для улучшения производительности прокрутки в Android ListView с изображениями
В настоящее время я использую кеш файловой системы для кэширования своих изображений при загрузке их с сервера. У меня есть ListView, который содержит пользовательские представления, каждый из которых получает свое изображение из файловой системы при вызове getView().
Для повышения производительности я реализовал java.util.WeakHashMap<String,Bitmap>
который хранит каждое из растровых изображений по уникальному ключу. Это позволяет мне помещать изображения в хэш-карту по мере их загрузки, а затем извлекать их непосредственно из памяти, чтобы заполнить мой список просмотра. Это позволяет избежать операции ввода-вывода файла и обеспечивает более плавную прокрутку.
Идея состоит в том, что, поскольку у ОС недостаточно памяти, она очищает WeakHashMap для освобождения памяти.
Тем не менее, это не работает на Android 2.3 или более ранней версии. Проблема в том, что растровые изображения не хранятся в куче Java, а вместо этого хранятся в собственной памяти. Это означает, что сборщик мусора JVM не имеет представления о том, сколько памяти занимают эти образы, и поэтому никогда не удосуживается освободить их, когда в операционной системе недостаточно встроенной памяти, что приводит к ошибкам OutOfMemory, когда остается много памяти, которую еще можно восстановить.
Это было исправлено в Android 3.0, так как 3.0 хранит растровые изображения в куче JVM вместо собственной памяти, но вопрос заключается в том, как можно легко кэшировать растровые изображения на Android 2.3 и более поздних версиях без непреднамеренного создания ненужных исключений OutOfMemory?
3 ответа
Таким образом, ответ, по-видимому, заключается в том, что в том, как виртуальная машина dalvik обнаруживает, когда ей нужно выполнить GC-передачу, есть ошибка. Если вы звоните вручную System.gc()
Непосредственно перед выделением памяти для вашего растрового изображения ошибки OutOfMemory неожиданно исчезают.
if(Build.VERSION.SDK_INT < 12) {
Log.d("Running garbage collection for Bitmaps");
System.gc();
}
return BitmapFactory.decodeStream(is);
Очевидно, что виртуальная машина должна автоматически выполнять этот сборщик мусора, прежде чем она выдаст OutOfMemory, но, похоже, этого не происходит.
Вы можете попробовать что-то вроде этого: http://code.google.com/p/xlarge-demos/source/browse/trunk/PhotoAlbum/src/com/example/android/photoalbum/LruCache.java и явно recycle()
ваши битмапы в выселенном шаге.
Таким образом, даже несмотря на то, что в более ранних версиях Android память растрового изображения выделяется в собственной куче, эта память по-прежнему расходуется на ваш процесс, ее просто труднее увидеть, поэтому вы можете получить исключение OOM. Тем не менее, ваш базовый анализ верен. Проблема в том, что нативный код на самом деле не имеет хорошей идеи, когда он может освободить память для растровых изображений, поэтому его рекомендуется всем разработчикам Bitmap.recycle()
, поскольку это, по сути, говорит нативному коду, что можно освободить память. Вероятно, когда элементы удаляются из WeakHashMap, это не вызывается.
Тем не менее, эмпирически я построил подобную систему, используя HashMap<String, SoftReference<Bitmap>>
и растровая память была правильно освобождена. Отмечу, однако, что я думаю, что это решение стало менее эффективным, начиная с Android 2.3, из-за изменений в сборщике мусора, хотя мне нужно вернуться и проверить это воспоминание.
В конце концов, я думаю, что ответ заключается в том, что я не знаю хорошего ответа на этот вопрос, который не использует явное управление, как LruCache
, Было бы здорово иметь решение, которое использует SoftReferences или WeakReferences, но при нынешнем способе сбора мусора я не уверен, что это сработает.