Przegląd transmisji

Aplikacje na Androida mogą wysyłać i odbierać komunikaty z systemu Android oraz innych aplikacji na Androida, podobnych opublikuj i zasubskrybuj i wzorca projektowy. Te komunikaty są wysyłane, gdy zdarzy się coś interesującego. Na przykład Android wysyła komunikaty, gdy różne zdarzenia systemowe na przykład podczas uruchamiania systemu lub rozpoczęcia ładowania urządzenia. aplikacji; może również wysyłać własne komunikaty, aby na przykład powiadamiać inne aplikacje o tym, co może ich zainteresować (np. nowe dane pobrane).

System optymalizuje dostarczanie transmisji, aby utrzymać w optymalnym stanie systemu. Dlatego czasy dostarczania transmisji nie są podawane gwarantowane. Aplikacje, które wymagają komunikacji międzyprocesowej z krótkim czasem oczekiwania, jako powiązane usługi.

Aplikacje mogą się rejestrować, aby odbierać określone komunikaty. Po wysłaniu transmisji system automatycznie kieruje komunikaty do aplikacji, które subskrybują w konkretny sposób.

Ogólnie komunikaty mogą służyć jako system przesyłania wiadomości w różnych aplikacjach. poza typowym ruchem w przeglądarce. Trzeba jednak uważać, aby nie nadużywać umożliwia reagowanie na komunikaty i uruchamianie zadań w tle, może przyczyniać się do powolnego działania systemu.

Komunikaty systemowe

System automatycznie wysyła komunikaty po wystąpieniu różnych zdarzeń systemowych, na przykład gdy system włącza i wyłącza tryb samolotowy. Systemowy wiadomości są wysyłane do wszystkich aplikacji, które subskrybują .

Sama transmisja wiadomości jest zawijana w element Intent obiektu, którego ciąg działania identyfikuje zdarzenie, które wystąpiło (na przykład android.intent.action.AIRPLANE_MODE). Intencja może też obejmować informacje zawarte w dodatkowym polu. Na przykład: intencja trybu zawiera dodatkową wartość logiczną, która wskazuje, czy tryb samolotowy Tryb jest włączony.

Więcej informacji o tym, jak odczytywać intencje i uzyskiwać ciąg znaków działania z intencja, zapoznaj się z sekcją Intencje i intencja Filtry.

Pełną listę działań związanych z transmisją systemu znajdziesz w BROADCAST_ACTIONS.TXT w pakiecie Android SDK. Każde transmitowane zdarzenie ma 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 polu stałym.

Zmiany w komunikatach systemowych

W miarę rozwoju platformy Android co jakiś czas zmienia ona sposób transmisji systemu. działania. We wszystkich wersjach Androida pamiętaj o tych zmianach.

Android 14

Gdy aplikacje są w pamięci podręcznej województwo, przesyłanie transmisji to są zoptymalizowane pod kątem stanu systemu. Na przykład mniej ważne komunikaty systemowe, takie jak bo ACTION_SCREEN_ON są jest odroczone, gdy aplikacja jest przechowywana w pamięci podręcznej. Gdy aplikacja zostanie usunięta z pamięci podręcznej, w aktywny proces, cyklu życia usługi, wszystkich odroczonych transmisji.

Ważne komunikaty zadeklarowane w manifest tymczasowo usuwa aplikacje z pamięci podręcznej. stan dostawy.

Android 9

Począwszy od Androida 9 (poziom interfejsu API 28) NETWORK_STATE_CHANGED_ACTION komunikat nie otrzymuje informacji o lokalizacji użytkownika ani danych osobowych dane umożliwiające identyfikację.

Jeśli aplikacja jest zainstalowana na urządzeniu z Androidem 9 lub nowszym, komunikaty systemowe z Wi-Fi nie zawierają identyfikatorów SSID, identyfikatorów BSSID ani połączeń. lub skanowanie wyników. Aby uzyskać te informacje, zadzwoń pod numer getConnectionInfo() .

Android 8.0

Począwszy od Androida 8.0 (poziom interfejsu API 26) system nakłada dodatkowe ograniczeń dotyczących odbiorców zadeklarowanych w pliku manifestu.

Jeśli Twoja aplikacja jest kierowana na Androida 8.0 lub nowszego, nie możesz użyć pliku manifestu, aby deklaracja odbiornika dla większości wiadomości niejawnych (transmisji, które nie są celem kierowania) w konkretnej aplikacji). Nadal możesz używać odbiorcy zarejestrowany w kontekście, gdy aktywnie korzysta z aplikacji.

Android 7.0

Android 7.0 (poziom interfejsu API 24) i nowsze wersje nie wysyłają poniższego systemu komunikaty:

Oprócz tego aplikacje kierowane na Androida 7.0 lub nowszego muszą rejestrować transmisję CONNECTIVITY_ACTION za pomocą: registerReceiver(BroadcastReceiver, IntentFilter). Nie można zadeklarować w pliku manifestu odbiorcy.

Odbieranie komunikatów

Aplikacje mogą odbierać komunikaty na 2 sposoby: przez odbiorniki zadeklarowane w pliku manifestu i odbiorców zarejestrowanych na podstawie kontekstu.

Odbiorcy zadeklarowani w pliku manifestu

Jeśli zadeklarujesz w pliku manifestu odbiornik, system uruchomi (jeśli nie jest ona jeszcze uruchomiona).

Aby zadeklarować odbiornik w pliku manifestu, wykonaj te czynności:

  1. Określanie elementu <receiver> w pliku manifestu aplikacji.

    <!-- 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="APP_SPECIFIC_BROADCAST" />
        </intent-filter>
    </receiver>
    

    Filtry intencji określają działania związane z transmisją subskrybowane przez odbiornik.

  2. Podklasa BroadcastReceiver i implementacja onReceive(Context, Intent). odbiornika w poniższych przykładowych logach i wyświetla ich zawartość transmisji:

    Kotlin

    private const val TAG = "MyBroadcastReceiver"
    
    class MyBroadcastReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent) {
            StringBuilder().apply {
                append("Action: ${intent.action}\n")
                append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
                toString().also { log ->
                    Log.d(TAG, log)
    
                    val binding = ActivityNameBinding.inflate(layoutInflater)
                    val view = binding.root
                    setContentView(view)
    
                    Snackbar.make(view, log, Snackbar.LENGTH_LONG).show()
                }
            }
        }
    }
    

    Java

    public class MyBroadcastReceiver extends BroadcastReceiver {
            private static final String TAG = "MyBroadcastReceiver";
            @Override
            public void onReceive(Context context, Intent intent) {
                StringBuilder sb = new StringBuilder();
                sb.append("Action: " + intent.getAction() + "\n");
                sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
                String log = sb.toString();
                Log.d(TAG, log);
    
                ActivityNameBinding binding =
                        ActivityNameBinding.inflate(layoutInflater);
                val view = binding.root;
                setContentView(view);
    
                Snackbar.make(view, log, Snackbar.LENGTH_LONG).show();
            }
        }
    

    Aby włączyć powiązanie widoków: skonfiguruj viewBinding na poziomie modułu. build.gradle.

Systemowy menedżer pakietów rejestruje odbiorcę, gdy aplikacja jest zainstalowana. Odbiorca staje się wówczas osobnym punktem wejścia do aplikacji, że system może uruchomić aplikację i przekazać transmisję, jeśli aplikacja nie uruchomione.

System tworzy nowy komponent BroadcastReceiver. który obsługuje każdą odebraną transmisję. Ten obiekt jest prawidłowy tylko przez cały czas trwania połączenia z numerem onReceive(Context, Intent). Gdy kod zwraca tę metodę, system uzna, że komponent nie jest już aktywne.

Odbiorcy zarejestrowani na podstawie kontekstu

Odbiorcy zarejestrowani w określonym kontekście otrzymują transmisje, dopóki ich rejestracja jest prawidłowy. Jeśli na przykład zarejestrujesz się w Activity otrzymujesz komunikaty, o ile aktywność nie zostanie zniszczona. Jeśli zarejestrować się z kontekstem aplikacji, będziesz otrzymywać komunikaty, jeśli aplikacja jest uruchomiony.

Aby zarejestrować odbiorcę z użyciem kontekstu, wykonaj te czynności:

  1. W pliku kompilacji na poziomie modułu aplikacji umieść wersję 1.9.0 lub nowszą bibliotekę AndroidX Core:

    Groovy

    dependencies {
        def core_version = "1.15.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.0.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-alpha02"
    }

    Kotlin

    dependencies {
        val core_version = "1.15.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.0.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-alpha02")
    }
  2. Utwórz instancję BroadcastReceiver:

    Kotlin

    val br: BroadcastReceiver = MyBroadcastReceiver()
    

    Java

    BroadcastReceiver br = new MyBroadcastReceiver();
    
  3. Utwórz instancję IntentFilter:

    Kotlin

    val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
    

    Java

    IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
    
  4. Wybierz, czy odbiornik ma być eksportowany i widoczny dla z innych aplikacji na urządzeniu. Jeśli ten odbiornik nasłuchuje wysyłanych wiadomości z systemu lub innych aplikacji, nawet innych należących do Ciebie, użyj flaga RECEIVER_EXPORTED. Jeśli zamiast tego ten odbiornik nasłuchuje tylko przez komunikatów 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;
    if (listenToBroadcastsFromOtherApps) {
        receiverFlags = ContextCompat.RECEIVER_EXPORTED;
    } else {
        receiverFlags = ContextCompat.RECEIVER_NOT_EXPORTED;
    }
    
  5. Zarejestruj odbiorcę, dzwoniąc registerReceiver():

    Kotlin

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

    Java

    ContextCompat.registerReceiver(context, br, filter, receiverFlags);
    
  6. Aby przestać otrzymywać komunikaty, zadzwoń pod numer unregisterReceiver(android.content.BroadcastReceiver). Pamiętaj, aby wyrejestrować odbiornik, gdy już go nie potrzebujesz lub Kontekst nie jest już prawidłowy.

    Zastanów się, gdzie rejestrujesz i wyrejestrowujesz odbiorcę, ponieważ Jeśli na przykład zarejestrujesz odbiorcę w usłudze onCreate(Bundle) przy użyciu kontekstu aktywności, powinien wyrejestrować go w usłudze onDestroy() w zapobiec wyciekom odbiorcy z kontekstu aktywności. Jeśli zarejestrujesz się odbiornika w onResume(), musisz wyrejestruj go w onPause(), aby zapobiec rejestrując go wielokrotnie (jeśli nie chcesz otrzymywać komunikatów po wstrzymaniu, co może ograniczyć niepotrzebne koszty systemu). Nie wolno wyrejestruj się w: onSaveInstanceState(Bundle), ponieważ ta funkcja nie jest wywoływana, gdy użytkownik wróci do stosu historii.

Wpływ na stan procesu

Niezależnie od tego, czy BroadcastReceiver działa lub nie ma wpływu na zawarty w nim proces, który może zmieniać skutkuje zniszczeniem systemu. Proces na pierwszym planie wykonuje metodę onReceive() odbiorcy. system uruchamia proces poza nadmiernym obciążeniem pamięci.

Odbiornik BroadcastReceiver zostanie wyłączony po onReceive(). Host odbiorcy jest tak istotny, co jej składniki. Jeśli ten proces obsługuje tylko hosty odbiornik zadeklarowany w pliku manifestu (często występujące w przypadku aplikacji, których użytkownik nigdy lub nie wchodził w interakcję z nim w ostatnim czasie), system może go zamknąć po onReceive(), aby wykonać zasobów dostępnych na potrzeby innych procesów o znaczeniu krytycznym.

Dlatego odbiorniki nie powinny inicjować długotrwałych wątków w tle. Aby odzyskać dane, system może zatrzymać ten proces w dowolnym momencie po onReceive() , zamykając utworzony wątek. Aby kontynuować proces, zaplanuj JobService od odbiornika za pomocą funkcji JobScheduler dzięki czemu system będzie wiedział, że proces wciąż trwa. Więcej informacji zawiera artykuł Omówienie pracy w tle.

Wysyłanie komunikatów

Android umożliwia aplikacjom wysyłanie komunikatów na 3 sposoby:

  • sendOrderedBroadcast(Intent, String) wysyła komunikaty do jednego odbiornika w danym momencie. Gdy wykonuje się każdy odbiorca z kolei może przekazać wynik do następnego odbiorcy lub całkowicie przerwać transmisję, aby nie została przekazana do innych odbiorców. Kolejność odbiorców, do których trafiają odbiorcy, można kontrolować za pomocą atrybutu android:Priority pasującego filtra intencji; odbiorników z będzie uruchamiany w dowolnej kolejności.
  • Metoda sendBroadcast(Intent) wysyła – komunikaty są wysyłane do wszystkich odbiorników w nieokreślonej kolejności. Jest to tzw. Normalny Transmisja. Jest to bardziej wydajne, ale oznacza, że odbiorcy nie mogą odczytywać wyników od innych odbiorców, propagować dane otrzymane z transmisji; przerwać transmisję.

Fragment kodu poniżej pokazuje, jak wysłać transmisję przez utworzenie Intencja i wywoływanie: sendBroadcast(Intent).

Kotlin

Intent().also { intent ->
    intent.setAction("com.example.broadcast.MY_NOTIFICATION")
    intent.putExtra("data", "Nothing to see here, move along.")
    sendBroadcast(intent)
}

Java

Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data", "Nothing to see here, move along.");
sendBroadcast(intent);

Przesłany komunikat jest zapakowany w obiekt Intent. Ciąg działania intencji musi zawierać składnię nazwy pakietu Java oraz jednoznacznie identyfikują transmitowane wydarzenie. Możesz dołączyć dodatkowe informacje do intencji za pomocą funkcji putExtra(String, Bundle). Możesz też ograniczyć transmisję do zestawu aplikacji w tej samej organizacji przez: Wywołuję funkcję setPackage(String) w intencji.

Ograniczanie transmisji z uprawnieniami

Uprawnienia pozwalają ograniczyć transmisję do grupy aplikacji, które pewnych uprawnień. Możesz egzekwować ograniczenia na poziomie nadawcy lub odbiornikiem komunikatu.

Wysyłanie z uprawnieniami

Gdy zadzwonisz do: sendBroadcast(Intent, String) lub sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle), możesz określić Uprawnienia. Tylko odbiorcy, którzy poprosili o to uprawnienie w w pliku manifestu (a następnie otrzymuje tag w przypadku, gdy jest ona niebezpieczna) może odbierać transmisję. Na przykład parametr ten kod wysyła komunikat:

Kotlin

sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

Java

sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

Aby odebrać transmisję, aplikacja odbierająca musi poprosić o uprawnienie poniżej:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

Możesz określić istniejące uprawnienia systemowe, takie jak BLUETOOTH_CONNECT lub zdefiniuj uprawnienia niestandardowe za pomocą Element <permission>. Dla: informacji na temat uprawnień i bezpieczeństwa można znaleźć w sekcji System Uprawnienia.

Odbieranie z uprawnieniami

Jeśli określisz parametr uprawnień podczas rejestrowania odbiornika (z registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) lub w Tag <receiver> w manifestu), to jedynie nadawcy, którzy poprosili o to uprawnienie Tag <uses-permission> w pliku manifestu (a później otrzymał odpowiednie uprawnienia, jeśli niebezpieczny) może wysłać intencję do odbiorcy.

Załóżmy na przykład, że aplikacja odbierająca ma zadeklarowany w pliku manifestu odbiornik jako poniżej:

<receiver android:name=".MyBroadcastReceiver"
          android:permission="android.permission.BLUETOOTH_CONNECT">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_FOUND"/>
    </intent-filter>
</receiver>

Aplikacja odbierająca ma też odbiornik zarejestrowany kontekstowo, jak pokazano poniżej:

Kotlin

var filter = IntentFilter(Intent.ACTION_FOUND)
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )

Java

IntentFilter filter = new IntentFilter(Intent.ACTION_FOUND);
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null );

Następnie, aby móc wysyłać komunikaty do tych odbiorców, aplikacja wysyłająca musi poproś o pozwolenie, jak pokazano poniżej:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

Kwestie bezpieczeństwa i sprawdzone metody

Poniżej przedstawiamy kilka kwestii związanych z bezpieczeństwem oraz sprawdzone metody wysyłania i odbieranie komunikatów:

  • Jeśli wiele aplikacji zarejestrowało się, by odbierać tę samą transmisję na swoim pliku manifestu, może on powodować uruchamianie przez system wielu aplikacji, powodując mają duży wpływ na wydajność urządzenia i wrażenia użytkownika. Aby unikać lepiej korzystać z rejestracji kontekstu zamiast deklaracji w pliku manifestu. Czasami system Android wymusza użycie odbiorców. Na przykład dostarczana jest transmisja CONNECTIVITY_ACTION tylko do odbiorców zarejestrowanych na podstawie kontekstu.

  • Nie rozpowszechniaj informacji poufnych, nie mając intencji. informacje mogą być odczytywane przez każdą aplikację rejestrującą się, aby odbierać transmisję. Istnieją 3 sposoby określania, kto może odbierać Twoje komunikaty:

    • Uprawnienia możesz określić podczas wysyłania transmisji.
    • W Androidzie 4.0 i nowszych możesz określić package z setPackage(String) podczas wysyłania transmisję. System ogranicza transmisję do zestawu aplikacji, które pasujące do pakietu.
  • Gdy zarejestrujesz odbiorcę, każda aplikacja może wysyłać potencjalnie szkodliwe aplikacje do odbiornika. Istnieje kilka sposobów na ograniczenie komunikaty wysyłane przez aplikację:

    • Uprawnienia możesz określić podczas rejestrowania odbiornika.
    • W przypadku odbiorców zadeklarowanych w pliku manifestu możesz ustawić android:exported ma wartość „false”; w pliku manifestu. Odbiorca nie otrzymuje komunikaty ze źródeł spoza aplikacji.
  • Przestrzeń nazw działań związanych z transmisją jest globalna. Upewnij się, że nazwy działań a inne ciągi są zapisane w Twojej przestrzeni nazw. W przeciwnym razie możesz niechętnie kolidują z innymi aplikacjami.

  • Ponieważ metoda onReceive(Context, Intent) odbiorcy działa na w wątku głównym, powinien zostać wykonany i szybko zwrócony. W razie potrzeby długotrwała praca, uważaj na pojawianie się wątków i usług w tle, ponieważ system może zakończyć cały proces onReceive() za możliwość zwrotu. Więcej informacji znajdziesz w sekcji Wpływ na proces stan Aby przeprowadzić długotrwałą pracę, Rekomendacja:

    • Dzwonię pod goAsync() w onReceive() odbiorcy i przekazywanie źródła BroadcastReceiver.PendingResult do wątku w tle. Dzięki temu transmisja pozostanie aktywna, gdy wrócisz z: onReceive(). Jednak nawet przy takim podejściu system wymaga, aby użytkownik transmitowanie bardzo szybko (poniżej 10 sekund). Pozwala przenosić w innym wątku, aby uniknąć zakłóceń w wątku głównym.
    • Planujesz zadanie za pomocą JobScheduler. Więcej Więcej informacji na ten temat można znaleźć w artykule Intelligent Job (Inteligentne zadanie) Harmonogram.
  • Nie uruchamiaj działań od odbiorników, ponieważ jest drażniący; zwłaszcza wtedy, gdy jest więcej niż jeden odbiornik. Zamiast tego rozważ wyświetlanie powiadomienia.