IO — Ivan Labs

Создание offline-first Android-приложения

3 мин чтения
Android

«Offline-first» используется свободно, означая «работает без интернета», но реальная архитектура более конкретна.

Основная идея: локальные данные — источник истины

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

Это значимо другая форма, чем «кэшировать последний ответ API и показывать его, если офлайн» — это поддерживает чтение, запись и даже создание новых данных в офлайне, с синхронизацией, примиряющей всё, как только соединение возвращается.

Типичный паттерн

  1. UI читает из Room (или другой локальной базы данных) через Flow для реактивных обновлений.
  2. Слой репозитория решает, когда получать данные из сети, и записывает результаты в локальную базу данных — UI никогда не общается с сетью напрямую.
  3. Механизм синхронизации (WorkManager — стандартный современный выбор) работает в фоне, отправляя локальные изменения вверх и получая удалённые изменения вниз, независимо от того, открыто ли приложение у пользователя прямо сейчас.
class NotesRepository(private val dao: NoteDao, private val api: NotesApi) {
    fun getNotes(): Flow<List<Note>> = dao.getAll()
 
    suspend fun refresh() {
        val remoteNotes = api.fetchNotes()
        dao.insertAll(remoteNotes)
    }
}

UI непрерывно наблюдает за getNotes() — он обновляется автоматически, когда бы refresh() ни записывал новые данные, без необходимости для слоя UI знать, что вообще произошёл сетевой вызов.

Сложная часть: разрешение конфликтов

Если одна и та же запись может редактироваться и офлайн (на устройстве), и в другом месте (другое устройство, веб-версия, сервер напрямую), вам нужна явная стратегия того, что происходит, когда обе версии пытаются примириться:

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

Здесь нет универсально правильного выбора — это зависит от того, насколько потерянная правка реально важна для вашего конкретного приложения.

Почему встраивание этого позже дорого

Приложение, построенное в предположении, что UI читает напрямую из сетевых ответов, имеет это предположение встроенным в большую часть кода — экраны, view model, обработку ошибок, всё построено вокруг «сетевой вызов либо успешен, либо показывает ошибку». Преобразование этого в «UI всегда читает локальные данные, которые могут быть, а могут и не быть свежесинхронизированы» затрагивает большинство тех же мест. Это именно то решение, которое стоит принять явно в начале — см. наш гайд по архитектуре Android насчёт того, как это вписывается в более широкую картину ранних решений.

Планируете приложение, которому нужно надёжно работать без соединения, или пытаетесь встроить офлайн-поддержку в существующее? Свяжитесь со мной — эти две ситуации требуют по-настоящему разных подходов.

Частые вопросы

Offline-first — то же самое, что кэширование ответов API?

Связано, но уже — кэширование просто хранит прошлые ответы для повторного использования. Offline-first означает, что приложение относится к локальным данным как к источнику истины, из которого читает UI, с сетевой синхронизацией как фоновой заботой, что также поддерживает запись/создание данных в офлайне.

Можно ли добавить offline-first к существующему приложению позже?

Это возможно, но значимо сложнее, чем проектировать это с самого начала — встраивание модели «локальная база данных как источник истины» в приложение, построенное в предположении, что UI читает напрямую из сетевых вызовов, — существенное архитектурное изменение, а не постепенное.

Что происходит, если одни и те же данные редактируются и офлайн, и на сервере?

Это сложная часть — разрешение конфликтов нуждается в явной стратегии (последняя запись побеждает, логика слияния, или показ конфликта пользователю), и стоит проектировать это намеренно, а не оставлять как второстепенное, поскольку именно здесь offline-first приложения чаще всего ведут себя неожиданно.

Нужна помощь с этим?

Свяжитесь со мной, и я помогу разобраться.

Связаться со мной

Похожие статьи

Поделиться:X / TwitterLinkedIn