Всегда ли BroadcastReceiver.onReceive выполняется в потоке пользовательского интерфейса?

117

В своем приложении я создаю заказ BroadcastReceiverи регистрирую его в своем контексте вручную через Context.registerReceiver. У меня также есть программа, AsyncTaskкоторая отправляет уведомления-намерения через Context.sendBroadcast. Намерения отправляются из рабочего потока, не относящегося к пользовательскому интерфейсу, но кажется, что BroadcastReceiver.onReceive(который получает указанные намерения) всегда выполняется в потоке пользовательского интерфейса (что хорошо для меня). Это гарантировано или мне не следует полагаться на это?

Ханнес Штрусс
источник

Ответы:

163

Всегда ли BroadcastReceiver.onReceive выполняется в потоке пользовательского интерфейса?

Да.

CommonsWare
источник
9
это где-то задокументировано?
Ханнес Штрусс,
15
@hannes: 99,44% времени, если Android вызывает ваш код, он находится в основном потоке приложения. Все методы жизненного цикла (например, onCreate(), onReceive()) называется на главном потоке приложения. И это задокументировано в документации для onReceive(): goo.gl/8kPuH
CommonsWare
2
хорошо, я просто интерпретирую «обычно вызывается в основном потоке» из документации как «всегда» и надеюсь, что ничего не сломается ;-) Спасибо!
Ханнес Штрусс
4
@Hannes Struß: Я не знаю, почему они хеджировали свой язык словом «обычно». Я не могу представить себе ни одного случая, когда onReceive()вызывается поток, отличный от потока основного приложения («UI»).
CommonsWare
31
@CommonsWare: «Я не могу придумать ни одного случая, когда onReceive () вызывается в потоке, отличном от потока основного приложения (« UI »)» - это случай, если BroadcastReceiver зарегистрирован с помощью registerReceiver (BroadcastReceiver, IntentFilter, String, Handler), аргумент обработчика не имеет значения NULL и относится к обработчику, созданному в потоке, отличном от основного потока приложения.
Жюль
76

Поскольку вы динамически регистрируете получатель, вы можете указать, что другой поток (кроме потока пользовательского интерфейса) обрабатывает onReceive(). Это делается с помощью параметра Handler функции registerReceiver () .

Тем не менее, если вы не указали другой обработчик, он всегда будет обрабатываться в потоке пользовательского интерфейса.

TommyTh
источник
Да. Похоже, ваша способность изменить его с помощью параметра Handler является причиной того, что они «хеджировали» свой язык в документации.
Эндрю Маккензи,
64

Всегда ли BroadcastReceiver.onReceive выполняется в потоке пользовательского интерфейса?

Обычно все зависит от того, как вы его регистрируете.

Если вы зарегистрируете свое BroadcastReceiverиспользование:

registerReceiver(BroadcastReceiver receiver, IntentFilter filter)

Он будет работать в основном потоке активности (он же поток пользовательского интерфейса) .

Если вы зарегистрируете свой BroadcastReceiverдействительный Handler запуск в другом потоке :

registerReceiver (BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)

Он будет работать в контексте вашего Handler

Например:

HandlerThread handlerThread = new HandlerThread("ht");
handlerThread.start();
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);
context.registerReceiver(receiver, filter, null, handler); // Will not run on main thread

Подробности здесь и здесь .

Джанер
источник
3
Посмотрев на этот вариант некоторое время, я в конце концов понял, что LocalBroadcastManager не поддерживает использование настраиваемого обработчика. Поэтому, если вы используете LBM вместо контекста для регистрации своего приемника, этот подход не применяется. К сожалению, в этом случае кажется, что у нас остается единственный выход - использовать Службу для работы в фоновом режиме и избегать ошибок ANR, которые получатели запускают после 10 секунд бездействия.
gMale
9

Как правильно указано в предыдущих ответах, он onReceiveбудет работать в потоке, в котором он зарегистрирован, если вкус registerReceiver() вызывается , принимающий обработчик, в противном случае в основном потоке.

За исключением случаев, когда получатель зарегистрирован в LocalBroadcastManagerи трансляция осуществляется через sendBroadcastSync- где он, по- видимому, будет работать в потоке, который вызываетsendBroadcastSync.

Mr_and_Mrs_D
источник
Я не согласен с этой частью and the broadcast is via sendBroadcastSync. Когда мы используем LocalBroadcastManagerдля регистрации приемника, он должен вызываться основным потоком независимо от того, используется ли sendBroadcastSyncили sendBroadcast. Итак, ключ в том, чтобы использовать LocalBroadcastManagerдля регистрации. Я прав?
kidoher
@kidoher: Вы переходили по ссылкам на код здесь: stackoverflow.com/q/20820244/281545 ?
Mr_and_Mrs_D
0

ДА Context.registerReceiver (приемник BroadcastReceiver, фильтр IntentFilter, String broadcastPermission, планировщик обработчика)

Акаша
источник