Przegląd transmisji

Aplikacje na Androida wysyłają i odbierają komunikaty rozgłoszeniowe z systemu Android i innych aplikacji na Androida, podobnie jak w przypadku wzorca projektowego publikowania i subskrybowania. System i aplikacje zwykle wysyłają komunikaty rozgłoszeniowe, gdy wystąpią określone zdarzenia. Na przykład system Android wysyła komunikaty rozgłoszeniowe, gdy wystąpią różne zdarzenia systemowe, takie jak uruchomienie systemu lub ładowanie urządzenia. Aplikacje wysyłają też niestandardowe komunikaty rozgłoszeniowe, 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 komunikaty rozgłoszeniowe. Gdy komunikat rozgłoszeniowy zostanie wysłany, system automatycznie kieruje go do aplikacji, które subskrybują odbieranie tego konkretnego typu komunikatu.

Ogólnie rzecz biorąc, komunikaty mogą być używane jako system przesyłania wiadomości między aplikacjami i poza normalnym wzorcem przeglądania użytkownika. Musisz jednak uważać, aby nie nadużywać możliwości odpowiadania na komunikaty rozgłoszeniowe i uruchamiania zadań w tle, które mogą przyczyniać się do spowolnienia działania systemu.

Informacje o komunikatach systemowych

System automatycznie wysyła komunikaty rozgłoszeniowe, gdy wystąpią różne zdarzenia systemowe, np. gdy system przełącza się w tryb samolotowy lub z niego wychodzi. Wszystkie subskrybowane aplikacje otrzymują te komunikaty rozgłoszeniowe.

Komunikat rozgłoszeniowy 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. Z każdym działaniem związanym z komunikatem rozgłoszeniowym jest powiązane 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 komunikatem rozgłoszeniowym jest dostępna w powiązanym z nim polu stałym.

Zmiany w komunikatach systemowych

Wraz z rozwojem platformy Android okresowo zmienia się sposób działania komunikatów systemowych. Aby obsługiwać wszystkie wersje Androida, pamiętaj o tych zmianach.

Android 16

W Androidzie 16 kolejność dostarczania komunikatów rozgłoszeniowych za pomocą atrybutu android:priority lub IntentFilter.setPriority() w różnych procesach nie będzie gwarantowana. Priorytety komunikatów rozgłoszeniowych są uwzględniane tylko w ramach tego samego procesu aplikacji, a nie we wszystkich procesach.

Priorytety komunikatów rozgłoszeniowych są też automatycznie ograniczane do zakresu (SYSTEM_LOW_PRIORITY + 1, SYSTEM_HIGH_PRIORITY - 1). Tylko komponenty systemowe mogą ustawiać SYSTEM_LOW_PRIORITY i SYSTEM_HIGH_PRIORITY jako priorytet komunikatu rozgłoszeniowego.

Android 14

Gdy aplikacje są w stanie buforowanym, system optymalizuje dostarczanie komunikatów rozgłoszeniowych pod kątem stanu systemu. Na przykład gdy aplikacja jest w stanie buforowanym, system odracza mniej ważne komunikaty rozgłoszeniowe systemu takie jak ACTION_SCREEN_ON. Gdy aplikacja przejdzie ze stanu buforowanego do aktywnego cyklu życia procesu, system dostarczy wszystkie odroczone komunikaty rozgłoszeniowe.

Ważne komunikaty rozgłoszeniowe zadeklarowane w manifeście tymczasowo usuwają aplikacje ze stanu buforowanego na czas dostarczenia.

Android 9

Od Androida 9 (poziom interfejsu API 28) komunikat rozgłoszeniowy NETWORK_STATE_CHANGED_ACTION nie zawiera informacji o lokalizacji użytkownika ani danych umożliwiających identyfikację.

Jeśli aplikacja jest zainstalowana na urządzeniu z Androidem 9.0 (poziom interfejsu API 28) lub nowszym, system nie uwzględnia w komunikatach rozgłoszeniowych Wi-Fi identyfikatorów SSID, BSSID, informacji o połączeniu ani wyników skanowania. Aby uzyskać te informacje, zamiast tego wywołaj getConnectionInfo().

Android 8.0

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ć pliku manifestu do deklarowania odbiornika większości komunikatów ogólnych (komunikatów rozgłoszeniowych, 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 komunikatów rozgłoszeniowych systemu:

Aplikacje kierowane na Androida 7.0 i nowszego muszą też rejestrować komunikat rozgłoszeniowy CONNECTIVITY_ACTION za pomocą registerReceiver(BroadcastReceiver, IntentFilter). Deklarowanie odbiornika w manifeście nie działa.

Odbieranie komunikatów rozgłoszeniowych

Aplikacje mogą odbierać komunikaty rozgłoszeniowe 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ą komunikaty rozgłoszeniowe, dopóki ich kontekst rejestracji jest prawidłowy. Zwykle jest to okres 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ć komunikaty rozgłoszeniowe, dopóki aktywność pozostanie aktywna. Jeśli zarejestrujesz się w kontekście aplikacji, będziesz otrzymywać komunikaty rozgłoszeniowe, dopóki aplikacja będzie działać.

Aby zarejestrować odbiornik w kontekście:

  1. 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.1"
    
        // 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.1"
    
        // 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")
    }
  2. Utwórz instancję BroadcastReceiver:

    Kotlin

    val myBroadcastReceiver = MyBroadcastReceiver()
    

    Java

    MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
    
  3. 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");
    
  4. Wybierz, czy odbiornik ma być eksportowany i widoczny dla innych aplikacji na urządzeniu. Jeśli ten odbiornik nasłuchuje komunikatów rozgłoszeniowych wysyłanych przez system lub inne aplikacje – nawet inne aplikacje, które są Twoją własnością – użyj flagi RECEIVER_EXPORTED. Jeśli ten odbiornik nasłuchuje tylko komunikatów rozgłoszeniowych wysyłanych przez Twoją aplikację, użyj flagi RECEIVER_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;
    
  5. Zarejestruj odbiornik, wywołując registerReceiver():

    Kotlin

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)
    

    Java

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
    
  6. Aby przestać odbierać komunikaty rozgłoszeniowe, 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 interesuje Cię wynik. Wybierz najmniejszy możliwy zakres odbiornika:

  • LifecycleResumeEffect lub metody cyklu życia aktywności onResume/onPause: odbiornik otrzymuje aktualizacje tylko wtedy, gdy aplikacja jest w stanie wznowienia.
  • LifecycleStartEffect lub metody cyklu życia aktywności onStart/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 działania. Rozważ zarejestrowanie odbiornika w kontekście aplikacji. Element kompozycyjny może bowiem teoretycznie przetrwać zakres cyklu życia aktywności i spowodować wyciek pamięci.
  • Aktywność onCreate/onDestroy: odbiornik otrzymuje aktualizacje, gdy aktywność jest w stanie utworzenia. Pamiętaj, aby wyrejestrować się w onDestroy(), a nie w onSaveInstanceState(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ść po przekazaniu tych samych parametrów. 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 przeniesienie tworzenia 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łany komunikat rozgłoszeniowy. Jeśli aplikacja nie jest jeszcze uruchomiona, system ją uruchomi.

Aby zadeklarować odbiornik w manifeście:

  1. 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 komunikatami rozgłoszeniowymi, które subskrybuje Twój odbiornik.

  2. Utwórz podklasę BroadcastReceiver i zaimplementuj onReceive(Context, Intent). Odbiornik w poniższym przykładzie rejestruje i wyświetla zawartość komunikatu rozgłoszeniowego:

    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 jeśli aplikacja nie działa, system może ją uruchomić i dostarczyć komunikat rozgłoszeniowy.

System tworzy nowy BroadcastReceiver obiekt komponentu, aby obsługiwać każdy otrzymany komunikat rozgłoszeniowy. 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órymi użytkownik nigdy nie wchodził w interakcję lub nie robił tego 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 Omówienie pracy w tle.

Wysyłanie komunikatów rozgłoszeniowych

Android umożliwia aplikacjom wysyłanie komunikatów rozgłoszeniowych na 2 sposoby:

  • Metoda sendOrderedBroadcast(Intent, String) wysyła komunikaty rozgłoszeniowe 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ć komunikat rozgłoszeniowy, aby nie dotarł do innych odbiorników. Możesz kontrolować kolejność, w jakiej odbiorniki działają w ramach tego samego procesu aplikacji. Aby to zrobić, użyj atrybutu android:priority pasującego filtra intencji. Odbiorniki o tym samym priorytecie są uruchamiane w dowolnej kolejności.
  • Metoda sendBroadcast(Intent) wysyła komunikaty rozgłoszeniowe do wszystkich odbiorników w nieokreślonej kolejności. Jest to tzw. normalny komunikat rozgłoszeniowy. Jest to bardziej wydajne, ale oznacza, że odbiorniki nie mogą odczytywać wyników z innych odbiorników, propagować danych otrzymanych z komunikatu rozgłoszeniowego ani przerywać komunikatu rozgłoszeniowego.

Poniższy fragment kodu pokazuje, jak wysłać komunikat rozgłoszeniowy, 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 rozgłoszeniowy jest opakowany w obiekt Intent. Ciąg znaków action intencji musi zawierać składnię nazwy pakietu Java aplikacji i jednoznacznie identyfikować zdarzenie rozgłoszeniowe. Możesz dołączyć dodatkowe informacje do intencji za pomocą putExtra(String, Bundle). Możesz też ograniczyć komunikat rozgłoszeniowy do zestawu aplikacji w tej samej organizacji, wywołując setPackage(String) w intencji.

Ograniczanie komunikatów rozgłoszeniowych za pomocą uprawnień

Uprawnienia umożliwiają ograniczenie komunikatów rozgłoszeniowych do zestawu aplikacji, które mają określone uprawnienia. Możesz wymusić ograniczenia dotyczące nadawcy lub odbiorcy komunikatu rozgłoszeniowego.

Wysyłanie komunikatów rozgłoszeniowych z uprawnieniami

Gdy wywołujesz sendBroadcast(Intent, String) lub sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) , możesz określić parametr uprawnienia. Komunikat rozgłoszeniowy 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ć komunikat rozgłoszeniowy. Na przykład ten kod wysyła komunikat rozgłoszeniowy z uprawnieniem:

Kotlin

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)

Java

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);

Aby odbierać komunikat rozgłoszeniowy, 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 komunikatów rozgłoszeniowych z uprawnieniami

Jeśli podczas rejestrowania odbiornika określisz parametr uprawnienia (za pomocą registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) lub w <receiver> tagu w manifeście), tylko nadawcy, którzy poprosili o uprawnienie za pomocą tagu <uses-permission> 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ć komunikaty rozgłoszeniowe 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 komunikatów rozgłoszeniowych w ramach tego samego procesu

Komunikaty rozgłoszeniowe 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 komunikatu rozgłoszeniowego do samego siebie, czyli komunikatu rozgłoszeniowego, którego wszystkie odbiorniki działają w tym samym procesie, który wysłał komunikat rozgłoszeniowy, jest bardzo nieefektywne, powoduje niepotrzebne obciążenie systemu i jest zdecydowanie odradzane.

Dwa typowe scenariusze, w których aplikacje wysyłają komunikaty rozgłoszeniowe do siebie:

  • 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ć komunikaty rozgłoszeniowe, używaj standardowych mechanizmów komunikacji w ramach procesu , takich jak wzorzec obserwatora lub strumienie reaktywne:

    • Kotlin Flows (SharedFlow i StateFlow): 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 (takimi jak fragmenty lub elementy kompozycyjne) 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.
  • Obsługa zdarzeń systemowych, zadań lub alarmów: np. odbieranie zaplanowanego zadania, alarmu lub wywołania zwrotnego systemu, a następnie wysyłanie komunikatu rozgłoszeniowego w celu wywołania rzeczywistej pracy. Zamiast wysyłać komunikat rozgłoszeniowy, wykonaj pracę bezpośrednio w ramach tego zadania (np. JobService lub WorkManager proces roboczy), modułu obsługi alarmu lub komponentu wywołania zwrotnego systemu albo przekieruj bezpośrednio do klas logiki biznesowej aplikacji.

Na urządzeniach z Androidem 17 QPR2 lub nowszym system dostarcza komunikaty rozgłoszeniowe do samego siebie wydajniej, zwracając je do procesu wysyłającego, który dostarcza je do własnych odbiorników w wątku głównym. Wysyłanie komunikatu rozgłoszeniowego do samego siebie nie zwiększa znaczenia procesu i nie zapobiega zamrożeniu procesu buforowanego, więc nie polegaj na komunikatach rozgłoszeniowych do samego siebie, aby utrzymać działanie aplikacji lub wykonywać pracę w tle. Zamiast tego wybierz odpowiedni interfejs API do swojego zadania zgodnie z opisem w artykule Omówienie zadań w tle. Nawet w przypadku tej optymalizacji komunikaty rozgłoszeniowe do samego siebie są mniej wydajne niż mechanizmy komunikacji w ramach procesu, dlatego zamiast nich używaj 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 podczas wysyłania i odbierania komunikatów rozgłoszeniowych:

  • Jeśli wiele aplikacji zarejestrowało się w manifeście, aby odbierać ten sam komunikat rozgłoszeniowy, 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 komunikat rozgłoszeniowy CONNECTIVITY_ACTION jest dostarczany tylko do odbiorników zarejestrowanych w kontekście.

  • Nie wysyłaj informacji poufnych za pomocą niejawnej intencji. Każda aplikacja może odczytać te informacje, jeśli zarejestruje się, aby odbierać komunikat rozgłoszeniowy. Istnieją 3 sposoby kontrolowania, kto może odbierać Twoje komunikaty rozgłoszeniowe:

    • Podczas wysyłania komunikatu rozgłoszeniowego możesz określić uprawnienie.
    • W Androidzie 4.0 (poziom interfejsu API 14) i nowszym podczas wysyłania komunikatu rozgłoszeniowego możesz określić a pakiet za pomocą setPackage(String). System ogranicza komunikat rozgłoszeniowy do zestawu aplikacji, które pasują do pakietu.
  • Gdy zarejestrujesz odbiornik, każda aplikacja może wysyłać do niego potencjalnie złośliwe komunikaty rozgłoszeniowe. Istnieje kilka sposobów ograniczenia komunikatów rozgłoszeniowych 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ć komunikatów rozgłoszeniowych ze źródeł spoza aplikacji.
  • Przestrzeń nazw działań związanych z komunikatami rozgłoszeniowymi 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.

  • Metoda onReceive(Context, Intent) odbiornika działa w wątku głównym, dlatego powinna się szybko wykonać i zwrócić wynik. 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 z onReceive(). Więcej informacji znajdziesz w sekcji Wpływ na stan procesu. Aby wykonać długotrwałą pracę, zalecamy:

    • Wywołanie goAsync() w metodzie onReceive() odbiornika i przekazanie BroadcastReceiver.PendingResult do wątku w tle. Dzięki temu komunikat rozgłoszeniowy pozostanie aktywny po powrocie z onReceive(). Nawet w przypadku tego podejścia system oczekuje, że bardzo szybko (w ciągu 10 sekund) zakończysz pracę z komunikatem rozgłoszeniowym. Umożliwia to jednak 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ń.
  • 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.