Ответ
Регистрация BroadcastReceiver в манифесте не работает в таких случаях:
- Для неэкспортируемых broadcast'ов (начиная с Android 8.0) - система не доставит их
- Для динамических broadcast'ов (например, LOCAL_BROADCAST) - они работают только в контексте приложения
- Когда требуется runtime-проверка разрешений (RECEIVER_EXPORTED/RECEIVER_NOT_EXPORTED)
Пример правильной динамической регистрации:
val filter = IntentFilter("MY_ACTION")
registerReceiver(myReceiver, filter, RECEIVER_NOT_EXPORTED)
Важно: начиная с Android 14, все ресиверы должны явно указывать экспортируемость.
Ответ 18+ 🔞
А, ну это же классика, ёпта! Смотри, бывает такая ситуация — ты в манифесте прописал свой BroadcastReceiver, как учили в старых учебниках, а он нихуя не работает. И стоишь ты такой, чешешь репу, и думаешь: «Какого хуя? Всё же правильно!».
А причина, чувак, может быть в трёх вещах, и все они — пиздец как важны.
Первое — и самое жирное. Начиная с Android 8.0 (Oreo), система перестала доставлять неэкспортируемые броадкасты тем ресиверам, которые зарегистрированы только в манифесте. Представь: какое-то левое приложение внутри себя шлёт интент с действием "com.ebany.mysecret.event". Раньше твой ресивер в манифесте мог его словить, даже если приложение спало. Сейчас — хуй там! Если броадкаст не экспортный (то есть не для всех), система его просто проигнорит. Твоя регистрация в манифесте для таких случаев — всё равно что кричать в пустоту. Доверия ебать ноль у системы к такому подходу.
Второе. Есть броадкасты, которые вообще живут только в пределах твоего приложения. Ну, типа LocalBroadcastManager (хотя он уже deprecated, но суть та же). Это как внутренняя почта в офисе. Ты же не будешь заказное письмо через «Почту России» отправлять, чтобы передать бумажку из кабинета в кабинет? Вот и здесь так. Эти штуки работают только в контексте твоего живого процесса. Манифест тут вообще не при делах.
Третье. Начиная с Android 14, вообще все ресиверы, объявленные в манифесте, должны явно указывать — экспортируемые они или нет. Не указал флажок android:exported? Получай ошибку при установке или просто молча игнорируйся системой. Терпения ноль ебать у новых андроидов к такой неопределённости.
Так что делать-то, спросишь? А делать вот так, динамически, прямо в коде активности или сервиса:
val filter = IntentFilter("MY_ACTION")
registerReceiver(myReceiver, filter, RECEIVER_NOT_EXPORTED)
Видишь этот третий параметр, RECEIVER_NOT_EXPORTED? Это ты системе чётко заявляешь: «Чувак, этот ресивер только для внутреннего пользования, со стороны не принимай». Или наоборот — RECEIVER_EXPORTED, если хочешь слушать внешние события (но тогда готовься к проверке разрешений, это отдельная песня).
Короче, мораль простая: забудь как страшный сон про регистрацию в манифесте для всего, что не является системным широковещательным событием (типа BOOT_COMPLETED). Всё, что твоё, внутреннее, или требует контроля — регистрируй динамически, пока приложение живо. И явно указывай, можно ли в него стучаться с улицы. Иначе будет тебе не BroadcastReceiver, а Broadcast-Non-Receiver, хитрая жопа!