Урок в Android Studio на Kotlin: слушаем события с BroadcastReceiver

Приложения не живут в вакууме — вокруг постоянно что-то происходит. Телефон разряжается, подключаются наушники, меняется сеть с Wi-Fi на мобильный интернет. Чтобы программа могла на это реагировать, в Android придумали механизм широковещательных сообщений. В этом уроке мы разберём, как с помощью Kotlin создать приёмник таких событий прямо в Android Studio и сделать приложение умнее, без постоянных проверок в фоне.


Что такое BroadcastReceiver

BroadcastReceiver — это компонент, который слушает системные оповещения и выполняет ваш код, когда происходит что-то важное. Представьте почтальона, который приносит письма. Система отправляет «письма» с новостями: батарея села до 15%, сеть пропала, наушники воткнули. BroadcastReceiver — это почтовый ящик, куда эти письма приходят. Вам остаётся только открыть его и прочитать.

Приёмник может быть зарегистрирован двумя способами: статически (в манифесте) или динамически (в коде). Статический работает даже когда приложение не запущено, но в современных версиях Android для большинства событий этот способ ограничен. Динамический регистрируется, когда приложение активно, и отключается вместе с ним — он безопаснее и проще для начала.


Создаём приёмник заряда батареи

Начнём с практического примера: будем показывать процент зарядки прямо в приложении. Создайте класс, наследующий BroadcastReceiver, и переопределите метод onReceive. Внутри этого метода из Intent можно извлечь текущий уровень батареи.

class BatteryReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
        val scale = intent?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
        val percent = (level * 100 / scale)
        // здесь можно обновить UI или показать уведомление
    }
}

Два значения — EXTRA_LEVEL (текущий уровень) и EXTRA_SCALE (максимальный уровень) — нужны, чтобы вычислить процент. У разных устройств scale может отличаться, поэтому просто взять level недостаточно.


Регистрируем и отменяем приёмник в Activity

Динамическая регистрация делается через метод registerReceiver. Лучшее место для этого — onResume. А в onPause нужно обязательно отменить регистрацию, иначе приложение будет получать события даже когда неактивно, расходуя батарею.

class MainActivity : AppCompatActivity() {
    private val batteryReceiver = BatteryReceiver()

    override fun onResume() {
        super.onResume()
        val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
        registerReceiver(batteryReceiver, filter)
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(batteryReceiver)
    }
}

IntentFilter указывает, какие именно события нас интересуют. Действие ACTION_BATTERY_CHANGED посылается системой при любом изменении состояния батареи — зарядке, разрядке, подключении к питанию.


Показываем результат на экране

Сам по себе BroadcastReceiver не имеет доступа к UI, потому что это отдельный компонент. Чтобы обновить текст на экране, можно передать лямбду в конструктор приёмника и вызвать её при получении данных.

class BatteryReceiver(
    private val onUpdate: (Int) -> Unit
) : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
        val scale = intent?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
        val percent = (level * 100 / scale)
        onUpdate(percent)
    }
}

// в Activity
private val batteryReceiver = BatteryReceiver { percent ->
    batteryText.text = "Заряд: $percent%"
}

Слушаем подключение наушников

По такому же принципу можно отследить, когда пользователь воткнул или вытащил наушники. Действие называется ACTION_HEADSET_PLUG. Intent будет содержать состояние — 1 (подключены) или 0 (отключены).

class HeadsetReceiver(
    private val onStateChanged: (Boolean) -> Unit
) : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val state = intent?.getIntExtra("state", 0) ?: 0
        onStateChanged(state == 1)
    }
}

// в Activity
private val headsetReceiver = HeadsetReceiver { connected ->
    val message = if (connected) "Наушники подключены" else "Наушники отключены"
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

override fun onResume() {
    super.onResume()
    registerReceiver(batteryReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
    registerReceiver(headsetReceiver, IntentFilter(Intent.ACTION_HEADSET_PLUG))
}

Несколько замечаний для начинающих

  • Всегда отменяйте регистрацию. Если забыть unregisterReceiver, система выдаст ошибку, а батарея будет садиться быстрее.
  • Не делайте долгих операций в onReceive. Этот метод выполняется в главном потоке и должен отработать быстро. Для длительных задач запускайте Service или WorkManager.
  • Проверяйте наличие действия. Один приёмник можно подписать на несколько событий, но внутри onReceive нужно различать их через intent?.action.

Что мы сегодня освоили

Сегодняшний урок в Android Studio на языке Kotlin показал, как приложение может откликаться на системные события с помощью BroadcastReceiver. Вы научились создавать приёмник, регистрировать его динамически и получать данные о батарее и наушниках. Эти знания помогут делать приложения более отзывчивыми и дружелюбными. Попробуйте добавить отслеживание состояния сети — это будет отличным продолжением сегодняшней темы.