Приведи пример использования BroadcastReceiver

Ответ

BroadcastReceiver позволяет реагировать на системные или кастомные события. Пример: регистрация ресивера для события ACTION_AIRPLANE_MODE_CHANGED.

1. Объявление в манифесте:

<receiver android:name=".AirplaneModeReceiver">
    <intent-filter>
        <action android:name="android.intent.action.AIRPLANE_MODE"/>
    </intent-filter>
</receiver>

2. Класс ресивера:

class AirplaneModeReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val isEnabled = Settings.Global.getInt(
            context.contentResolver,
            Settings.Global.AIRPLANE_MODE_ON
        ) != 0
        Toast.makeText(context, "Airplane mode: $isEnabled", Toast.LENGTH_SHORT).show()
    }
}

Важно: Для Android 8+ динамическая регистрация предпочтительнее:

val filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
registerReceiver(receiver, filter)

Не забудьте вызвать unregisterReceiver() в onDestroy().

Ответ 18+ 🔞

Э, слушай, смотри, вот тебе BroadcastReceiver — это такая штука, которая сидит и ждёт, пока система не начнёт орать на всё село о каком-нибудь событии. Типа самолётный режим включили или батарея сдохла. Ну, ёпта, как сторожевой пёс, только для интентов.

Вот, допустим, тебе нужно поймать момент, когда юзер включает этот ёперный театр с авиарежимом. Раньше, в старые добрые времена, ты просто прописывал это в манифесте, и всё, блядь, работало. Прямо как волшебство.

1. Объявление в манифесте (старый способ, но для некоторых случаев жив):

<receiver android:name=".AirplaneModeReceiver">
    <intent-filter>
        <action android:name="android.intent.action.AIRPLANE_MODE"/>
    </intent-filter>
</receiver>

Видишь? Прямо как объявление: «Слушай, система, у меня тут есть чувак по имени AirplaneModeReceiver, который хочет знать про авиарежим. Шли ему весточку, как что».

2. А сам этот чувак, ресивер, выглядит примерно так:

class AirplaneModeReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val isEnabled = Settings.Global.getInt(
            context.contentResolver,
            Settings.Global.AIRPLANE_MODE_ON
        ) != 0
        Toast.makeText(context, "Airplane mode: $isEnabled", Toast.LENGTH_SHORT).show()
    }
}

Вот он, красавец. Как только событие прилетело, вызывается onReceive. Тут мы лезем в настройки глобальные, смотрим, ноль там или единица, и показываем тост. Проще пареной репы, да?

НО! Вот тут-то и начинается, блядь, самое интересное. Начиная с Android 8 (Oreo), гугл такие: «А давайте-ка усложним им жизнь!». Статическая регистрация в манифесте для большинства системных событий накрылась медным тазом. Теперь, если твоё приложение не на экране, оно нихрена не услышит.

Поэтому теперь модно, круто и правильно регистрировать ресивер динамически, прямо в коде активити или сервиса:

val filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
registerReceiver(receiver, filter)

Сделал это, например, в onCreate() — и ты в теме. Ресивер живёт, пока живёт твой контекст.

И главное, блядь, не забудь! Это как взять в долг — нужно отдать. Если зарегистрировал в onCreate(), то обязательно сними его в onDestroy():

override fun onDestroy() {
    super.onDestroy()
    unregisterReceiver(receiver)
}

А то будет тебе утечка контекста, и приложение начнёт сосать память как не в себя, а потом просто вылетит с ошибкой. Сам от такого охуевал не раз. В общем, правило простое: что включил — то и выключи. И будет тебе счастье, а не пиздец.