Aplikacje na Androida wysyłają i odbierają komunikaty transmisji z systemu Android i innych aplikacji na Androida, podobnie jak w przypadku wzorca projektowego publikowania i subskrybowania. System i aplikacje zwykle wysyłają transmisje, gdy wystąpią określone zdarzenia. Na przykład system Android wysyła transmisje, gdy wystąpią różne zdarzenia systemowe, takie jak uruchomienie systemu lub ładowanie urządzenia. Aplikacje wysyłają też niestandardowe transmisje, np. aby powiadomić inne aplikacje o czymś, co może je zainteresować (np. o pobraniu nowych danych).
Aplikacje mogą się zarejestrować, aby otrzymywać określone transmisje. Gdy transmisja zostanie wysłana, system automatycznie kieruje ją do aplikacji, które subskrybują odbieranie tego konkretnego typu transmisji.
Ogólnie rzecz biorąc, transmisje mogą być używane jako system przesyłania wiadomości między aplikacjami i poza normalnym przepływem pracy użytkownika. Musisz jednak uważać, aby nie nadużywać możliwości odpowiadania na transmisje i uruchamiania zadań w tle, które mogą przyczyniać się do spowolnienia działania systemu.
Informacje o transmisjach systemowych
System automatycznie wysyła transmisje, gdy wystąpią różne zdarzenia systemowe, np. gdy system przechodzi w tryb samolotowy lub z niego wychodzi. Wszystkie subskrybowane aplikacje otrzymują te transmisje.
Komunikat transmisji jest opakowany w obiekt Intent. Ciąg znaków action identyfikuje
zdarzenie, które wystąpiło, np. android.intent.action.AIRPLANE_MODE. Intencja może też zawierać dodatkowe informacje spakowane w polu dodatkowym.
Na przykład intencja trybu samolotowego zawiera dodatkową wartość logiczną, która wskazuje, czy tryb samolotowy jest włączony.
Więcej informacji o tym, jak odczytywać intencje i pobierać z nich ciąg znaków działania z intencji, znajdziesz w artykule Intencje i filtry intencji.
Działania związane z komunikatami systemowymi
Pełną listę działań związanych z komunikatami systemowymi znajdziesz w pliku BROADCAST_ACTIONS.TXT w pakiecie Android SDK. Każde działanie związane z transmisją ma powiązane z nim pole stałe. Na przykład wartość stałej
ACTION_AIRPLANE_MODE_CHANGED to android.intent.action.AIRPLANE_MODE.
Dokumentacja każdego działania związanego z transmisją jest dostępna w powiązanym z nim polu stałym.
Zmiany w transmisjach systemowych
W miarę rozwoju platformy Android okresowo zmienia się sposób działania transmisji systemowych. Aby obsługiwać wszystkie wersje Androida, pamiętaj o tych zmianach.
Android 16
W Androidzie 16 kolejność dostarczania transmisji za pomocą atrybutu android:priority
lub IntentFilter.setPriority() w różnych procesach
nie będzie gwarantowana. Priorytety transmisji są uwzględniane tylko w ramach tego samego procesu aplikacji, a nie we wszystkich procesach.
Priorytety transmisji są też automatycznie ograniczone do zakresu
(SYSTEM_LOW_PRIORITY + 1, SYSTEM_HIGH_PRIORITY - 1).
Tylko komponenty systemowe mogą ustawiać SYSTEM_LOW_PRIORITY i SYSTEM_HIGH_PRIORITY jako priorytet transmisji.
Android 14
Gdy aplikacje są w stanie buforowanym, system optymalizuje dostarczanie transmisji
pod kątem stanu systemu. Na przykład system odracza mniej ważne systemowe
transmisje, takie jak ACTION_SCREEN_ON, gdy aplikacja jest w stanie buforowanym.
Gdy aplikacja przechodzi ze stanu buforowanego do aktywnego cyklu życia procesu,
system dostarcza wszystkie odroczone transmisje.
Ważne transmisje, które są zadeklarowane w manifeście, tymczasowo usuwają aplikacje ze stanu buforowanego na potrzeby dostarczenia.
Android 9
Począwszy od Androida 9 (poziom interfejsu API 28), transmisja NETWORK_STATE_CHANGED_ACTION
nie otrzymuje informacji o lokalizacji użytkownika ani danych umożliwiających
identyfikację.
Jeśli Twoja aplikacja jest zainstalowana na urządzeniu z Androidem 9.0 (poziom interfejsu API 28) lub nowszym, system nie uwzględnia identyfikatorów SSID, BSSID, informacji o połączeniu ani wyników skanowania w transmisjach Wi-Fi. Aby uzyskać te informacje, zamiast tego wywołaj
getConnectionInfo().
Android 8.0
Począwszy od Androida 8.0 (poziom interfejsu API 26), system nakłada dodatkowe ograniczenia na odbiorniki zadeklarowane w manifeście.
Jeśli Twoja aplikacja jest kierowana na Androida 8.0 lub nowszego, nie możesz używać manifestu do deklarowania odbiornika większości transmisji niejawnych (transmisji, które nie są kierowane konkretnie na Twoją aplikację). Gdy użytkownik aktywnie korzysta z Twojej aplikacji, nadal możesz używać odbiornika zarejestrowanego w kontekście.
Android 7.0
Android 7.0 (poziom interfejsu API 24) i nowsze nie wysyłają tych transmisji systemowych:
Aplikacje kierowane na Androida 7.0 i nowsze muszą też rejestrować transmisję
CONNECTIVITY_ACTION za pomocą
registerReceiver(BroadcastReceiver, IntentFilter). Deklarowanie odbiornika w manifeście nie działa.
Odbieranie transmisji
Aplikacje mogą odbierać transmisje na 2 sposoby: za pomocą odbiorników zarejestrowanych w kontekście i odbiorników zadeklarowanych w manifeście.
Odbiorniki zarejestrowane w kontekście
Odbiorniki zarejestrowane w kontekście odbierają transmisje, dopóki ich kontekst rejestracji jest prawidłowy. Zwykle dzieje się to między wywołaniami registerReceiver i
unregisterReceiver. Kontekst rejestracji staje się też nieprawidłowy, gdy system niszczy odpowiedni kontekst. Jeśli na przykład zarejestrujesz się w
kontekście Activity, będziesz otrzymywać transmisje, dopóki aktywność
pozostanie aktywna. Jeśli zarejestrujesz się w kontekście aplikacji, będziesz otrzymywać transmisje, dopóki aplikacja będzie działać.
Aby zarejestrować odbiornik w kontekście, wykonaj te czynności:
W pliku kompilacji na poziomie modułu aplikacji dołącz bibliotekę AndroidX Core w wersji 1.9.0 lub nowszej:
Groovy
dependencies { def core_version = "1.19.0" // Java language implementation implementation "androidx.core:core:$core_version" // Kotlin implementation "androidx.core:core-ktx:$core_version" // To use RoleManagerCompat implementation "androidx.core:core-role:1.1.0" // To use the Animator APIs implementation "androidx.core:core-animation:1.0.0" // To test the Animator APIs androidTestImplementation "androidx.core:core-animation-testing:1.0.0" // Optional - To enable APIs that query the performance characteristics of GMS devices. implementation "androidx.core:core-performance:1.0.0" // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google implementation "androidx.core:core-google-shortcuts:1.1.0" // Optional - to support backwards compatibility of RemoteViews implementation "androidx.core:core-remoteviews:1.1.0" // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation "androidx.core:core-splashscreen:1.2.0" }
Kotlin
dependencies { val core_version = "1.19.0" // Java language implementation implementation("androidx.core:core:$core_version") // Kotlin implementation("androidx.core:core-ktx:$core_version") // To use RoleManagerCompat implementation("androidx.core:core-role:1.1.0") // To use the Animator APIs implementation("androidx.core:core-animation:1.0.0") // To test the Animator APIs androidTestImplementation("androidx.core:core-animation-testing:1.0.0") // Optional - To enable APIs that query the performance characteristics of GMS devices. implementation("androidx.core:core-performance:1.0.0") // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google implementation("androidx.core:core-google-shortcuts:1.1.0") // Optional - to support backwards compatibility of RemoteViews implementation("androidx.core:core-remoteviews:1.1.0") // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation("androidx.core:core-splashscreen:1.2.0") }
Utwórz instancję
BroadcastReceiver:Kotlin
val myBroadcastReceiver = MyBroadcastReceiver()Java
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();Utwórz instancję
IntentFilter:Kotlin
val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")Java
IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");Wybierz, czy odbiornik ma być eksportowany i widoczny dla innych aplikacji na urządzeniu. Jeśli ten odbiornik nasłuchuje transmisji wysyłanych przez system lub inne aplikacje (nawet inne aplikacje, których jesteś właścicielem), użyj flagi
RECEIVER_EXPORTED. Jeśli ten odbiornik nasłuchuje tylko transmisji wysyłanych przez Twoją aplikację, użyj flagiRECEIVER_NOT_EXPORTED.Kotlin
val listenToBroadcastsFromOtherApps = false val receiverFlags = if (listenToBroadcastsFromOtherApps) { ContextCompat.RECEIVER_EXPORTED } else { ContextCompat.RECEIVER_NOT_EXPORTED }Java
boolean listenToBroadcastsFromOtherApps = false; int receiverFlags = listenToBroadcastsFromOtherApps ? ContextCompat.RECEIVER_EXPORTED : ContextCompat.RECEIVER_NOT_EXPORTED;Zarejestruj odbiornik, wywołując
registerReceiver():Kotlin
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)Java
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);Aby przestać odbierać transmisje, wywołaj
unregisterReceiver(android.content.BroadcastReceiver). Pamiętaj, aby wyrejestrować odbiornik, gdy nie będzie już potrzebny lub gdy kontekst stanie się nieprawidłowy.
Wyrejestrowywanie odbiornika
Gdy odbiornik jest zarejestrowany, przechowuje odniesienie do kontekstu, w którym został zarejestrowany. Może to spowodować wycieki pamięci, jeśli zarejestrowany zakres odbiornika przekracza zakres cyklu życia kontekstu. Może się to zdarzyć na przykład wtedy, gdy zarejestrujesz odbiornik w zakresie aktywności, ale zapomnisz go wyrejestrować, gdy system zniszczy aktywność. Dlatego zawsze wyrejestrowuj odbiornik.
Kotlin
class MyActivity : ComponentActivity() {
private val myBroadcastReceiver = MyBroadcastReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
setContent { MyApp() }
}
override fun onDestroy() {
super.onDestroy()
// When you forget to unregister your receiver here, you're causing a leak!
this.unregisterReceiver(myBroadcastReceiver)
}
}
Java
class MyActivity extends ComponentActivity {
MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
// Set content
}
}
Rejestrowanie odbiorników w najmniejszym zakresie
Odbiornik powinien być zarejestrowany tylko wtedy, gdy rzeczywiście interesuje Cię wynik. Wybierz najmniejszy możliwy zakres odbiornika:
LifecycleResumeEffectlub metody cyklu życia aktywnościonResume/onPause: odbiornik otrzymuje aktualizacje tylko wtedy, gdy aplikacja jest w stanie wznowienia.LifecycleStartEffectlub metody cyklu życia aktywnościonStart/onStop: odbiornik otrzymuje aktualizacje tylko wtedy, gdy aplikacja jest w stanie wznowienia.DisposableEffect: odbiornik otrzymuje aktualizacje tylko wtedy, gdy element kompozycyjny znajduje się w drzewie kompozycji. Ten zakres nie jest powiązany z zakresem cyklu życia aktywności. Rozważ zarejestrowanie odbiornika w kontekście aplikacji. Element kompozycyjny może teoretycznie przetrwać zakres cyklu życia działania i spowodować wyciek pamięci.- Aktywność
onCreate/onDestroy: odbiornik otrzymuje aktualizacje, gdy aktywność jest w stanie utworzenia. Pamiętaj, aby wyrejestrować się wonDestroy(), a nie wonSaveInstanceState(Bundle), ponieważ ta metoda może nie zostać wywołana. - Zakres niestandardowy: możesz na przykład zarejestrować odbiornik w zakresie
ViewModel, aby przetrwał ponowne utworzenie aktywności. Pamiętaj, aby użyć kontekstu aplikacji do zarejestrowania odbiornika, ponieważ może on przetrwać zakres cyklu życia aktywności i spowodować wyciek pamięci.
Tworzenie elementów kompozycyjnych ze stanem i bez stanu
Compose ma elementy kompozycyjne ze stanem i bez stanu. Zarejestrowanie lub wyrejestrowanie odbiornika w elemencie kompozycyjnym sprawia, że staje się on elementem ze stanem. Element kompozycyjny nie jest funkcją deterministyczną, która renderuje tę samą treść, gdy przekazywane są te same parametry. Stan wewnętrzny może się zmieniać w zależności od wywołań zarejestrowanego odbiornika.
Zgodnie z najlepszymi praktykami w Compose zalecamy dzielenie elementów kompozycyjnych na wersje ze stanem i bez stanu. Dlatego zalecamy, aby przenieść tworzenie odbiornika poza element kompozycyjny, aby stał się on elementem bez stanu:
@Composable
fun MyStatefulScreen() {
val myBroadcastReceiver = remember { MyBroadcastReceiver() }
val context = LocalContext.current
LifecycleStartEffect(true) {
// ...
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
}
MyStatelessScreen()
}
@Composable
fun MyStatelessScreen() {
// Implement your screen
}
Odbiorniki zadeklarowane w manifeście
Jeśli zadeklarujesz odbiornik w manifeście, system uruchomi Twoją aplikację, gdy zostanie wysłana transmisja. Jeśli aplikacja nie jest jeszcze uruchomiona, system ją uruchomi.
Aby zadeklarować odbiornik w manifeście, wykonaj te czynności:
W manifeście aplikacji określ element
<receiver>.<!-- If this receiver listens for broadcasts sent from the system or from other apps, even other apps that you own, set android:exported to "true". --> <receiver android:name=".MyBroadcastReceiver" android:exported="false"> <intent-filter> <action android:name="com.example.snippets.ACTION_UPDATE_DATA" /> </intent-filter> </receiver>Filtry intencji określają działania związane z transmisją, które subskrybuje odbiornik.
Utwórz podklasę
BroadcastReceiveri zaimplementujonReceive(Context, Intent). Odbiornik w poniższym przykładzie rejestruje i wyświetla zawartość transmisji:Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var dataRepository: DataRepository override fun onReceive(context: Context, intent: Intent) { if (intent.action == "com.example.snippets.ACTION_UPDATE_DATA") { val data = intent.getStringExtra("com.example.snippets.DATA") ?: "No data" // Do something with the data, for example send it to a data repository: dataRepository.updateData(data) } } }Java
public static class MyBroadcastReceiver extends BroadcastReceiver { @Inject DataRepository dataRepository; @Override public void onReceive(Context context, Intent intent) { if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) { String data = intent.getStringExtra("com.example.snippets.DATA"); // Do something with the data, for example send it to a data repository: if (data != null) { dataRepository.updateData(data); } } } }
Menedżer pakietów systemu rejestruje odbiornik podczas instalowania aplikacji. Odbiornik staje się wtedy osobnym punktem wejścia do aplikacji, co oznacza, że system może uruchomić aplikację i dostarczyć transmisję, jeśli aplikacja nie jest uruchomiona.
System tworzy nowy BroadcastReceiver obiekt komponentu, aby obsługiwać
każdą otrzymaną transmisję. Ten obiekt jest ważny tylko przez czas trwania
wywołania onReceive(Context, Intent). Gdy kod powróci z tej metody, system uzna, że komponent nie jest już aktywny.
Wpływ na stan procesu
To, czy BroadcastReceiver działa, czy nie, wpływa na proces, w którym się znajduje
, co może zmienić prawdopodobieństwo jego zakończenia przez system. Proces działający na pierwszym planie
wykonuje metodę onReceive() odbiornika. System uruchamia proces, z wyjątkiem sytuacji, gdy występuje ekstremalne obciążenie pamięci.
Po wywołaniu onReceive() system dezaktywuje BroadcastReceiver.
Znaczenie procesu hosta odbiornika zależy od jego komponentów aplikacji. Jeśli ten proces hostuje tylko odbiornik zadeklarowany w manifeście, system może go zakończyć po wywołaniu onReceive(), aby zwolnić zasoby dla innych, bardziej krytycznych procesów. Jest to typowe w przypadku aplikacji, z których użytkownik nigdy nie korzystał lub nie korzystał ostatnio.
Dlatego odbiorniki nie powinny inicjować długotrwałych wątków w tle.
System może w dowolnym momencie po wywołaniu onReceive() zatrzymać proces, aby odzyskać pamięć, co spowoduje zakończenie utworzonego wątku. Aby utrzymać proces przy życiu, zaplanuj
JobService z odbiornika za pomocą JobScheduler, aby
system wiedział, że proces nadal działa. Więcej informacji znajdziesz w artykule
Przegląd pracy w tle.
Wysyłanie transmisji
Android udostępnia 2 sposoby wysyłania transmisji przez aplikacje:
- Metoda
sendOrderedBroadcast(Intent, String)wysyła transmisje do 1 odbiornika naraz. Gdy każdy odbiornik jest wykonywany po kolei, może przekazywać wynik do następnego odbiornika. Może też całkowicie przerwać transmisję, aby nie dotarła do innych odbiorników. Możesz kontrolować kolejność, w jakiej odbiorniki są uruchamiane w ramach tego samego procesu aplikacji. Aby to zrobić, użyj atrybutuandroid:prioritypasującego filtra intencji. Odbiorniki o tym samym priorytecie są uruchamiane w dowolnej kolejności. - Metoda
sendBroadcast(Intent)wysyła transmisje do wszystkich odbiorników w nieokreślonej kolejności. Nazywa się to transmisją normalną. Jest to bardziej wydajne, ale oznacza, że odbiorniki nie mogą odczytywać wyników z innych odbiorników, propagować danych otrzymanych z transmisji ani przerywać transmisji.
Ten fragment kodu pokazuje, jak wysłać transmisję, tworząc intencję i wywołując sendBroadcast(Intent).
Kotlin
val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
putExtra("com.example.snippets.DATA", newData)
setPackage("com.example.snippets")
}
context.sendBroadcast(intent)
Java
Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
intent.putExtra("com.example.snippets.DATA", newData);
intent.setPackage("com.example.snippets");
context.sendBroadcast(intent);
Komunikat transmisji jest opakowany w obiekt Intent. Ciąg znaków action intencji musi zawierać składnię nazwy pakietu Java aplikacji i jednoznacznie identyfikować zdarzenie transmisji. Możesz dołączyć dodatkowe informacje do
intencji za pomocą putExtra(String, Bundle). Możesz też ograniczyć transmisję do
zestawu aplikacji w tej samej organizacji, wywołując setPackage(String) w
intencji.
Ograniczanie transmisji za pomocą uprawnień
Uprawnienia pozwalają ograniczyć transmisje do zestawu aplikacji, które mają określone uprawnienia. Możesz wymusić ograniczenia dotyczące nadawcy lub odbiorcy transmisji.
Wysyłanie transmisji z uprawnieniami
Gdy wywołujesz sendBroadcast(Intent, String) lub
sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String,
Bundle)
, możesz określić parametr uprawnień. Transmisję mogą odbierać tylko odbiorniki, które poprosiły o to
uprawnienie za pomocą tagu <uses-permission> w manifeście. Jeśli uprawnienie jest niebezpieczne, musisz je przyznać, zanim odbiornik będzie mógł odbierać transmisję. Na przykład ten kod wysyła transmisję z uprawnieniem:
Kotlin
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)
Java
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);
Aby odbierać transmisję, aplikacja odbierająca musi poprosić o uprawnienie w ten sposób:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Możesz określić istniejące uprawnienie systemowe, takie jak
BLUETOOTH_CONNECT lub zdefiniować uprawnienie niestandardowe za pomocą elementu
<permission>. Więcej informacji o uprawnieniach i bezpieczeństwie znajdziesz w
artykule Uprawnienia systemowe.
Odbieranie transmisji z uprawnieniami
Jeśli podczas rejestrowania odbiornika określisz parametr uprawnień
(za pomocą
registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) lub w
<receiver> tagu w manifeście), tylko nadawcy, którzy
poprosili o uprawnienie za pomocą <uses-permission> tagu w
manifeście, będą mogli wysyłać intencję do odbiornika. Jeśli uprawnienie jest niebezpieczne, nadawca musi też je otrzymać.
Załóżmy na przykład, że aplikacja odbierająca ma odbiornik zadeklarowany w manifeście w ten sposób:
<!-- If this receiver listens for broadcasts sent from the system or from
other apps, even other apps that you own, set android:exported to "true". -->
<receiver
android:name=".MyBroadcastReceiverWithPermission"
android:permission="android.permission.ACCESS_COARSE_LOCATION"
android:exported="true">
<intent-filter>
<action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
</intent-filter>
</receiver>
Lub aplikacja odbierająca ma odbiornik zarejestrowany w kontekście w ten sposób:
Kotlin
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
)
Java
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
);
Aby móc wysyłać transmisje do tych odbiorników, aplikacja wysyłająca musi poprosić o uprawnienie w ten sposób:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Unikanie transmisji w ramach tego samego procesu
Transmisje są zaprojektowane jako mechanizm komunikacji międzyprocesowej (IPC) do wysyłania wiadomości między różnymi aplikacjami lub między systemem a aplikacjami. Wysyłanie transmisji z procesu do odbiornika działającego w tym samym procesie jest bardzo nieefektywne, powoduje niepotrzebne obciążenie systemu i jest zdecydowanie odradzane.
Dwa typowe scenariusze, w których aplikacje wysyłają do siebie transmisje:
Komunikacja między komponentami w tym samym procesie: np. przekazywanie zdarzeń lub danych między aktywnościami, fragmentami, usługami lub wątkami w tle. Zamiast wysyłać transmisje, używaj standardowych mechanizmów komunikacji w ramach procesu, takich jak wzorzec obserwatora lub strumienie reaktywne:
- Kotlin Flows (
SharedFlowiStateFlow): nowoczesne, idiomatyczne rozwiązanie w Kotlinie do emitowania i obserwowania strumieni zdarzeń lub aktualizacji stanu w ramach korutyn i komponentów w aplikacji. - Współdzielony
ViewModel: ułatwia udostępnianie danych i zdarzeń między różnymi komponentami interfejsu (np. fragmentami lub elementami kompozycyjnymi) w ramach tej samej aktywności. - Wywołania zwrotne i odbiorniki: standardowe wywołania zwrotne interfejsu lub odniesienia do funkcji przekazywane bezpośrednio między komponentami lub zarejestrowane w centralnym repozytorium lub kontrolerze.
- Kotlin Flows (
Obsługa zdarzeń systemowych, zadań lub alarmów: np. odbieranie zaplanowanego zadania, alarmu lub wywołania zwrotnego systemu, a następnie wysyłanie transmisji w celu uruchomienia rzeczywistej pracy. Zamiast wysyłać transmisję, wykonaj pracę bezpośrednio w ramach tego zadania (np.
JobServicelub WorkManager), modułu obsługi alarmów lub komponentu wywołania zwrotnego systemu albo przekieruj bezpośrednio do klas logiki biznesowej aplikacji.
Jeśli system wykryje transmisję do samego siebie, może spróbować zoptymalizować dostarczanie, przekierowując ją w ramach procesu. Jest to jednak mniej wydajne niż opisane wcześniej alternatywy komunikacji w ramach procesu, dlatego należy używać tych alternatyw.
Więcej informacji o projektowaniu komunikacji między komponentami aplikacji znajdziesz w przewodniku po architekturze aplikacji.
Bezpieczeństwo
Oto kilka kwestii związanych z bezpieczeństwem wysyłania i odbierania transmisji:
Jeśli wiele aplikacji zarejestrowało się w manifeście, aby odbierać tę samą transmisję, może to spowodować uruchomienie przez system wielu aplikacji, co będzie miało znaczący wpływ na wydajność urządzenia i wygodę użytkownika. Aby tego uniknąć, zamiast deklaracji w manifeście używaj rejestracji w kontekście. Czasami sam system Android wymusza używanie odbiorników zarejestrowanych w kontekście. Na przykład transmisja
CONNECTIVITY_ACTIONjest dostarczana tylko do odbiorników zarejestrowanych w kontekście.Nie transmituj informacji poufnych za pomocą intencji ogólnej. Każda aplikacja może odczytać te informacje, jeśli zarejestruje się, aby odbierać transmisję. Istnieją 3 sposoby kontrolowania, kto może odbierać Twoje transmisje:
- Podczas wysyłania transmisji możesz określić uprawnienie.
- W Androidzie 4.0 (poziom interfejsu API 14) i nowszych podczas wysyłania transmisji możesz określić
pakiet za pomocą
setPackage(String). System ogranicza transmisję do zestawu aplikacji, które pasują do pakietu.
Gdy zarejestrujesz odbiornik, każda aplikacja może wysyłać do niego potencjalnie złośliwe transmisje. Istnieje kilka sposobów ograniczenia transmisji odbieranych przez Twoją aplikację:
- Podczas rejestrowania odbiornika możesz określić uprawnienie.
- W przypadku odbiorników zadeklarowanych w manifeście możesz ustawić atrybut android:exported na „false” w manifeście. Odbiornik nie będzie odbierać transmisji ze źródeł spoza aplikacji.
Przestrzeń nazw działań związanych z transmisją jest globalna. Upewnij się, że nazwy działań i inne ciągi znaków są zapisane w przestrzeni nazw, której jesteś właścicielem. W przeciwnym razie możesz nieumyślnie wejść w konflikt z innymi aplikacjami.
Ponieważ metoda
onReceive(Context, Intent)odbiornika jest uruchamiana w wątku głównym, powinna być wykonywana i zwracać wynik szybko. Jeśli musisz wykonać długotrwałą pracę, uważaj na tworzenie wątków lub uruchamianie usług w tle, ponieważ system może zakończyć cały proces po powrocie zonReceive(). Więcej informacji znajdziesz w artykule Wpływ na stan procesu. Aby wykonać długotrwałą pracę, zalecamy:- Wywołanie
goAsync()w metodzieonReceive()odbiornika i przekazanieBroadcastReceiver.PendingResultdo wątku w tle. Dzięki temu transmisja pozostaje aktywna po powrocie zonReceive(). Nawet w tym przypadku system oczekuje jednak, że zakończysz pracę z transmisją bardzo szybko (w ciągu 10 sekund). Umożliwia to przeniesienie pracy do innego wątku, aby uniknąć zakłóceń w wątku głównym. - Zaplanowanie zadania za pomocą
JobScheduler. Więcej informacji znajdziesz w artykule Inteligentne planowanie zadań.
- Wywołanie
Nie uruchamiaj aktywności z odbiorników, ponieważ może to powodować problemy z wygodą użytkownika, zwłaszcza jeśli jest więcej niż 1 odbiornik. Zamiast tego rozważ wyświetlenie powiadomienia.