Как реализовать загрузку и отображение изображений с кешированием в Android?
Что проверяют
Знаете ли вы, как устроено кэширование картинок и почему его почти не пишут руками. Хороший ответ называет библиотеку, объясняет два уровня кэша и связь загрузки с жизненным циклом. Следом часто просят: «А если без библиотеки?»
Как отвечать
В новом проекте берут Coil или Glide. Coil написан на Kotlin, работает на корутинах и даёт для Compose AsyncImage(model = url, contentDescription = ...). Третья версия стала мультиплатформенной, сеть подключают отдельным артефактом, например coil-network-okhttp. Glide остаётся стандартом во View-коде: Glide.with(fragment).load(url).placeholder(...).error(...).into(imageView).
Что под капотом:
- кэш в памяти: LRU по декодированным
Bitmap, размер считается от доступной приложению памяти; - дисковый кэш: в
cacheDir, переживает перезапуск. У Glide по умолчаниюDiskCacheStrategy.AUTOMATIC, естьDATA(исходные байты),RESOURCE(уменьшенный результат),ALLиNONE; - даунсэмплинг: картинку декодируют под размер view, а не в исходном разрешении. Это главная защита от
OutOfMemoryError; - жизненный цикл: запрос привязан к фрагменту или активности и отменяется при их уничтожении, а в
RecyclerViewновая загрузка в тот жеImageViewотменяет предыдущую; - ключ кэша: по умолчанию URL. Для подписанных ссылок с меняющимся токеном ключ задают вручную.
Без библиотеки: LruCache<String, Bitmap> с переопределённым sizeOf, файлы в cacheDir, декодирование через BitmapFactory.Options с inJustDecodeBounds и inSampleSize на Dispatchers.IO, отмена Job при переиспользовании ячейки.
Где спотыкаются
Передают в Glide.with() контекст приложения. Загрузка перестаёт зависеть от экрана, продолжается после его закрытия и держит ссылку на уже ненужную view.
Пишут кэш руками без проверки, что ячейка всё ещё ждёт ту же картинку. При быстрой прокрутке RecyclerView медленный ответ приходит позже и показывает в строке чужое изображение.
Ставят DiskCacheStrategy.ALL «на всякий случай». На диске лежат и оригиналы, и уменьшенные копии, а для картинок, которые открывают один раз, это лишнее место.