Розробка компаньон-додатка для умних годинників
Компаньон-додаток — це телефонна частина зв'язки «телефон + годинник». Його задача: приймати дані з годинника, віддавати конфігурацію, синхронізувати стан. Звучить просто. На практиці — це окремий шар архітектури з асинхронними каналами, конфліктами версій та поведінком, яке змінюється залежно від того, підключені ли годинник в момент запиту.
Де виникають реальні проблеми
Несогласованість стану. Годинник відправив дані через DataClient, телефон був у фоні та обробив повідомлення в WearableListenerService.onDataChanged(). Користувач відкриває додаток — UI показує старе стан, тому що ViewModel не знає, що Room вже оновився. Класичне race condition, яке відтворюється лише при визначеному тайменгу фонової обробки.
Рішення: WearableListenerService пише в Room через Repository, ViewModel підписана на Flow із DAO. Ніяких LiveData через EventBus — лише реактивна цепочка.
Версионування протоколу. Телефонне додаток оновилось, часове — ще ні (користувач не заходив у Google Play на годинник). Якщо структура DataMap змінилась — часове додаток краснется при десеріалізації. Обов'язково версионируємо протокол: додаємо поле protocol_version в кожний PutDataMapRequest. Телефонна частина обробляє застарілі версії gracefully.
Фонова робота на Android 14+. WearableListenerService все ще запускається системою при отриманні даних — це винятак з обмежень фонових сервісів. Але якщо компаньон-додаток робить HTTP-запит в відповідь на дані з годинника, потрібен WorkManager з setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST). Прямий вызов Retrofit з сервіса заблокується на Android 14 з ForegroundServiceStartNotAllowedException.
Архітектура компаньон-додатка
WearableListenerService
↓ (coroutine, Dispatchers.IO)
Repository
↓
Room DAO (Flow)
↓
ViewModel (StateFlow)
↓
Compose UI
Для конфігурації (налаштування, які юзер змінює на телефоні та вони мають прийти на годинник) — DataClient.putDataItem() з шляхом /config/v2. Шлях версионируємо явно.
Для команд реального часу (пауза тренування, переключення трека) — MessageClient.sendMessage(). Він швидше DataClient, але не гарантує доставку при відключених годинниках.
Для великих файлів (оновлення бази маршрутів, синхронізація медіатеки) — ChannelClient. Відкриваємо канал, передаємо через OutputStream, закриваємо. Це єдиний спосіб передати більше кількох килобайт без ризику попасти в обмеження Data Layer (100 КБ на один DataItem).
Перевірка доступності годинника. Перед відправленням даних перевіряємо CapabilityClient.getCapability(CAPABILITY_NAME, CapabilityClient.FILTER_REACHABLE). Якщо годинник недоступен — ставимо дані в чергу (Room + WorkManager), відправляємо при наступному підключенні через CapabilityClient.addListener().
Підтримка кількох платформ
Якщо годинник — Apple Watch, компаньйон будується на WatchConnectivity framework (iOS). WCSession.default.sendMessage() для реального часу, transferUserInfo() для фонової синхронізації. Логіка та сама, API інший.
Flutter-додатки інтегруються з Wear OS через wear пакет або прямі platform channels до нативного WearableListenerService. React Native — аналогічно через native module.
Сроки
Компаньон-додаток для Android + Wear OS з двусторонней синхронізацією: 3–6 тижнів залежно від обсягу даних та складності UI. Мультиплатформенний варіант (iOS + WatchOS): окрема оцінка після аналізу ТЗ.







