← Назад к вопросам

На каком потоке работает BroadcastReceiver

1.0 Junior🔥 241 комментариев
#Работа с данными#Сетевое взаимодействие

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Работа BroadcastReceiver и управление потоками

BroadcastReceiver по умолчанию работает на главном потоке (Main Thread/UI Thread). Это ключевой аспект, который должен учитывать каждый Android-разработчик, поскольку длительные операции в onReceive() приведут к Application Not Responding (ANR) ошибке. Система дает на выполнение onReceive() примерно 10 секунд, после чего считает поток заблокированным и завершает приложение.

Механизм доставки Broadcast

Когда система или приложение отправляет broadcast, система добавляет его в очередь. Затем Activity Manager Service доставляет его всем зарегистрированным приемникам, вызывая их метод onReceive() в главном потоке приложения-получателя.

// Пример BroadcastReceiver
class MyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // ВНИМАНИЕ: Этот код выполняется в главном потоке!
        // Долгие операции здесь вызовут ANR
        
        when (intent.action) {
            Intent.ACTION_AIRPLANE_MODE_CHANGED -> {
                // Обработка изменения режима полета
                if (isInitialStickyBroadcast) {
                    // Особый тип широковещательных сообщений
                }
            }
        }
    }
}

Регистрация и потоки

1. Динамическая регистрация в коде

// Регистрация без указания Handler - работает в главном потоке
val receiver = MyReceiver()
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)

// Этот вызов выполнит onReceive() в главном потоке
context.registerReceiver(receiver, filter)

// Важно: не забывать отменять регистрацию!
// context.unregisterReceiver(receiver)

2. Статическая регистрация в манифесте

<!-- AndroidManifest.xml -->
<receiver 
    android:name=".MyReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

При статической регистрации система создает новый процесс для вашего приложения (если оно не запущено) и вызывает onReceive() в его главном потоке.

Управление потоком выполнения

Для выполнения длительных операций необходимо явно управлять потоками:

Вариант 1: Запуск Service (устаревший, но поддерживаемый)

override fun onReceive(context: Context, intent: Intent) {
    // Запускаем сервис для долгой операции
    val serviceIntent = Intent(context, MyIntentService::class.java)
    serviceIntent.putExtra("data", intent.extras)
    context.startService(serviceIntent)
}

Вариант 2: Использование goAsync() (API 11+)

override fun onReceive(context: Context, intent: Intent) {
    val pendingResult = goAsync() // Получаем PendingResult
    
    // Запускаем фоновую задачу
    AsyncTask.execute {
        try {
            // Долгая операция в фоне
            Thread.sleep(5000) // Пример блокирующей операции
            
            // Обновляем результат
            pendingResult.setResultCode(Activity.RESULT_OK)
            pendingResult.setResultData(null)
        } finally {
            // ОБЯЗАТЕЛЬНО завершаем работу
            pendingResult.finish()
        }
    }
}

Вариант 3: Coroutines в современных приложениях

class ModernReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // Используем LifecycleScope, если доступен
        val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
        
        scope.launch {
            // Долгие операции
            val result = withContext(Dispatchers.IO) {
                performNetworkRequest()
            }
            
            // Возвращаемся в главный поток для UI операций
            withContext(Dispatchers.Main) {
                updateUI(result)
            }
        }
    }
    
    private suspend fun performNetworkRequest(): String {
        delay(3000) // Имитация сетевого запроса
        return "Данные получены"
    }
}

Регистрация с указанием Handler (API 26+)

В Android 8.0 (API 26) появилась возможность явно указать Handler для выполнения:

// Создаем отдельный Looper для фоновых задач
val handlerThread = HandlerThread("BroadcastThread")
handlerThread.start()

val handler = Handler(handlerThread.looper)

// Регистрируем с указанием Handler
context.registerReceiver(
    receiver,
    filter,
    null, // broadcastPermission
    handler // Выполнение в указанном Handler
)

Ключевые ограничения и лучшие практики

  1. Время выполнения: Не более 10 секунд в onReceive()
  2. Запрещенные операции:
    • Сетевые запросы (без явного выноса в фон)
    • Работа с базами данных (кроме быстрых операций)
    • Любые блокирующие вызовы
  3. Рекомендации:
    • Используйте goAsync() для API ≥ 11
    • Для долгих операций запускайте Service или WorkManager
    • Всегда проверяйте isInitialStickyBroadcast при необходимости
    • Не забывайте про отмену регистрации динамических приемников
    • Для Android 8.0+ используйте ContextCompat.registerReceiver()

Особенности Android 8.0+

С Android Oreo введены ограничения на статические приемники для несистемных событий. Большинство broadcast теперь требуют динамической регистрации:

// Правильная регистрация для Android 8.0+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    ContextCompat.registerReceiver(
        context,
        receiver,
        filter,
        ContextCompat.RECEIVER_EXPORTED
    )
} else {
    context.registerReceiver(receiver, filter)
}

Важный вывод: Разработчик должен всегда помнить, что BroadcastReceiver по умолчанию работает в главном потоке и предпринимать соответствующие меры для выноса долгих операций в фоновые потоки, используя современные механизмы типа goAsync(), корутин или WorkManager для отложенных задач.

На каком потоке работает BroadcastReceiver | PrepBro