Какие ограничения есть у Broadcast Receiver в Android
Что проверяют
Знаете ли вы, как менялись правила для ресиверов от версии к версии Android, и умеете ли выбрать правильный механизм для фоновой работы. Следом обычно спрашивают, чем заменить ресивер в манифесте после Android 8 и где делать долгую работу, начатую в onReceive().
Как отвечать
Разложите ограничения по группам:
- поток и время:
onReceive()вызывается в главном потоке, на обработку есть считанные секунды, дальше ANR. После возврата из метода процесс может быть убит, поэтому запускать там поток бессмысленно. Для короткой асинхронной работы естьgoAsync()с обязательнымfinish(), для длиннойWorkManager; - неявные рассылки: с Android 8.0 (API 26) ресиверы из манифеста у приложений с таким targetSdk не получают большинство неявных рассылок, исключения вроде
BOOT_COMPLETEDесть в документации. Такие события слушают ресивером из кода или заменяют наWorkManagerсConstraintsиConnectivityManager.NetworkCallback; - экспорт: с Android 12 компоненту с
intent-filterнужно явно указатьandroid:exported, а с Android 14 при регистрации в коде обязателен флагRECEIVER_EXPORTEDилиRECEIVER_NOT_EXPORTED, удобнее черезContextCompat.registerReceiver(); - жизненный цикл: ресивер из кода живёт, пока жив его контекст, и снимается через
unregisterReceiver()в парном колбэке, иначе утечка. С Android 14 рассылки кешированному приложению копятся и приходят с задержкой; - безопасность: экспортированный ресивер может вызвать любое приложение. Защищают разрешением в
android:permissionили адресной отправкой черезsetPackage().
Где спотыкаются
Советуют запустить из onReceive() обычный Service для долгой задачи. С Android 8 запуск сервиса из фона падает с IllegalStateException, а foreground service из фона с Android 12 разрешён только в оговорённых исключениях.
Переворачивают ограничение: думают, что часть ресиверов работает только из манифеста. На деле с Android 8 наоборот, большинство неявных событий доступно лишь ресиверам из кода.
Предлагают LocalBroadcastManager для событий внутри приложения. Он объявлен устаревшим, вместо него используют Flow, LiveData или общий репозиторий.