Использование кэширования для улучшения производительности прокрутки в 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, но при нынешнем способе сбора мусора я не уверен, что это сработает.

Другие вопросы по тегам