Intencje i filtry intencji

Intent to obiekt wiadomości, którego można użyć do wysłania żądania działania z innego komponentu aplikacji. Chociaż intencje ułatwiają komunikację między komponentami na kilka sposobów, są 3 główne przypadki użycia:

  • Rozpoczynanie aktywności

    Element Activity reprezentuje jeden ekran w aplikacji. Możesz rozpocząć nowe wystąpienie obiektu Activity, przekazując Intent do startActivity(). Intent opisuje działanie, które ma zostać uruchomione, i przenosi potrzebne dane.

    Jeśli chcesz otrzymać wynik aktywności po jej zakończeniu, wywołaj startActivityForResult(). Aktywność otrzymuje wynik jako osobny obiekt Intent w wywołaniu zwrotnym onActivityResult() aktywności. Więcej informacji znajdziesz w przewodniku po ćwiczeniach.

  • Uruchamianie usługi

    Service to komponent, który wykonuje operacje w tle bez interfejsu użytkownika. W Androidzie 5.0 (poziom interfejsu API 21) i nowszych możesz uruchamiać usługę za pomocą JobScheduler. Więcej informacji o JobScheduler znajdziesz tutaj: API-reference documentation.

    W przypadku wersji starszych niż 5.0 (poziom interfejsu API 21) możesz uruchomić usługę za pomocą metod klasy Service. Aby uruchomić usługę do wykonania operacji jednorazowej (takiej jak pobranie pliku), możesz przekazać żądanie Intent do startService(). Intent określa uruchomienie usługi i przenoszenie wszelkich niezbędnych danych.

    Jeśli usługa została zaprojektowana z myślą o interfejsie klient-serwer, możesz ją powiązać z innym komponentem, przekazując Intent do bindService(). Więcej informacji znajdziesz w przewodniku Usługi.

  • Przesyłanie transmisji

    Komunikat to wiadomość, którą może odebrać każda aplikacja. System dostarcza różne transmisje zdarzeń systemowych, np. podczas uruchamiania systemu lub rozpoczęcia ładowania urządzenia. Komunikat możesz przekazać do innych aplikacji, przekazując kod Intent do interfejsu sendBroadcast() lub sendOrderedBroadcast().

W dalszej części tej strony wyjaśniamy, jak działają intencje i jak z nich korzystać. Więcej informacji znajdziesz w sekcjach Korzystanie z innych aplikacji i Udostępnianie treści.

Typy intencji

Istnieją 2 rodzaje intencji:

  • Intencje jawne określają, który komponent aplikacji spełni intencję, określając pełną właściwość ComponentName. Zwykle używasz wyraźnej intencji uruchomienia komponentu w aplikacji, ponieważ znasz nazwę klasy działania lub usługi, które chcesz uruchomić. Możesz na przykład rozpocząć nową aktywność w aplikacji w odpowiedzi na działanie użytkownika lub uruchomić usługę, która pobiera plik w tle.
  • Intencje niejawne nie wskazują konkretnego komponentu, ale deklarują ogólne działanie, które umożliwia komponentowi z innej aplikacji. Jeśli na przykład chcesz pokazać użytkownikowi lokalizację na mapie, możesz za pomocą niejawnej prośby o wyświetlenie na mapie określonej lokalizacji w innej aplikacji, która spełnia te wymagania.

Rysunek 1 przedstawia sposób użycia intencji podczas uruchamiania działania. Gdy obiekt Intent jawnie nadaje nazwę konkretnemu komponentowi aktywności, system natychmiast go uruchamia.

Rysunek 1. W jaki sposób intencja pośrednia jest przekazywana przez system w celu uruchomienia innego działania: [1] Działanie A tworzy element Intent z opisem działania i przekazuje go do startActivity(). [2] System Android wyszukuje we wszystkich aplikacjach filtr intencji pasujący do intencji. Po znalezieniu dopasowania [3] system uruchamia pasujące działanie (Działanie B), wywołując metodę onCreate() i przekazując mu Intent.

Jeśli korzystasz z intencji niejawnej, system Android znajduje odpowiedni komponent i zaczyna przez porównanie zawartości intencji z filtrami intencji zadeklarowanymi w pliku manifestu innych aplikacji na urządzeniu. Jeśli intencja pasuje do filtra intencji, system uruchomi dany komponent i dostarczy go obiektowi Intent. Jeśli zgodnych jest wiele filtrów intencji, system wyświetla okno, w którym użytkownik może wybrać aplikację.

Filtr intencji to wyrażenie w pliku manifestu aplikacji, które określa typ intencji, które komponent chce otrzymywać. Na przykład, jeśli zadeklarujesz filtr intencji dla danej aktywności, inne aplikacje będą mogły ją bezpośrednio uruchamiać z określonym zamiarem. Jeśli nie zadeklarujesz żadnych filtrów intencji dla danej aktywności, może ona zostać uruchomiona tylko z wyraźną intencją.

Uwaga: aby mieć pewność, że aplikacja jest bezpieczna, podczas uruchamiania Service zawsze używaj wyraźnej intencji i nie deklaruj filtrów intencji dla swoich usług. Korzystanie z niejawnej intencji uruchomienia usługi stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa zareaguje na intencję, a użytkownik nie będzie w stanie zobaczyć, która usługa się uruchamia. Począwszy od Androida 5.0 (poziom interfejsu API 21) system zgłasza wyjątek, jeśli wywołujesz bindService() z intencją niejawną.

Tworzenie intencji

Obiekt Intent zawiera informacje używane przez system Android do określenia, którego komponentu użyć (np. dokładna nazwa komponentu lub kategoria komponentu, który powinien otrzymać intencję), oraz informacje, których komponent adresata używa do prawidłowego wykonania działania (np. działanie i dane, na których ma wykonać działanie).

Intent zawiera te podstawowe informacje:

Nazwa komponentu
Nazwa komponentu, od którego ma się rozpocząć.

Ten krok jest opcjonalny, ale to kluczowa informacja tworząca intencję wyraźna. Oznacza to, że intencja powinna być dostarczana tylko do komponentu aplikacji zdefiniowanego przez nazwę komponentu. Bez nazwy komponentu intencja jest ukryta, a system decyduje, który z nich powinien ją otrzymać, na podstawie innych informacji o intencji (takich jak działanie, dane i kategoria (jak opisano poniżej). Jeśli chcesz uruchomić konkretny komponent w aplikacji, musisz podać jego nazwę.

Uwaga: rozpoczynając pisanie Service, zawsze należy podać nazwę komponentu. W przeciwnym razie nie będziesz mieć pewności, która usługa odpowie na intencję, a użytkownik nie będzie mógł zobaczyć, która usługa się uruchomi.

To pole obiektu Intent jest obiektem ComponentName, który możesz określić za pomocą pełnej i jednoznacznej nazwy klasy komponentu docelowego, w tym nazwy pakietu aplikacji, np. com.example.ExampleActivity. Nazwę komponentu możesz ustawić za pomocą setComponent(), setClass(), setClassName() lub konstruktora Intent.

Działanie
Ciąg tekstowy określający działanie ogólne do wykonania (np. wyświetl lub wybierz).

W przypadku intencji związanych z transmisją jest to działanie, które miało miejsce i jest zgłaszane. Działanie w dużej mierze decyduje o strukturze reszty intencji, a zwłaszcza informacji zawartych w danych i dodatkach.

Możesz określić własne działania do wykorzystania przez intencje w aplikacji (lub do wykorzystania przez inne aplikacje do wywoływania komponentów w aplikacji), ale zazwyczaj określa się stałe działania zdefiniowane przez klasę Intent lub inne klasy platformy. Oto kilka typowych działań związanych z rozpoczynaniem działania:

ACTION_VIEW
Możesz użyć tego działania w intencji startActivity(), jeśli masz informacje, które aktywność może wyświetlić użytkownikowi, np. zdjęcie do wyświetlenia w aplikacji z galerią lub adres do wyświetlenia w aplikacji z mapami.
ACTION_SEND
Intencje są też nazywane intencją udostępniania. Używaj jej w intencji startActivity(), gdy masz dane, które użytkownik może udostępnić w innej aplikacji, np. w aplikacji do poczty e-mail lub aplikacji do udostępniania społecznościowego.

Więcej stałych definiujących działania ogólne znajdziesz w dokumentacji klasy Intent. Inne działania są definiowane w innym miejscu w ramach platformy Androida, np. w Settings w przypadku działań, które otwierają określone ekrany w aplikacji Ustawienia w systemie.

Działanie intencji możesz określić za pomocą konstruktora Intent lub setAction().

Jeśli definiujesz własne działania, pamiętaj, aby dodać prefiks nazwy pakietu aplikacji, jak pokazano w tym przykładzie:

Kotlin

const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Dane
Identyfikator URI (obiekt Uri) odwołujący się do danych, na których ma zostać wykonana czynność, lub typ MIME tych danych. Typ dostarczanych danych zależy zazwyczaj od działania intencji. Jeśli na przykład działaniem jest ACTION_EDIT, dane powinny zawierać identyfikator URI dokumentu do edycji.

Podczas tworzenia intencji często trzeba podać typ danych (typ MIME) oprócz identyfikatora URI. Na przykład działanie, które może wyświetlać obrazy, prawdopodobnie nie pozwoli na odtworzenie pliku audio, chociaż formaty URI mogą być podobne. Określenie typu MIME danych pomaga systemowi Android znaleźć komponent, który najlepiej spełnia Twoją intencję. Typ MIME można jednak czasem określić na podstawie identyfikatora URI, zwłaszcza gdy dane są związane z identyfikatorem URI content:. Identyfikator URI content: wskazuje, że dane znajdują się na urządzeniu i są kontrolowane przez ContentProvider, co sprawia, że typ MIME danych jest widoczny dla systemu.

Aby ustawić tylko identyfikator URI danych, wywołaj setData(). Aby ustawić tylko typ MIME, wywołaj setType(). W razie potrzeby możesz ustawić obie te wartości za pomocą funkcji setDataAndType().

Uwaga: jeśli chcesz ustawić zarówno identyfikator URI, jak i typ MIME, nie wywołaj funkcji setData() i setType(), ponieważ obie te wartości zerują wartości drugiego adresu. Do ustawiania zarówno identyfikatora URI, jak i typu MIME zawsze używaj polecenia setDataAndType().

Category
Ciąg tekstowy zawierający dodatkowe informacje o rodzaju komponentu, który powinien obsługiwać intencję. W intencji można umieścić dowolną liczbę opisów kategorii, ale większość intencji nie wymaga kategorii. Oto kilka typowych kategorii:
CATEGORY_BROWSABLE
Docelowa aktywność pozwala się uruchomić przez przeglądarkę internetową w celu wyświetlenia danych, do których odwołuje się link, na przykład obrazu lub e-maila.
CATEGORY_LAUNCHER
Aktywność to początkowa aktywność związana z zadaniem i jest wyświetlana w menu z aplikacjami w systemie.

Pełną listę kategorii znajdziesz w opisie klasy Intent.

Kategorię możesz określić za pomocą parametru addCategory().

Wymienione powyżej właściwości (nazwa komponentu, działanie, dane i kategoria) reprezentują cechy definiujące intencję. Odczytując te właściwości, Android może zdecydować, który komponent aplikacji powinien uruchomić. Intencja może jednak zawierać dodatkowe informacje, które nie mają wpływu na sposób jego interpretacji w komponencie aplikacji. Intencja może też zawierać takie informacje:

Dodatki
Pary klucz-wartość zawierające dodatkowe informacje wymagane do wykonania żądanego działania. Tak jak niektóre działania wykorzystują określone rodzaje identyfikatorów URI danych, niektóre z nich wymagają też specjalnych elementów dodatkowych.

Możesz dodawać dodatkowe dane za pomocą różnych metod putExtra(), z których każda akceptuje 2 parametry: nazwę klucza i wartość. Możesz też utworzyć obiekt Bundle ze wszystkimi dodatkowymi danymi, a potem wstawić Bundle w Intent przy użyciu polecenia putExtras().

Podczas tworzenia intencji wysłania e-maila za pomocą ACTION_SEND możesz na przykład określić adresata to za pomocą klucza EXTRA_EMAIL, a temat – kluczem EXTRA_SUBJECT.

Klasa Intent określa wiele stałych EXTRA_* na potrzeby standardowych typów danych. Jeśli musisz zadeklarować własne dodatkowe klucze (na potrzeby intencji, które odbiera aplikacja), pamiętaj, aby dodać przedrostek nazwy pakietu aplikacji, tak jak w tym przykładzie:

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

Uwaga: nie używaj danych Parcelable ani Serializable do wysyłania intencji, którą spodziewasz się otrzymać inna aplikacja. Jeśli aplikacja próbuje uzyskać dostęp do danych w obiekcie Bundle, ale nie ma dostępu do klasy spakowanej lub zserializowanej, system zgłasza RuntimeException.

Flagi
Flagi zdefiniowane są w klasie Intent, które funkcjonują jako metadane intencji. Flagi mogą informować system Android, jak uruchomić działanie (np. do którego zadania powinna ona należeć) i jak postępować z nią po jej uruchomieniu (np. czy należy ona do listy ostatnich aktywności).

Więcej informacji znajdziesz w opisie metody setFlags().

Przykład jednoznacznej intencji

Intencja bezpośrednia to taka, za pomocą której uruchamiasz określony komponent aplikacji, np. konkretną aktywność lub usługę. Aby utworzyć intencję bezpośrednią, podaj nazwę komponentu obiektu Intent – pozostałe właściwości intencji są opcjonalne.

Jeśli np. utworzysz w aplikacji usługę o nazwie DownloadService, która służy do pobierania pliku z internetu, możesz zacząć ją od tego kodu:

Kotlin

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
val downloadIntent = Intent(this, DownloadService::class.java).apply {
    data = Uri.parse(fileUrl)
}
startService(downloadIntent)

Java

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

Konstruktor Intent(Context, Class) dostarcza aplikację Context, a komponent – obiekt Class. Dlatego ta intencja jawnie uruchamia klasę DownloadService w aplikacji.

Więcej informacji o tworzeniu i uruchamianiu usługi znajdziesz w przewodniku Usługi.

Przykład niejawnej intencji

Intencja niejawna określa działanie, które może wywołać dowolną aplikację na urządzeniu, która może je wykonać. Intencje niejawne są przydatne, gdy aplikacja nie może wykonać danego działania, ale inne aplikacje prawdopodobnie mają taką możliwość i chcesz, aby użytkownik sam wybierał aplikację.

Jeśli na przykład masz treści, które chcesz udostępnić innym osobom, utwórz intencję za pomocą działania ACTION_SEND i dodaj elementy określające treść do udostępnienia. Jeśli wywołujesz startActivity() w związku z tym zamiarem, użytkownik może wybrać aplikację, za pomocą której chce udostępnić treści.

Kotlin

// Create the text message with a string.
val sendIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(Intent.EXTRA_TEXT, textMessage)
    type = "text/plain"
}

// Try to invoke the intent.
try {
    startActivity(sendIntent)
} catch (e: ActivityNotFoundException) {
    // Define what your app should do if no activity can handle the intent.
}

Java

// Create the text message with a string.
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// Try to invoke the intent.
try {
    startActivity(sendIntent);
} catch (ActivityNotFoundException e) {
    // Define what your app should do if no activity can handle the intent.
}

Po wywołaniu funkcji startActivity() system sprawdza wszystkie zainstalowane aplikacje, aby określić, które z nich mogą obsłużyć tego rodzaju intencję (zamiar z działaniem ACTION_SEND, który przenosi dane typu „text/plain”). Jeśli tylko jedna aplikacja może obsłużyć daną aplikację, zostanie ona otwarta natychmiast i zgodzi się na jej działanie. Jeśli żadna inna aplikacja nie jest w stanie go obsłużyć, może zarejestrować ActivityNotFoundException, który występuje. Jeśli wiele działań zaakceptuje intencję, system wyświetli okno, takie jak na Rysunku 2, aby użytkownik mógł wybrać aplikację.

Więcej informacji o uruchamianiu innych aplikacji znajdziesz też w przewodniku o kierowaniu użytkownika do innej aplikacji.

Rysunek 2. Okno wyboru.

Wymuszanie wyboru aplikacji

Jeśli na Twoją pośrednią intencję odpowiada więcej niż jedna aplikacja, użytkownik może wybrać aplikację, której chce użyć, i ustawić ją jako domyślną dla danego działania. Możliwość wyboru wartości domyślnej jest przydatna, gdy użytkownik prawdopodobnie chce używać tej samej aplikacji za każdym razem, np. przy otwieraniu strony internetowej (użytkownicy często preferują tylko jedną przeglądarkę).

Jeśli jednak wiele aplikacji może odpowiadać na intencję, a użytkownik może za każdym razem używać innej aplikacji, wyraźnie pokaż okno wyboru. Okno wyboru zawiera prośbę o wybranie aplikacji, której ma używać działanie (użytkownik nie może wybrać domyślnej aplikacji dla tego działania). Jeśli na przykład aplikacja wykonuje „udostępnianie” przy użyciu działania ACTION_SEND, użytkownicy mogą zechcieć udostępniać treści za pomocą innej aplikacji zależnie od ich obecnej sytuacji, dlatego zawsze korzystaj z okna wyboru, jak pokazano na Rysunku 2.

Aby pokazać selektor, utwórz dokument Intent za pomocą metody createChooser() i przekaż go do startActivity(), jak pokazano w poniższym przykładzie. W tym przykładzie wyświetla się okno z listą aplikacji, które reagują na intencję przekazaną do metody createChooser() i używają podanego tekstu jako tytułu okna.

Kotlin

val sendIntent = Intent(Intent.ACTION_SEND)
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
val title: String = resources.getString(R.string.chooser_title)
// Create intent to show the chooser dialog
val chooser: Intent = Intent.createChooser(sendIntent, title)

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(packageManager) != null) {
    startActivity(chooser)
}

Java

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

Wykrywanie uruchamiania niebezpiecznych intencji

Aplikacja może uruchamiać intencje dotyczące przechodzenia między komponentami w samej aplikacji lub wykonywania działania w imieniu innej aplikacji. Aby zwiększyć bezpieczeństwo platformy, Android 12 (poziom interfejsu API 31) i nowszy udostępnia funkcję debugowania, która ostrzega, jeśli aplikacja wykonuje niebezpieczne uruchomienie intencji. Aplikacja może na przykład wykonywać niebezpieczne uruchamianie intencji zagnieżdżonej, która jest przekazywana jako dodatkowa intencja w innej intencji.

Jeśli aplikacja wykona obie te czynności, system wykryje uruchomienie niebezpiecznej intencji i dochodzi do naruszenia StrictMode:

  1. Aplikacja wyodrębnia zagnieżdżoną intencję od dodatków w dostarczonej intencji.
  2. Aplikacja natychmiast uruchamia komponent aplikacji, używając tej zagnieżdżonej intencji, np. przekazuje intencję do startActivity(), startService() lub bindService().

Więcej informacji o tym, jak rozpoznać taką sytuację i wprowadzić zmiany w aplikacji, znajdziesz w poście na blogu na temat intencji na Androidzie Nesting Intents w serwisie Medium.

Sprawdzanie, czy nie uruchomiono niebezpiecznych intencji

Aby sprawdzić, czy w aplikacji nie są uruchamiane niebezpieczne intencje, wywołaj detectUnsafeIntentLaunch() podczas konfigurowania elementu VmPolicy, jak pokazano w tym fragmencie kodu. Jeśli aplikacja wykryje naruszenie zasad StrictMode, możesz zatrzymać wykonywanie aplikacji, aby chronić potencjalnie poufne informacje.

Kotlin

fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build())
}

Java

protected void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build());
}

Odpowiedzialne korzystanie z intencji

Aby zminimalizować ryzyko uruchomienia niebezpiecznej intencji i naruszenia zasad StrictMode, postępuj zgodnie z tymi sprawdzonymi metodami.

Skopiuj w obrębie intencji tylko niezbędne elementy dodatkowe, a następnie przeprowadź proces oczyszczania i weryfikacji. Aplikacja może kopiować dodatki z jednej intencji do drugiej, co służy do uruchamiania nowego komponentu. Dzieje się tak, gdy aplikacja wywołuje metodę putExtras(Intent) lub putExtras(Bundle). Jeśli aplikacja wykonuje jedną z tych operacji, skopiuj tylko te dodatkowe, których oczekuje komponent odbierający. Jeśli druga intencja (która otrzymuje kopię) uruchomi komponent, który nie jest wyeksportowany, oczyść i zweryfikuj dodatki, zanim skopiujesz je do intencji uruchamiającej komponent.

Nie eksportuj komponentów aplikacji bez potrzeby. Jeśli np. zamierzasz uruchamiać komponent aplikacji za pomocą wewnętrznej intencji zagnieżdżonej, ustaw atrybut android:exported tego komponentu na false.

Użyj obiektu PendingIntent zamiast intencji zagnieżdżonej. Dzięki temu, gdy inna aplikacja wyodrębnia PendingIntent z zawartego w niej komponentu Intent, może ona uruchomić PendingIntent, korzystając z tożsamości Twojej aplikacji. Dzięki tej konfiguracji druga aplikacja może bezpiecznie uruchamiać w Twojej aplikacji dowolny komponent, w tym niewyeksportowany.

Diagram na rys. 2 pokazuje, w jaki sposób system przekazuje elementy sterujące aplikacją (klienta) do innej aplikacji (usługi) i z powrotem do aplikacji:

  1. Aplikacja tworzy intencję, która wywołuje działanie w innej aplikacji. W tej intencji dodajesz jako dodatkowy obiekt PendingIntent. Ta intencja oczekująca będzie wywoływać komponent w aplikacji. Ten komponent nie jest eksportowany.
  2. Po otrzymaniu intencji aplikacji druga aplikacja wyodrębnia zagnieżdżony obiekt PendingIntent.
  3. Druga aplikacja wywołuje metodę send() w obiekcie PendingIntent.
  4. Po przekazaniu kontroli z powrotem do aplikacji system wywołuje oczekującą intencję, korzystając z kontekstu aplikacji.

Rysunek 2. Diagram komunikacji między aplikacjami podczas korzystania z zagnieżdżonej intencji oczekującej.

Odbieranie intencji ogólnej

Aby określić, jakie intencje niejawne może otrzymywać Twoja aplikacja, zadeklaruj co najmniej 1 filtr intencji dla każdego komponentu aplikacji za pomocą elementu <intent-filter> w pliku manifestu. Każdy filtr intencji określa typ akceptowanych intencji na podstawie działania, danych i kategorii intencji. System realizuje niejawną intencję w komponencie aplikacji tylko wtedy, gdy może ona przejść przez jeden z filtrów intencji.

Uwaga: intencja bezpośrednia jest zawsze dostarczana do celu, niezależnie od filtrów intencji deklarowanych przez komponent.

Komponent aplikacji powinien zadeklarować osobne filtry dla każdego unikalnego zadania, które może wykonywać. Na przykład jedna aktywność w aplikacji z galerią obrazów może mieć 2 filtry: jeden do wyświetlania obrazu i drugi do edycji obrazu. Po rozpoczęciu aktywności sprawdza obiekt Intent i na podstawie informacji w elemencie Intent określa, jak ma działać (np. w celu wyświetlenia opcji edytora).

Każdy filtr intencji jest definiowany przez element <intent-filter> w pliku manifestu aplikacji umieszczony w odpowiednim komponencie aplikacji (np. elemencie <activity>).

W każdym komponencie aplikacji, który zawiera element <intent-filter>, ustaw wartość android:exported. Ten atrybut wskazuje, czy komponent aplikacji jest dostępny dla innych aplikacji. W niektórych sytuacjach, np. w przypadku działań, których filtry intencji obejmują kategorię LAUNCHER, warto ustawić wartość tego atrybutu na true. W przeciwnym razie bezpieczniej jest ustawić ten atrybut na false.

Ostrzeżenie: jeśli aktywność, usługa lub odbiornik w aplikacji używa filtrów intencji i nie ustawi wyraźnie wartości dla parametru android:exported, aplikacji nie można zainstalować na urządzeniu z Androidem 12 lub nowszym.

W obrębie <intent-filter> możesz określić typ intencji do zaakceptowania, korzystając z co najmniej 1 z tych 3 elementów:

<action>
Deklaruje zaakceptowane działanie intencji w atrybucie name. Wartość musi być dosłowną wartością ciągu działania, a nie stałą klasy.
<data>
Deklaruje typ akceptowanych danych, używając co najmniej jednego atrybutu określającego różne aspekty identyfikatora URI danych (scheme, host, port, path) i typu MIME.
<category>
Deklaruje zaakceptowaną kategorię intencji w atrybucie name. Wartość musi być dosłowną wartością ciągu działania, a nie stałą klasy.

Uwaga: aby otrzymywać intencje niejawne, musisz uwzględnić kategorię CATEGORY_DEFAULT w filtrze intencji. Metody startActivity() i startActivityForResult() traktują wszystkie intencje tak, jakby zadeklarowały kategorię CATEGORY_DEFAULT. Jeśli nie zadeklarujesz tej kategorii w filtrze intencji, do Twojej aktywności nie będą trafiały żadne niejawne intencje.

Na przykład oto deklaracja aktywności z filtrem intencji, która odbiera intencję ACTION_SEND, gdy typem danych jest tekst:

<activity android:name="ShareActivity" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

Możesz utworzyć filtr, który zawiera więcej niż 1 wystąpienie <action>, <data> lub <category>. Jeśli to zrobisz, musisz upewnić się, że komponent obsługuje wszystkie kombinacje tych elementów filtra.

Jeśli chcesz obsługiwać wiele rodzajów intencji, ale tylko w przypadku określonych kombinacji działań, danych i typów kategorii, musisz utworzyć kilka filtrów intencji.

Intencja niejawna jest testowana pod kątem filtra przez porównanie intencji z każdym z 3 elementów. Aby intencja mogła zostać wyświetlona do komponentu, musi przejść wszystkie 3 testy. Jeśli nie uda się dopasować któregoś z nich, Android nie dostarczy intencji do komponentu. Ponieważ jednak komponent może mieć wiele filtrów intencji, intencja, która nie przejdzie przez jeden z tych filtrów, może przedostać się przez inny filtr. Więcej informacji o sposobie, w jaki system rozpoznaje intencje, znajdziesz w poniższej sekcji o rozwiązaniu intencji.

Uwaga: użycie filtra intencji nie jest bezpiecznym sposobem, aby uniemożliwić innym aplikacjom uruchamianie komponentów. Choć filtry intencji ograniczają reagowanie komponentu tylko na określone rodzaje niejawnych intencji, to inna aplikacja może uruchomić komponent aplikacji, korzystając z intencji jawnej, jeśli to deweloper określi nazwy komponentów. Jeśli zależy Ci, aby tylko Twoja aplikacja mogła uruchamiać jeden z komponentów, nie deklaruj filtrów intencji w pliku manifestu. Zamiast tego ustaw wartość atrybutu exported na "false" dla tego komponentu.

Podobnie, aby uniknąć przypadkowego uruchomienia polecenia Service innej aplikacji, zawsze używaj wyraźnej intencji uruchomienia własnej usługi.

Uwaga: w przypadku wszystkich aktywności filtry intencji musisz zadeklarować w pliku manifestu. Filtry odbiorników można jednak rejestrować dynamicznie, wywołując metodę registerReceiver(). Następnie możesz wyrejestrować odbiorcę za pomocą unregisterReceiver(). Dzięki temu aplikacja może nasłuchiwać określonych komunikatów tylko przez określony czas, gdy jest uruchomiona.

Przykładowe filtry

Aby zademonstrować niektóre działania filtra intencji, oto przykład z pliku manifestu aplikacji do udostępniania społecznościowego:

<activity android:name="MainActivity" android:exported="true">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity" android:exported="false">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>

Pierwszym działaniem (MainActivity) jest główny punkt wejścia aplikacji – działanie uruchamiane, gdy użytkownik po raz pierwszy uruchomi aplikację z ikoną programu uruchamiającego:

  • Działanie ACTION_MAIN wskazuje, że jest to główny punkt wejścia i nie oczekuje żadnych danych intencji.
  • Kategoria CATEGORY_LAUNCHER oznacza, że ikona tej aktywności powinna znajdować się w systemie uruchamiającym aplikacje. Jeśli element <activity> nie określa ikony z wartością icon, system użyje ikony z elementu <application>.

Aby aktywność była widoczna w Menu z aplikacjami, muszą one być sparowane ze sobą.

Druga czynność, ShareActivity, ma ułatwiać udostępnianie tekstu i multimediów. Użytkownicy mogą otworzyć tę aktywność, przechodząc do niej z poziomu MainActivity, ale mogą też wpisywać ShareActivity bezpośrednio z innej aplikacji, która wysyła niejawną intencję pasującą do jednego z 2 filtrów intencji.

Uwaga: typ MIME application/vnd.google.panorama360+jpg to specjalny typ danych określający zdjęcia panoramiczne, które obsługują interfejsy API Google Panorama.

Dopasuj intencje do filtrów intencji innych aplikacji

Jeśli inna aplikacja jest kierowana na Androida 13 (poziom interfejsu API 33) lub nowszego, może obsługiwać intencje Twojej aplikacji tylko wtedy, gdy ta intencja odpowiada działaniom i kategoriom elementu <intent-filter> w tej aplikacji. Jeśli system nie znajdzie dopasowania, zgłasza ActivityNotFoundException. Aplikacja wysyłająca musi obsługiwać ten wyjątek.

Podobnie jeśli zaktualizujesz aplikację w taki sposób, aby była kierowana na Androida w wersji 13 lub nowszej, wszystkie intencje pochodzące z aplikacji zewnętrznych będą dostarczane do wyeksportowanego komponentu aplikacji tylko wtedy, gdy ta intencja odpowiada działaniom i kategoriom elementu <intent-filter> deklarowanych przez aplikację. To zachowanie występuje niezależnie od docelowej wersji pakietu SDK aplikacji wysyłającej.

Dopasowywanie intencji nie jest wymuszane w tych przypadkach:

  • Intencje dostarczone do komponentów, które nie deklarują żadnych filtrów intencji.
  • Intencje pochodzące z tej samej aplikacji.
  • intencje pochodzące z systemu, czyli intencje wysyłane z „identyfikatora UID systemu” (uid=1000). Aplikacje systemowe to m.in. system_server i aplikacje z ustawieniem android:sharedUserId na android.uid.system.
  • Intencje pochodzące z katalogu głównego

Dowiedz się więcej o dopasowywaniu zamiarów.

Korzystanie z intencji oczekującej

Obiekt PendingIntent otacza obiekt Intent. Głównym celem PendingIntent jest udzielanie zagranicznym aplikacji pozwolenia na używanie zawartego w niej elementu Intent w taki sposób, jakby były wykonywane przez Twój proces aplikacji.

Główne przypadki użycia intencji oczekującej to:

  • Zadeklarowanie intencji do wykonania, gdy użytkownik wykona działanie z użyciem powiadomienia (NotificationManager systemu Android wykonuje polecenie Intent).
  • Zadeklarowanie intencji do wykonania, gdy użytkownik wykona działanie za pomocą Twojego widżetu aplikacji (aplikacja na ekranie głównym uruchamia Intent).
  • Zadeklarowanie intencji do wykonania o określonej godzinie w przyszłości (AlarmManager systemu Android wykonuje polecenie Intent).

Każdy obiekt Intent jest przeznaczony do obsługi przez konkretny typ komponentu aplikacji (Activity, Service lub BroadcastReceiver), więc też PendingIntent należy utworzyć z tym samym uwzględnieniem. Gdy używasz intencji oczekującej, aplikacja nie wykonuje intencji za pomocą wywołania, np. startActivity(). Zamiast tego musisz zadeklarować odpowiedni typ komponentu podczas tworzenia komponentu PendingIntent, wywołując odpowiednią metodę twórcy:

Jeśli Twoja aplikacja nie otrzymuje oczekujących intencji z innych aplikacji, powyższe metody tworzenia PendingIntent to prawdopodobnie jedyne metody PendingIntent, których będziesz potrzebować.

Każda metoda pobiera bieżącą aplikację Context, element Intent, który chcesz zapakować, i co najmniej 1 flagę określającą sposób użycia intencji (np. czy intencję można użyć więcej niż raz).

Więcej informacji o używaniu intencji oczekujących znajdziesz w dokumentacji poszczególnych przypadków użycia, np. w przewodnikach po interfejsie API Powiadomienia i Widżetów aplikacji.

Określ zmienność

Jeśli Twoja aplikacja jest kierowana na Androida 12 lub nowszego, musisz określić zmienność każdego tworzonego przez nią obiektu PendingIntent. Aby zadeklarować, że dany obiekt PendingIntent jest zmienny lub stały, użyj odpowiednio flagi PendingIntent.FLAG_MUTABLE lub PendingIntent.FLAG_IMMUTABLE.

Jeśli aplikacja spróbuje utworzyć obiekt PendingIntent bez ustawienia flagi zmienności, system zgłosi IllegalArgumentException, a w narzędziu Logcat pojawi się następujący komunikat:

PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.

Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.

W miarę możliwości twórz stałe oczekujące intencje

W większości przypadków aplikacja powinna tworzyć stałe obiekty PendingIntent, jak pokazano w tym fragmencie kodu. Jeśli obiektu PendingIntent nie można zmienić, inne aplikacje nie mogą modyfikować intencji, aby dostosować wynik jej wywołania.

Kotlin

val pendingIntent = PendingIntent.getActivity(applicationContext,
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE)

Java

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE);

Jednak w niektórych przypadkach użycia wymagane są jednak zmienne obiekty PendingIntent:

  • Obsługuje działania związane z odpowiedzią w powiadomieniach bezpośrednio. Bezpośrednia odpowiedź wymaga zmiany danych klipu w obiekcie PendingIntent, który jest z nią powiązany. Zwykle o taką zmianę prosisz, przekazując do metody fillIn() flagę FILL_IN_CLIP_DATA.
  • Wiążę powiadomienia z platformą Android Auto za pomocą wystąpień CarAppExtender.
  • Umieszczanie wątków w dymkach przy użyciu wystąpień PendingIntent. Zmienny obiekt PendingIntent umożliwia systemowi stosowanie prawidłowych flag, np. FLAG_ACTIVITY_MULTIPLE_TASK i FLAG_ACTIVITY_NEW_DOCUMENT.
  • Żądanie informacji o lokalizacji urządzenia przez wywołanie requestLocationUpdates() lub podobne interfejsy API. zmienny obiekt PendingIntent pozwala systemowi dodawać elementy dodatkowe, które reprezentują zdarzenia cyklu życia lokalizacji. Zdarzenia te obejmują zmianę lokalizacji oraz udostępnienie dostawcy.
  • Planowanie alarmów w aplikacji AlarmManager. Zmienny obiekt PendingIntent pozwala systemowi dodać dodatkową intencję EXTRA_ALARM_COUNT. Ta wartość określa, ile razy został aktywowany powtarzający się alarm. Zawierając ten dodatkowy element, intencja może dokładnie powiadamiać aplikację o tym, czy wielokrotnie został aktywowany alarm, np. gdy urządzenie było uśpione.

Jeśli aplikacja tworzy zmienny obiekt PendingIntent, zdecydowanie zalecamy użycie jawnej intencji i wypełnienie pola ComponentName. Dzięki temu za każdym razem, gdy inna aplikacja wywołuje PendingIntent i przekazuje do niej kontrolę, zawsze uruchamia się ten sam komponent w Twojej aplikacji.

Używanie intencji wyraźnych w ramach oczekujących intencji

Aby lepiej określić, jak inne aplikacje mogą korzystać z intencji oczekujących w Twojej aplikacji, zawsze okładaj oczekującą intencję z jawną intencją. Aby w pełni wykorzystać tę sprawdzoną metodę, wykonaj te czynności:

  1. Sprawdź, czy działanie, pakiet i pola komponentu intencji podstawowej są ustawione.
  2. Aby utworzyć intencje oczekujące, użyj narzędzia FLAG_IMMUTABLE dodanego w Androidzie 6.0 (poziom interfejsu API 23). Ta flaga uniemożliwia aplikacjom otrzymującym pole PendingIntent wypełnianie niewypełnionych właściwości. Jeśli minSdkVersion Twojej aplikacji to 22 lub mniej, możesz zapewnić bezpieczeństwo i zgodność, korzystając z tego kodu:

    if (Build.VERSION.SDK_INT >= 23) {
      // Create a PendingIntent using FLAG_IMMUTABLE.
    } else {
      // Existing code that creates a PendingIntent.
    }

Rozwiązanie intencji

Gdy system otrzyma pośrednią intencję rozpoczęcia działania, wyszukuje dla niej najlepszą aktywność, porównując ją z filtrami intencji na podstawie 3 aspektów:

  • Action (Działanie).
  • Dane (identyfikator URI i typ danych).
  • Category [Kategoria]:

W kolejnych sekcjach opisujemy, w jaki sposób intencje są dopasowywane do odpowiednich komponentów zgodnie z deklaracją filtra intencji w pliku manifestu aplikacji.

Test działania

Aby określić akceptowane działania intencji, filtr intencji może zadeklarować zero lub więcej elementów <action>. W tym przykładzie:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

Aby przejść ten filtr, działanie określone w Intent musi być zgodne z jednym z działań wymienionych w filtrze.

Jeśli filtr nie zawiera żadnych działań, nie ma nic do dopasowania intencji, więc wszystkie intencje zakończą się niepowodzeniem. Jeśli jednak Intent nie określa działania, test zostanie zakończony, o ile filtr zawiera co najmniej jedno działanie.

Test kategorii

Aby określić akceptowane kategorie intencji, filtr intencji może zadeklarować zero lub więcej elementów <category>. W tym przykładzie:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

Aby intencja zaliczyła się do testu kategorii, każda kategoria w elemencie Intent musi pasować do kategorii w filtrze. Odwrotność nie jest konieczna – filtr intencji może zadeklarować więcej kategorii niż określono w zasadzie Intent i tak zostanie przesłany tag Intent. Dlatego intencja bez kategorii zawsze przechodzi test, niezależnie od tego, jakie kategorie są zadeklarowane w filtrze.

Uwaga: Android automatycznie stosuje kategorię CATEGORY_DEFAULT do wszystkich intencji niejawnych przekazanych do startActivity() i startActivityForResult(). Jeśli chcesz, aby Twoja aktywność otrzymywała intencje niejawne, musi zawierać kategorię dla elementu "android.intent.category.DEFAULT" w filtrach intencji, tak jak w poprzednim przykładzie <intent-filter>.

Test danych

Aby określić akceptowane dane intencji, filtr intencji może zadeklarować zero lub więcej elementów <data>. W tym przykładzie:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

Każdy element <data> może określać strukturę identyfikatora URI i typ danych (typ mediów MIME). Każda część identyfikatora URI jest osobnym atrybutem: scheme, host, port i path:

<scheme>://<host>:<port>/<path>

Przykład poniżej pokazuje możliwe wartości tych atrybutów:

content://com.example.project:200/folder/subfolder/etc

W tym identyfikatorze URI schemat to content, host to com.example.project, port to 200, a ścieżka to folder/subfolder/etc.

Każdy z tych atrybutów w elemencie <data> jest opcjonalny, ale występują zależności liniowe:

  • Jeśli schemat nie zostanie określony, host będzie ignorowany.
  • Jeśli host nie został określony, port jest ignorowany.
  • Jeśli nie podasz zarówno schematu, jak i hosta, ścieżka będzie ignorowana.

Jeśli identyfikator URI w intencji jest porównywany w filtrze ze specyfikacją identyfikatora URI, jest on porównywany tylko z częściami identyfikatora URI uwzględnionymi w filtrze. Na przykład:

  • Jeśli filtr określa tylko schemat, do niego pasują wszystkie identyfikatory URI z tym schematem.
  • Jeśli filtr określa schemat i uprawnienie, ale nie ma ścieżki, wszystkie identyfikatory URI o tym samym schemacie i uprawnieniu przechodzą filtr niezależnie od swoich ścieżek.
  • Jeśli filtr określa schemat, uprawnienia i ścieżkę, pomijają go tylko identyfikatory URI o tym samym schemacie, uprawnieniach i ścieżce.

Uwaga: specyfikacja ścieżki może zawierać symbol wieloznaczny (*), aby wymagać tylko częściowego dopasowania nazwy ścieżki.

Test danych porównuje identyfikator URI i typ MIME w intencji z identyfikatorem URI i typem MIME określonym w filtrze. Zasady są następujące:

  1. Intencja, która nie zawiera identyfikatora URI ani MIME, nie przechodzi testu tylko wtedy, gdy filtr nie zawiera żadnych identyfikatorów URI ani typów MIME.
  2. Intencja, która zawiera identyfikator URI, ale nie ma żadnego typu MIME (jawnego ani nie można go wywnioskować z identyfikatora URI), przechodzi test tylko wtedy, gdy jego identyfikator URI pasuje do formatu identyfikatora URI filtra, a filtr nie określa typu MIME.
  3. Intencja zawierająca typ MIME, ale nie identyfikator URI, przechodzi test tylko wtedy, gdy filtr zawiera ten sam typ MIME i nie określa formatu identyfikatora URI.
  4. Intencja, która zawiera zarówno identyfikator URI, jak i typ MIME (jawny lub wywnioskowany z identyfikatora URI), przechodzi tę część testu tylko wtedy, gdy pasuje on do wymienionego w filtrze. Przechodzi część testu o identyfikatorze URI, jeśli jej identyfikator URI pasuje do identyfikatora URI w filtrze albo gdy określony jest identyfikator URI content: lub file:, a filtr nie określa tego identyfikatora. Inaczej mówiąc, przyjmuje się, że komponent obsługuje dane content: i file:, jeśli jego filtr zawiera tylko typ MIME.

Uwaga: jeśli intencja określa identyfikator URI lub typ MIME, test danych zakończy się niepowodzeniem, jeśli w elemencie <intent-filter> nie będzie elementów <data>.

Ostatnia reguła (d) odzwierciedla oczekiwanie, że komponenty będą mogły pobierać dane lokalne od pliku lub dostawcy treści. Dlatego ich filtry mogą uwzględniać tylko typ danych i nie muszą wyraźnie nazywać schematów content: ani file:. W poniższym przykładzie element <data> informuje Androida, że komponent może pobrać dane obrazu z dostawcy treści i je wyświetlić:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

Filtry, które określają typ danych, ale nie identyfikator URI, są prawdopodobnie najczęściej stosowane, ponieważ większość dostępnych danych jest rozpowszechniana przez dostawców treści.

Kolejna często używana konfiguracja to filtr ze schematem i typem danych. Na przykład element <data> taki jak poniżej informuje Androida, że może on pobierać dane wideo z sieci, by wykonać działanie:

<intent-filter>
    <data android:scheme="http" android:mimeType="video/*" />
    ...
</intent-filter>

Dopasowywanie intencji

Intencje są dopasowywane do filtrów intencji, aby nie tylko odkryć komponent docelowy do aktywowania, ale także dowiedzieć się czegoś o zbiorze komponentów na urządzeniu. Na przykład aplikacja Home wypełnia menu z aplikacjami, znajdując wszystkie aktywności z filtrami intencji, które określają działanie ACTION_MAIN i kategorię CATEGORY_LAUNCHER. Dopasowanie jest skuteczne tylko wtedy, gdy działania i kategorie w intencji są zgodne z filtrem, zgodnie z opisem w dokumentacji klasy IntentFilter.

Aplikacja może korzystać z dopasowywania intencji w sposób podobny do tego, co robi aplikacja Home. PackageManager ma zestaw metod query...(), które zwracają wszystkie komponenty, które mogą zaakceptować konkretną intencję, i podobną serię metod resolve...(), które określają, który komponent najlepiej odpowiada na intencję. Na przykład queryIntentActivities() zwraca listę wszystkich działań, które mogą wykonać intencję przekazaną jako argument, a queryIntentServices() zwraca podobną listę usług. Żadna z nich nie aktywuje komponentów, a jedynie wyświetla te, które mogą zareagować. Istnieje podobna metoda (queryBroadcastReceivers()) dla odbiorników.