Как репозиторий узнает о push-уведомлениях в Android
Что проверяют
Умеете ли вы встроить внешний источник событий в слоистую архитектуру так, чтобы сервис уведомлений не тянул за собой логику и не знал про экраны. Следом обычно спрашивают, что будет, если процесс приложения убит или пуш вообще не дошёл.
Как отвечать
Точка входа одна: наследник FirebaseMessagingService, объявленный в манифесте с intent-filter на com.google.firebase.MESSAGING_EVENT. Регистрировать свой BroadcastReceiver для FCM не нужно.
- направление зависимостей: не репозиторий «узнаёт» о пуше, а сервис сообщает репозиторию. Сервис с
@AndroidEntryPointполучает репозиторий через Hilt и вызывает у него метод вродеonPushReceived(payload); - единый источник правды: репозиторий пишет пришедшее в Room, а UI подписан на
Flowиз DAO черезViewModelиStateFlow. Экран обновится сам, и неважно, открыт ли он сейчас; - пуш как сигнал: чаще в data-сообщении шлют только тип события и id, а свежие данные репозиторий забирает с сервера. Размер сообщения FCM до 4 КБ;
- долгая работа: у
onMessageReceivedкороткое окно, поэтому синхронизацию отдают вWorkManager(expedited work), а не держат сетевой запрос в самом сервисе; - токен:
onNewTokenтоже идёт через репозиторий, который отправляет токен на бэкенд; - типы сообщений: notification-сообщение в фоне показывает сама система, и
onMessageReceivedне вызывается. Если данные должны попасть в базу всегда, нужно data-сообщение.
Для показа уведомления на Android 13 и выше нужно runtime-разрешение POST_NOTIFICATIONS.
Где спотыкаются
Делают репозиторий зависимым от Firebase: он подписывается на сервис или держит на него ссылку. Правильно наоборот, репозиторий ничего не знает о транспорте, и в тестах пуш заменяется простым вызовом метода.
Строят логику на гарантии доставки. FCM доставку не обещает: сообщения схлопываются, откладываются в Doze, приходят с задержкой. Поэтому при старте приложения и при возврате сети нужна обычная синхронизация.
Обновляют UI прямо из сервиса через LiveData-синглтон или шину событий. Это работает только при открытом экране и ломается после смерти процесса, а запись в базу с подпиской из UI закрывает оба случая.