Czy BroadcastReceiver.onReceive zawsze działa w wątku interfejsu użytkownika?

117

W mojej aplikacji tworzę niestandardowy BroadcastReceiveri ręcznie rejestruję go w moim kontekście za pośrednictwem Context.registerReceiver. Mam też wiadomość, AsyncTaskktóra wysyła notifier-Intents przez Context.sendBroadcast. Intencje są wysyłane z wątku roboczego innego niż UI, ale wydaje się, że BroadcastReceiver.onReceive(który odbiera wspomniane intencje) zawsze działa w wątku interfejsu użytkownika (co jest dla mnie dobre). Czy to jest gwarantowane, czy nie powinienem na tym polegać?

Hannes Struß
źródło

Odpowiedzi:

163

Czy BroadcastReceiver.onReceive zawsze działa w wątku interfejsu użytkownika?

Tak.

CommonsWare
źródło
9
czy to jest gdzieś udokumentowane?
Hannes Struß
15
@hannes: 99,44% czasu, jeśli Android wywołuje Twój kod, znajduje się on w głównym wątku aplikacji. Wszystkie metody cyklu życia (np onCreate(), onReceive()) są nazywane w głównym wątku aplikacji. Jest to udokumentowane w dokumentacji onReceive(): goo.gl/8kPuH
CommonsWare
2
ok, po prostu interpretuję „jest zwykle wywoływane w głównym wątku” z dokumentacji jako „zawsze” i mam nadzieję, że nic się nie zepsuje ;-) Dzięki!
Hannes Struß
4
@Hannes Struß: Nie wiem, dlaczego zabezpieczyli swój język słowem „normalnie”. Nie przychodzi mi do głowy żaden przypadek, w którym onReceive()jest wywoływany w wątku innym niż główny wątek aplikacji („UI”).
CommonsWare
31
@CommonsWare: "Nie przychodzi mi do głowy żaden przypadek, w którym onReceive () jest wywoływany w wątku innym niż główny wątek aplikacji (" UI ")" - przypadek ma miejsce, gdy BroadcastReceiver jest zarejestrowany przy użyciu registerReceiver (BroadcastReceiver, IntentFilter, String, Handler), argument programu obsługi nie ma wartości null i odnosi się do programu obsługi utworzonego w wątku innym niż główny wątek aplikacji.
Jules
76

Ponieważ dynamicznie rejestrujesz odbiornik, możesz określić, że inny wątek (inny niż wątek interfejsu użytkownika) obsługuje plik onReceive(). Odbywa się to za pomocą parametru Handler funkcji registerReceiver () .

To powiedziawszy, jeśli nie określisz innego programu obsługi, będzie on zawsze obsługiwany w wątku interfejsu użytkownika.

TommyTh
źródło
Tak. Wygląda na to, że możliwość zmiany tego za pomocą parametru Handler jest powodem, dla którego „zabezpieczyli” swój język w dokumentach.
Andrew Mackenzie,
64

Czy BroadcastReceiver.onReceive zawsze działa w wątku interfejsu użytkownika?

Zwykle wszystko zależy od sposobu rejestracji.

Jeśli zarejestrujesz się BroadcastReceiverza pomocą:

registerReceiver(BroadcastReceiver receiver, IntentFilter filter)

Będzie działać w głównym wątku aktywności (aka wątku interfejsu użytkownika) .

Jeśli zarejestrujesz się BroadcastReceiverprzy użyciu ważnego Handler działającego w innym wątku :

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

Będzie działać w kontekście twojego Handler

Na przykład:

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

Szczegóły tutaj i tutaj .

Caner
źródło
3
Po dłuższym przyjrzeniu się tej opcji, w końcu zdałem sobie sprawę, że LocalBroadcastManager nie obsługuje używania niestandardowego modułu obsługi. Więc jeśli używasz LBM zamiast kontekstu do rejestracji swojego odbiornika, to podejście nie ma zastosowania. Niestety w takim przypadku wydaje się, że jedyną naszą opcją jest skorzystanie z usługi, aby dostać się w tle i uniknąć błędów ANR, które odbiorniki wyzwalają po 10 sekundach bezczynności.
gMale
9

Jak poprawnie podano poprzednie odpowiedzi, onReceivebędzie działać w wątku, w którym jest zarejestrowany, jeśli registerReceiver()wywoływany jest smak, który akceptuje funkcję obsługi - w przeciwnym razie w wątku głównym.

Z wyjątkiem sytuacji, gdy odbiorca jest zarejestrowany w usłudze, LocalBroadcastManagera transmisja odbywa się za pośrednictwem sendBroadcastSync- gdzie najwyraźniej będzie działać w wywoływanym wątkusendBroadcastSync.

Mr_and_Mrs_D
źródło
Nie zgadzam się z częścią and the broadcast is via sendBroadcastSync. Kiedy używamy LocalBroadcastManagerdo rejestrowania odbiornika, musi być wywoływany przez główny wątek, niezależnie od tego, czy używasz, sendBroadcastSyncczy sendBroadcast. Więc kluczem jest użycie LocalBroadcastManagerdo rejestracji. Czy mam rację?
kidoher,
@kidoher: Czy skorzystałeś z linków do kodu tutaj: stackoverflow.com/q/20820244/281545 ?
Mr_and_Mrs_D
0

TAK Context.registerReceiver (odbiornik BroadcastReceiver, filtr IntentFilter, String broadcastPermission, harmonogram obsługi)

Akash
źródło