← Назад к вопросам

По какому признаку будешь обновлять кэш

2.2 Middle🔥 81 комментариев
#Архитектура и паттерны#Работа с данными

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Стратегии обновления кэша в Android-приложениях

Выбор признака для обновления кэша зависит от типа данных, бизнес-логики и требований к актуальности информации. Я рассматриваю несколько ключевых подходов, которые комбинирую в зависимости от контекста.

Основные стратегии обновления

1. По времени (TTL - Time To Live) Самая распространенная стратегия: кэш считается устаревшим после определенного интервала. Идеально для данных с предсказуемой частотой изменений.

class TimeBasedCache<T>(
    private val ttlMillis: Long,
    private val cache: Cache<T>
) {
    private var lastUpdated: Long = 0
    
    fun getData(): T? {
        return if (System.currentTimeMillis() - lastUpdated > ttlMillis) {
            null // Данные устарели
        } else {
            cache.get()
        }
    }
    
    fun update(data: T) {
        cache.save(data)
        lastUpdated = System.currentTimeMillis()
    }
}

2. По событию (Event-based) Обновление инициируется внешними событиями:

  • Push-уведомление от сервера
  • Действие пользователя (pull-to-refresh)
  • Изменение системного состояния (сеть появилась)
  • BroadcastReceiver для специфичных интентов

3. По версии данных Сервер возвращает версию или хэш данных (ETag в HTTP). Клиент отправляет свою версию, сервер отвечает либо новыми данными, либо статусом 304 Not Modified.

interface VersionedCache {
    suspend fun fetchDataIfNeeded(
        currentVersion: String
    ): Result<DataWithVersion>
    
    data class DataWithVersion(
        val data: Any,
        val version: String,
        val eTag: String?
    )
}

4. По приоритету данных Категоризирую данные по критичности:

  • Высокий приоритет: профиль пользователя, баланс - обновляю при каждом запуске
  • Средний приоритет: каталог товаров - обновляю раз в день
  • Низкий приоритет: статичный контент - обновляю только при изменении версии приложения

Комбинированные стратегии на практике

В реальных проектах я редко использую одну стратегию. Вот типичная реализация:

class SmartCacheManager(
    private val networkDataSource: DataSource,
    private val localCache: Cache,
    private val connectivityManager: ConnectivityManager
) {
    suspend fun getDataWithSmartRefresh(
        cacheKey: String,
        forceRefresh: Boolean = false
    ): Result<Data> {
        // 1. Проверяем принудительное обновление
        if (forceRefresh) {
            return fetchFromNetwork(cacheKey)
        }
        
        // 2. Проверяем TTL
        val cached = localCache.get(cacheKey)
        if (cached != null && !cached.isExpired()) {
            return Result.success(cached.data)
        }
        
        // 3. Проверяем наличие сети для фонового обновления
        return if (connectivityManager.isConnected()) {
            fetchFromNetwork(cacheKey)
        } else {
            // Возвращаем устаревшие данные, если нет сети
            cached?.data?.let { Result.success(it) } 
                ?: Result.failure(NoDataException())
        }
    }
    
    private suspend fun fetchFromNetwork(key: String): Result<Data> {
        // Реализация с обработкой ETag и версионированием
    }
}

Критерии выбора стратегии

При определении признака обновления я учитываю:

  1. Частоту изменений данных на сервере

    • Высокая частота → Event-based или короткий TTL
    • Низкая частота → Длинный TTL или version-based
  2. Критичность актуальности

    • Финансовые данные → Event-based + короткий TTL
    • Контент для чтения → TTL 24 часа + manual refresh
  3. Ограничения сети и батареи

    • Экономлю трафик → Version-based (ETag)
    • Экономлю батарею → Увеличиваю TTL, минимизирую фоновые обновления
  4. Поведение при отсутствии сети

    • Всегда показываю последние доступные данные с индикацией устаревания
    • Использую механизм синхронизации при появлении сети

Продвинутые техники

Stale-while-revalidate: немедленно возвращаю устаревшие данные, параллельно обновляя кэш в фоне.

Predictive prefetching: предзагружаю данные на основе пользовательских паттернов (например, перед типичным временем использования).

Умная инвалидация: связываю зависимые данные - обновление одного элемента инвалидирует связанные кэши.

В конечном счете, лучший признак для обновления - тот, который обеспечивает баланс между актуальностью данных, производительностью приложения и эффективным использованием ресурсов устройства. Я всегда тестирую разные стратегии с помощью A/B-тестирования и аналитики, чтобы найти оптимальное решение для конкретного случая использования.