Intencje i filtry intencji

Intent to obiekt do przesyłania wiadomości, którego możesz użyć, aby zażądać działania z innego komponentu aplikacji. Choć intencje ułatwiają komunikację między komponentami na kilka sposobów, wyróżniamy 3 podstawowe przypadki użycia:

  • Rozpoczynanie aktywności

    Activity reprezentuje pojedynczy ekran aplikacji. Aby uruchomić nowe wystąpienie Activity, przekaż Intent do startActivity(). Intent opisuje działanie mające na celu uruchomienie i przenosi wszystkie niezbędne dane.

    Jeśli chcesz otrzymać wynik aktywności po jej zakończeniu, zadzwoń pod numer 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 użycia interfejsu. W Androidzie 5.0 (poziom interfejsu API 21) i nowszych możesz uruchomić usługę za pomocą JobScheduler. Więcej informacji na ten temat (JobScheduler) znajdziesz tutaj: API-reference documentation.

    W przypadku wersji Androida starszych niż 5.0 (poziom interfejsu API 21) możesz uruchamiać usługę za pomocą metod klasy Service. Możesz uruchomić usługę, aby wykonać jednorazową operację (na przykład pobranie pliku), przekazując Intent do startService(). Intent opisuje usługę, która uruchamia i przenosi wszystkie niezbędne dane.

    Jeśli usługa została zaprojektowana z wykorzystaniem interfejsu klient-serwer, możesz utworzyć z nią powiązanie z innego komponentu, przekazując Intent do bindService(). Więcej informacji znajdziesz w przewodniku po usługach.

  • Przesyłanie transmisji

    Komunikat to wiadomość, którą może odebrać każda aplikacja. System dostarcza różne transmisje w przypadku zdarzeń systemowych, na przykład podczas uruchamiania systemu lub rozpoczęcia ładowania urządzenia. Możesz przesłać transmisję do innych aplikacji, przekazując Intent do sendBroadcast() lub sendOrderedBroadcast().

W dalszej części tej strony wyjaśniamy, jak działają intencje i jak ich używać. Informacje pokrewne znajdziesz w sekcjach Korzystanie z innych aplikacji i Udostępnianie treści.

Typy intencji

Istnieją 2 typy intencji:

  • Intencje jawne określają, który komponent danej aplikacji spełni intencję, poprzez określenie pełnego parametru ComponentName. Zwykle do uruchomienia komponentu we własnej aplikacji zwykle używasz wyraźnej intencji, ponieważ znasz nazwę klasy aktywności lub usługi, którą chcesz uruchomić. Możesz na przykład rozpocząć nowe działanie w aplikacji w odpowiedzi na działanie użytkownika albo uruchomić usługę, która pobierze plik w tle.
  • Intencje ogólne nie określają nazwy konkretnego komponentu, tylko deklarują ogólne działanie do wykonania, które umożliwia komponentowi z innej aplikacji jego obsługę. Jeśli na przykład chcesz pokazać użytkownikowi lokalizację na mapie, możesz użyć intencji ogólnej, aby zażądać, by inna aplikacja z obsługą tej funkcji pokazywała określoną lokalizację na mapie.

Rysunek 1 pokazuje, w jaki sposób intencja jest wykorzystywana podczas rozpoczynania działania. Gdy obiekt Intent wyraźnie nadaje nazwę konkretnemu komponentowi aktywności, system od razu go uruchamia.

Rysunek 1. Sposób, w jaki intencja niejawna jest przekazywana przez system w celu rozpoczęcia innego działania: [1] Działanie A tworzy intencję Intent z opisem działania i przekazuje ją do startActivity(). [2] System Android wyszukuje we wszystkich aplikacjach filtr intencji pasujący do intencji. W przypadku znalezienia dopasowania [3] system rozpoczyna odpowiednie działanie (Działanie B), wywołując metodę onCreate() i przekazując mu Intent.

Gdy używasz intencji niejawnej, system Android znajduje odpowiedni komponent i zaczyna porównywać zawartość intencji z filtrami intencji zadeklarowanymi w pliku manifestu innych aplikacji na urządzeniu. Jeśli intencja pasuje do filtra intencji, system uruchamia ten komponent i dostarcza mu obiekt Intent. Jeśli kompatybilnych jest kilka filtrów intencji, system wyświetla okno, aby użytkownik mógł wybrać aplikację, której chce użyć.

Filtr intencji to wyrażenie w pliku manifestu aplikacji określające typ intencji, które komponent chce otrzymywać. Na przykład zadeklarowanie filtra intencji dla danej aktywności umożliwia innym aplikacjom bezpośrednie rozpoczęcie aktywności z określonym zamiarem. Podobnie, jeśli nie zadeklarujesz żadnych filtrów intencji dla działania, będzie można je uruchomić tylko z wyraźną intencją.

Uwaga: aby mieć pewność, że aplikacja jest bezpieczna, uruchamiając Service, zawsze używaj jednoznacznej intencji i nie deklaruj filtrów intencji dla swoich usług. Użycie intencji niejawnej do uruchomienia usługi stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa zareaguje na daną intencję, a użytkownik nie będzie mógł 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łasz funkcję bindService() z intencją niejawną.

Budowanie zamiaru

Obiekt Intent zawiera informacje używane przez system Android do określenia, który komponent należy uruchomić (np. dokładną nazwę komponentu lub jego kategorię, który powinien otrzymać intencję), a także informacje, których komponent odbiorcy używa do prawidłowego wykonania działania (np. nazwę i dane, na które należy ten komponent wykonać).

Intent zawiera następujące informacje:

Nazwa komponentu
Nazwa komponentu, który ma zostać uruchomiony.

Jest to opcjonalne, ale jest najważniejszym elementem informacji, który sprawia, że intencja jest wyraźna, co oznacza, że intencja powinna być dostarczana tylko do komponentu aplikacji zdefiniowanego przez nazwę komponentu. Bez nazwy komponentu intencja jest niejawna, a system decyduje, który komponent powinien otrzymać intencję na podstawie innych informacji o intencji (takich jak działanie, dane i kategoria opisane poniżej). Jeśli chcesz uruchomić w aplikacji określony komponent, podaj jego nazwę.

Uwaga: gdy uruchamiasz Service, zawsze podawaj nazwę komponentu. W przeciwnym razie nie będziesz mieć pewności, która usługa zareaguje na intencję, a użytkownik nie będzie mógł zobaczyć, która usługa się uruchamia.

To pole Intent jest obiektem ComponentName, który możesz określić, używając w pełni kwalifikowanej nazwy klasy komponentu docelowego z nazwą 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 ogólne działanie do wykonania (np. view lub pick).

W przypadku intencji transmisji jest to działanie, które miało miejsce i jest raportowane. Działanie to w dużym stopniu określa strukturę reszty intencji, a zwłaszcza informacji zawartych w danych i materiałach dodatkowych.

Możesz określić własne działania na potrzeby intencji w aplikacji (lub do wykorzystania przez inne aplikacje do wywoływania komponentów w aplikacji), ale zwykle określasz stałe działania zdefiniowane przez klasę Intent lub inne klasy platformy. Oto kilka typowych czynności, które można wykonać, rozpoczynając aktywność:

ACTION_VIEW
Użyj 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 galerii lub adres do wyświetlenia w aplikacji z mapami.
ACTION_SEND
Nazywa się to też intencją udostępniania. Należy jej używać w intencji startActivity(), gdy masz dane, które użytkownik może udostępnić za pomocą innej aplikacji, np. aplikacji do poczty e-mail lub aplikacji do udostępniania treści w mediach społecznościowych.

Więcej stałych definiujących działania ogólne znajdziesz w dokumentacji klasy Intent. Inne działania są zdefiniowane w innych miejscach w ramach platformy Androida, np. w Settings dla działań, które powodują otwarcie określonych ekranów w systemowej aplikacji Ustawienia.

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

Jeśli zdefiniujesz własne działania, pamiętaj o dodaniu nazwy pakietu aplikacji jako prefiksu, jak 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), który odwołuje się do danych, na które ma zostać wykonane działanie, lub typ MIME tych danych. Typ dostarczanych danych jest zwykle określany przez działanie intencji. Jeśli na przykład działaniem jest ACTION_EDIT, dane powinny zawierać identyfikator URI dokumentu do edycji.

Przy tworzeniu intencji często ważne jest, aby oprócz identyfikatora URI określić typ danych (jej typ MIME). Na przykład działanie, które może wyświetlać obrazy, prawdopodobnie nie odtworzy pliku audio, chociaż formaty URI mogą być podobne. Określenie typu MIME danych pomaga systemowi Android znajdować najlepszy komponent do odbioru intencji. Typ MIME czasem jednak można wywnioskować na podstawie identyfikatora URI, zwłaszcza gdy dane są za pomocą identyfikatora URI content:. Identyfikator URI content: wskazuje, że dane znajdują się na urządzeniu i są kontrolowane przez interfejs ContentProvider, dzięki czemu 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 bezpośrednio za pomocą funkcji setDataAndType().

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

Kategoria
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 popularnych kategorii:
CATEGORY_BROWSABLE
Działanie docelowe pozwala na uruchomienie działania przeglądarki w celu wyświetlenia danych, do których odwołuje się link, np. obrazu lub e-maila.
CATEGORY_LAUNCHER
Działanie to początkowe działanie zadania i jest wyświetlane w programie uruchamiającym aplikacje.

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

Możesz określić kategorię za pomocą atrybutu addCategory().

Te właściwości wymienione powyżej (nazwa komponentu, działanie, dane i kategoria) odzwierciedlają definicja intencji. Dzięki tym właściwościom system Android może określić, który komponent aplikacji powinien uruchomić. Intencja może jednak zawierać dodatkowe informacje, które nie wpływają na sposób, w jaki jest przekształcana w komponent aplikacji. Intencja może też dostarczać te informacje:

Dodatki
Pary klucz-wartość zawierające dodatkowe informacje wymagane do wykonania żądanego działania. Niektóre działania używają określonych rodzajów identyfikatorów URI danych, tak samo niektóre działania wykorzystują też określone dodatki.

Za pomocą różnych metod putExtra() możesz dodawać dodatkowe dane. Każda z nich akceptuje 2 parametry: nazwę klucza i wartość. Możesz też utworzyć obiekt Bundle ze wszystkimi dodatkowymi danymi, a następnie wstawić Bundle w Intent za pomocą putExtras().

Jeśli na przykład tworzysz intencję wysłania e-maila za pomocą ACTION_SEND, możesz określić adresata to za pomocą klucza EXTRA_EMAIL, a temat – za pomocą klucza EXTRA_SUBJECT.

Klasa Intent określa wiele stałych EXTRA_* dla standaryzowanych typów danych. Jeśli musisz zadeklarować własne dodatkowe klucze (na potrzeby intencji odbieranych przez aplikację), pamiętaj o dodaniu nazwy pakietu aplikacji jako prefiksu, jak pokazano 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 podczas wysyłania intencji, która według Ciebie powinna otrzymać inna aplikacja. Jeśli aplikacja próbuje uzyskać dostęp do danych w obiekcie Bundle, ale nie ma dostępu do klasy parcelowanej lub serializowanej, system zgłasza RuntimeException.

Flagi
Flagi są zdefiniowane w klasie Intent, które działają jako metadane intencji. Flagi mogą informować system Androida, jak uruchomić działanie (np. określić, do którego zadania ma ono należeć) i jak postępować z nim po uruchomieniu (np. czy należy ono do listy ostatnich działań).

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

Przykładowa jawna intencja

Intencja jawna to taka, która służy do uruchamiania określonego komponentu aplikacji, np. określonej aktywności lub usługi. Aby utworzyć intencję bezpośrednią, zdefiniuj nazwę komponentu obiektu Intent – wszystkie pozostałe właściwości intencji są opcjonalne.

Jeśli na przykład masz w aplikacji utworzoną usługę o nazwie DownloadService, która ma pobierać plik z internetu, możesz ją uruchomić za pomocą 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 i komponent Class. W związku z tym intencja wyraźnie uruchamia w aplikacji klasę DownloadService.

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

Przykładowa intencja ogólna

Intencja niejawna określa działanie, które może wywołać na urządzeniu dowolną aplikację mogącą wykonać to działanie. Korzystanie z intencji niejawnej przydaje się, gdy aplikacja nie może wykonać danego działania, podczas gdy inne aplikacje zazwyczaj mogą to robić i chcesz, aby użytkownik wybrał to, czego chce użyć.

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 dodatkowe, które określają treści do udostępnienia. Gdy wywołujesz startActivity() z tą intencją, użytkownik może wybrać aplikację, za pomocą której udostępni treść.

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 potrafią obsłużyć tego rodzaju intencje (intencja z działaniem ACTION_SEND i przechowujące dane „tekst/zwykły”). Jeśli istnieje tylko jedna aplikacja, która może ją obsłużyć, otwiera się od razu i otrzymuje intencję. Jeśli inne aplikacje nie są w stanie go obsłużyć, może wychwytywać błąd ActivityNotFoundException. Jeśli kilka aktywności akceptuje intencję, system wyświetla okno dialogowe

Więcej informacji na temat uruchamiania innych aplikacji znajdziesz w przewodniku dotyczącym wysyłania użytkownika do innej aplikacji.

Rysunek 2. Okno wyboru.

Wymuszanie wyboru aplikacji

Gdy więcej niż jedna aplikacja odpowiada na intencję domniemaną, użytkownik może wybrać aplikację, której chce użyć, i ustawić ją jako domyślną do wykonywania danego działania. Możliwość wyboru wartości domyślnych jest przydatna przy wykonywaniu czynności, w ramach której użytkownik prawdopodobnie chce używać tej samej aplikacji za każdym razem, np. podczas otwierania strony internetowej (użytkownicy często preferują tylko 1 przeglądarkę).

Jeśli jednak kilka aplikacji może odpowiedzieć na intencję, a użytkownik może chcieć użyć innej aplikacji za każdym razem, należy jawnie pokazać okno wyboru. W oknie wyboru użytkownik musi wybrać aplikację, która ma wykonać działanie (nie może wybrać domyślnej aplikacji). Gdy na przykład aplikacja wykonuje działanie „udostępnij” za pomocą działania ACTION_SEND, użytkownicy mogą zechcieć udostępnić coś innym aplikacji zależnie od ich obecnej sytuacji. W takiej sytuacji należy zawsze korzystać z okna wyboru, jak widać na rys. 2.

Aby wyświetlić selektor, utwórz pole Intent przy użyciu createChooser() i przekaż go do startActivity(), jak pokazano w przykładzie poniżej. W tym przykładzie wyświetla się okno z listą aplikacji, które odpowiadają 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 niebezpiecznych uruchomień intencji

Aplikacja może uruchamiać intencje nawigacji między komponentami wewnątrz aplikacji lub wykonywania działania w imieniu innej aplikacji. Aby poprawić bezpieczeństwo platformy, Android 12 (poziom interfejsu API 31) i nowsze wersje udostępniają funkcję debugowania, która ostrzega, gdy aplikacja wykonuje niebezpieczne uruchomienie intencji. Na przykład aplikacja może przeprowadzić niebezpieczne uruchomienie intencji zagnieżdżonej, która jest przekazywana jako dodatkowa intencja w innej intencji.

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

  1. Aplikacja oddziela zagnieżdżoną intencję od dodatkowych elementów intencji.
  2. Aplikacja natychmiast uruchamia komponent aplikacji, używając tej zagnieżdżonej intencji, np. przekazując intencję do startActivity(), startService() lub bindService().

Więcej informacji o tym, jak zidentyfikować tę sytuację i wprowadzić zmiany w aplikacji, znajdziesz w poście na blogu na temat intencji związanych z Androidem Nesting w Medium.

Sprawdzanie uruchamiania niebezpiecznych intencji

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

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());
}

Korzystaj z intencji bardziej odpowiedzialnie

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

Skopiuj tylko najważniejsze dodatki w ramach intencji oraz zadbaj o odpowiednie warunki sanitarne i weryfikację. Aplikacja może skopiować dodatki z jednej intencji do innej intencji, która służy do uruchamiania nowego komponentu. Dzieje się tak, gdy aplikacja wywołuje putExtras(Intent) lub putExtras(Bundle). Jeśli aplikacja wykonuje jedną z tych operacji, skopiuj tylko te dodatki, których oczekuje komponent odbierający. Jeśli inna intencja (która odbiera kopię) uruchamia komponent, który nie jest eksportowany, oczyść i zweryfikuj dodatki przed skopiowaniem ich do intencji uruchamiającej ten komponent.

Nie eksportuj niepotrzebnie komponentów aplikacji. Jeśli na przykład chcesz uruchomić komponent aplikacji za pomocą wewnętrznej intencji zagnieżdżonej, ustaw atrybut android:exported tego komponentu na false.

Zamiast intencji zagnieżdżonej używaj obiektu PendingIntent. Dzięki temu, gdy inna aplikacja odbierze pakiet PendingIntent, który zawiera Intent, druga aplikacja może uruchomić PendingIntent, korzystając z tożsamości Twojej aplikacji. Dzięki tej konfiguracji druga aplikacja może bezpiecznie uruchomić w Twojej aplikacji dowolny komponent, w tym komponent niewyeksportowany.

Diagram na ilustracji 2 pokazuje, jak system przekazuje kontrolę z aplikacji (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 ramach tej intencji dodajesz jako dodatkowy obiekt PendingIntent. Ta intencja oczekująca wywołuje komponent w aplikacji. Nie jest on 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ę, wykorzystując kontekst aplikacji.

Rysunek 2. Schemat komunikacji między aplikacjami, gdy używana jest zagnieżdżona oczekująca intencja.

Odbieranie intencji ogólnej

Aby reklamować intencje ogólne, które może otrzymywać 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 dostarcza domyślną intencję do komponentu aplikacji tylko wtedy, gdy intencja może przejść przez jeden z filtrów intencji.

Uwaga: intencja jawna jest zawsze dostarczana do miejsca docelowego, niezależnie od filtrów intencji zadeklarowanych przez komponent.

Komponent aplikacji powinien zadeklarować osobne filtry dla każdego unikalnego zadania. Na przykład jedna aktywność w aplikacji galerii obrazów może mieć 2 filtry: jeden filtr do wyświetlania obrazu i drugi do edycji obrazu. Po rozpoczęciu działania sprawdza Intent i na podstawie informacji w Intent określa, jak ma się zachować (np. wyświetlić elementy sterujące edytora).

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

W każdym komponencie aplikacji, który zawiera element <intent-filter>, ustaw wyraźną wartość android:exported. Ten atrybut wskazuje, czy komponent aplikacji jest dostępny dla innych aplikacji. W niektórych sytuacjach, takich jak działania, których filtry intencji zawierają kategorię LAUNCHER, warto ustawić w tym atrybucie wartość true. W przeciwnym razie bezpieczniej jest ustawić wartość tego atrybutu na false.

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

W elemencie <intent-filter> możesz określić typ akceptowanych intencji, korzystając z co najmniej jednego z tych 3 elementów:

<action>
W atrybucie name deklaruje zaakceptowane działanie intencji. Wartość musi być literałem w postaci ciągu działania, a nie stałą klasy.
<data>
Deklaruje typ akceptowanych danych za pomocą co najmniej 1 atrybutu określającego różne aspekty identyfikatora URI danych (scheme, host, port, path) i typu MIME.
<category>
W atrybucie name deklaruje zaakceptowaną kategorię intencji. Wartość musi być literałem w postaci 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, żadne intencje niejawne nie trafią do Twojej aktywności.

Na przykład oto deklaracja aktywności z filtrem intencji odbierającym intencję ACTION_SEND, gdy typ danych to 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 obejmujący więcej niż 1 wystąpienie <action>, <data> lub <category>. Jeśli to zrobisz, musisz mieć pewność, że komponent obsługuje wszystkie kombinacje tych elementów filtra.

Jeśli chcesz obsługiwać różne rodzaje intencji, ale tylko w przypadku określonych kombinacji działań, danych i typu kategorii, musisz utworzyć kilka filtrów intencji.

Intencję niejawną testuje się za pomocą filtra, porównując intencję z każdym z 3 elementów. Aby intencja mogła zostać dostarczona do komponentu, musi przejść wszystkie 3 testy. Jeśli nie uda się dopasować nawet jednego z nich, system Android nie przekaże intencji do komponentu. Komponent może jednak mieć wiele filtrów intencji, więc intencja, która nie przejdzie przez żaden z filtrów komponentu, może przedostać się przez inny filtr. Więcej informacji o tym, jak system rozpoznaje intencje, znajdziesz w sekcji dotyczącej rozwiązywania intencji poniżej.

Uwaga: użycie filtra intencji nie jest bezpiecznym sposobem na zapobieganie uruchamianiu komponentów przez inne aplikacje. Filtry intencji ograniczają reagowanie komponentu na tylko określone rodzaje intencji niejawnych. Z kolei inna aplikacja może uruchomić komponent aplikacji za pomocą intencji jawnej, jeśli deweloper określi nazwy komponentów. Jeśli ważne jest, aby tylko Twoja aplikacja mogła uruchamiać jeden z komponentów, nie deklaruj filtrów intencji w pliku manifestu. Zamiast tego ustaw atrybut exported na "false" dla tego komponentu.

Podobnie, aby uniknąć przypadkowego uruchomienia polecenia Service innej aplikacji, zawsze używaj jednoznacznej intencji, aby uruchomić własną usługę.

Uwaga: w przypadku wszystkich działań musisz zadeklarować filtry intencji w pliku manifestu. Filtry odbiorników można jednak rejestrować dynamicznie przez wywołanie metody registerReceiver(). Potem możesz wyrejestrować odbiorcę w unregisterReceiver(). Dzięki temu aplikacja może nasłuchiwać określonych transmisji tylko przez określony czas, gdy aplikacja jest uruchomiona.

Przykładowe filtry

Aby zademonstrować niektóre działanie filtra intencji, przygotowaliśmy przykład z pliku manifestu aplikacji do udostępniania treści w mediach społecznościowych:

<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>

Pierwsza aktywność (MainActivity) to główny punkt wejścia aplikacji, czyli działanie, które rozpoczyna się, gdy użytkownik uruchamia 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 wskazuje, że ikona aktywności powinna być umieszczona w programie uruchamiającym aplikacje systemu. Jeśli element <activity> nie ma ikony z atrybutem icon, system użyje ikony z elementu <application>.

Muszą one być ze sobą połączone, by aktywność była widoczna w Menu z aplikacjami.

Druga aktywność (ShareActivity) ma ułatwiać udostępnianie tekstu i treści multimedialnych. Użytkownicy mogą rozpoczynać to działanie, przechodząc do niego z poziomu MainActivity, ale mogą też wpisać ShareActivity bezpośrednio z innej aplikacji, która wywołuje intencję ogólną 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órych możesz użyć za pomocą interfejsów 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łużyć intencję aplikacji tylko wtedy, gdy intencja pasuje do działań i kategorii elementu <intent-filter> w tej aplikacji. Jeśli system nie znajdzie dopasowania, przesyła ActivityNotFoundException. Aplikacja wysyłająca musi obsługiwać ten wyjątek.

Podobnie, jeśli zaktualizujesz aplikację tak, aby była kierowana na Androida 13 lub nowszego, wszystkie intencje pochodzące z aplikacji zewnętrznych będą dostarczane do wyeksportowanego komponentu aplikacji tylko wtedy, gdy intencja ta pasuje do działań i kategorii elementu <intent-filter> zadeklarowanego przez aplikację. Dzieje się tak niezależnie od docelowej wersji pakietu SDK aplikacji wysyłającej.

W tych przypadkach dopasowywanie intencji nie jest egzekwowane:

  • 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. aplikacje system_server i aplikacje, w przypadku których android:sharedUserId ma wartość android.uid.system.
  • Intencje pochodzące z poziomu roota.

Dowiedz się więcej o dopasowywaniu intencji.

Używasz intencji oczekującej

Obiekt PendingIntent to otoka wokół obiektu Intent. Głównym celem obiektu PendingIntent jest udzielanie obcej aplikacji pozwolenia na używanie zawartego w niej elementu Intent w taki sposób, jakby byłby on uruchamiany w ramach własnego procesu Twojej aplikacji.

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

  • Deklaracja intencji do wykonania, gdy użytkownik wykona działanie związane z Twoim powiadomieniem (NotificationManager w systemie Android wykonuje Intent).
  • Deklaracja intencji, która ma być wykonywana, gdy użytkownik wykona działanie w Twoim widżecie aplikacji (aplikacja na ekranie głównym wykonuje polecenie Intent).
  • Deklarowanie intencji do wykonania w określonym czasie w przyszłości (AlarmManager systemu Android wykonuje Intent).

Obiekt Intent jest przeznaczony do obsługi przez określony typ komponentu aplikacji (Activity, Service lub BroadcastReceiver), tak samo jak obiekt PendingIntent należy utworzyć z tą samą zasadą. Gdy używasz intencji oczekującej, aplikacja nie wykonuje intencji za pomocą wywołania takiego jak startActivity(). Zamiast tego podczas tworzenia obiektu PendingIntent musisz zadeklarować odpowiedni typ komponentu, wywołując odpowiednią metodę twórcy:

O ile Twoja aplikacja nie otrzymuje oczekujących intencji z innych aplikacji, powyższe metody tworzenia PendingIntent będą prawdopodobnie jedynymiPendingIntentmetodami, których kiedykolwiek będziesz potrzebować.

Każda metoda pobiera bieżącą aplikację Context, Intent, którą chcesz opakować, oraz co najmniej 1 flagę określającą sposób użycia intencji (np. czy intencja może być używana więcej niż raz).

Więcej informacji na temat korzystania z intencji oczekujących znajdziesz w dokumentacji poszczególnych przypadków użycia, np. w przewodnikach po interfejsach API powiadomień i widżetów aplikacji.

Określ zmienność

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

Jeśli aplikacja próbuje utworzyć obiekt PendingIntent bez ustawienia flagi zmienności, system zgłasza IllegalArgumentException, a w Logcat pojawia się ten 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.

Gdy tylko jest to możliwe, twórz stałe intencje oczekujące

W większości przypadków aplikacja powinna tworzyć stałe obiekty PendingIntent, jak pokazano w tym fragmencie kodu. Jeśli obiekt PendingIntent jest stały, inne aplikacje nie mogą zmodyfikować intencji, aby dostosować wynik wywołania intencji.

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 wymagają zmiennych obiektów PendingIntent:

  • Obsługa bezpośrednich działań związanych z odpowiedziami w powiadomieniach. Bezpośrednia odpowiedź wymaga zmiany danych klipu w obiekcie PendingIntent, który jest powiązany z odpowiedzią. Aby żądanie tej zmiany było zwykle możliwe, należy przekazać FILL_IN_CLIP_DATA jako flagę do metody fillIn().
  • Tworzenie powiązania powiadomień ze platformą Androida Auto przy użyciu instancji CarAppExtender.
  • umieszczanie rozmów w dymkach przy użyciu instancji PendingIntent. Zmienny obiekt PendingIntent umożliwia systemowi stosowanie prawidłowych flag, takich jak FLAG_ACTIVITY_MULTIPLE_TASK czy FLAG_ACTIVITY_NEW_DOCUMENT.
  • Żądanie informacji o lokalizacji urządzenia przez wywołanie requestLocationUpdates() lub podobne interfejsy API. Zmienny obiekt PendingIntent umożliwia systemowi dodawanie dodatków, które reprezentują zdarzenia cyklu życia lokalizacji. Mogą one obejmować zmianę lokalizacji i udostępnienie dostawcy.
  • Planuję alarmy za pomocą AlarmManager. Zmienny obiekt PendingIntent umożliwia systemowi dodanie dodatkowej intencji EXTRA_ALARM_COUNT. Ta dodatkowa informacja wskazuje, ile razy aktywowany był powtarzający się alarm. Dzięki temu intencja może precyzyjnie informować aplikację o tym, czy powtarzający się alarm został aktywowany wiele razy, np. gdy urządzenie było uśpione.

Jeśli aplikacja tworzy zmienny obiekt PendingIntent, zdecydowanie zalecamy użycie intencji jawnej i wypełnienie ComponentName. Dzięki temu za każdym razem, gdy inna aplikacja wywoła PendingIntent i przekaże kontrolę z powrotem do Twojej aplikacji, uruchamia się ten sam komponent aplikacji.

Używaj bezpośrednich intencji w ramach intencji oczekujących

Aby lepiej zdefiniować, jak inne aplikacje mogą korzystać z oczekujących intencji, zawsze otocz intencję oczekującą na intencję jawną. Aby skorzystać ze sprawdzonych metod, wykonaj te czynności:

  1. Sprawdź, czy działanie, pakiet i pola komponentu intencji podstawowej są ustawione.
  2. Użyj narzędzia FLAG_IMMUTABLE dodanego w Androidzie 6.0 (poziom interfejsu API 23), aby utworzyć intencje oczekujące. Ta flaga uniemożliwia aplikacjom, które otrzymują wartość PendingIntent, wypełnianie brakujących właściwości. Jeśli minSdkVersion w Twojej aplikacji ma wartość 22 lub niższą, możesz zapewnić bezpieczeństwo i zgodność razem, używając 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 otrzymuje domniemaną intencję rozpoczęcia działania, wyszukuje najlepszą aktywność dla danej intencji, porównując ją z filtrami intencji na podstawie 3 aspektów:

  • Akcja.
  • dane (identyfikator URI i typ danych).
  • Category [Kategoria]:

W sekcjach poniżej opisujemy, jak intencje są dopasowywane do odpowiednich komponentów zgodnie z deklaracją filtra intencji w pliku manifestu aplikacji.

Test działania

Aby określić zaakceptowane działania intencji, filtr intencji może zadeklarować zero lub więcej elementów <action>, jak 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 polu 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, więc wszystkie intencje kończą się niepowodzeniem. Jeśli jednak Intent nie określa działania, test kończy test, o ile filtr zawiera co najmniej 1 działanie.

Test kategorii

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

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

Aby intencja została zaliczona, 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, a Intent i tak spełnia warunki. Dlatego intencja bez kategorii zawsze kończy ten test niezależnie od tego, jakie kategorie są zadeklarowane w filtrze.

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

Test danych

Aby określić dane zaakceptowanych intencji, filtr intencji może zadeklarować zero lub więcej elementów <data>, jak 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 oraz typ danych (typ multimedió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 jest opcjonalny w elemencie <data>, ale istnieją zależności liniowe:

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

Gdy identyfikator URI w intencji jest porównywany ze specyfikacją identyfikatora URI w filtrze, jest to porównywane tylko z częściami identyfikatora URI zawartymi w filtrze. Na przykład:

  • Jeśli filtr określa tylko schemat, wszystkie identyfikatory URI z tym schematem pasują do niego.
  • Jeśli filtr określa schemat i urzędnik, ale nie ścieżkę, pomijane są wszystkie identyfikatory URI o tym samym schemacie i uprawnieniu, niezależnie od ścieżki.
  • Jeśli filtr określa schemat, urząd i ścieżkę, pomijają go tylko identyfikatory URI o tym samym schemacie, urzędzie i ścieżce.

Uwaga: specyfikacja ścieżki może zawierać symbol wieloznaczny (*). Pozwala to 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. Reguły są następujące:

  1. Intencja, która nie zawiera ani identyfikatora URI, ani typu MIME, zalicza test tylko wtedy, gdy filtr nie określa żadnych identyfikatorów URI ani typów MIME.
  2. Intencja, która zawiera identyfikator URI, ale nie zawiera typu MIME (ani jawnego, ani niewywnioskowanego na podstawie identyfikatora URI), kończy test tylko wtedy, gdy identyfikator URI pasuje do formatu URI filtra, a filtr nie określa typu MIME.
  3. Intencja, która zawiera typ MIME, ale nie identyfikator URI, zalicza 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 możliwy do wywnioskowania z identyfikatora URI), przekazuje część MIME związaną z typem MIME w teście tylko wtedy, gdy ten typ pasuje do typu wymienionego w filtrze. Przekazuje część URI testu, jeśli jego identyfikator URI pasuje do identyfikatora URI w filtrze albo gdy zawiera identyfikator URI content: lub file:, a filtr go nie określa. Inaczej mówiąc, zakłada się, że komponent obsługuje dane content: i file:, jeśli filtr pokazuje tylko typ MIME.

Uwaga: jeśli intencja określa identyfikator URI lub typ MIME, test danych się nie powiedzie, jeśli w <intent-filter> nie będzie żadnych elementów <data>.

Ostatnia reguła (d) odpowiada oczekiwaniu, że komponenty mogą pobierać dane lokalne od pliku lub dostawcy treści. Dlatego filtry mogą uwzględniać tylko typ danych i nie muszą nadawać schematom content: i file: jawnym nazwom. Ten przykład pokazuje typowy przypadek, w którym element <data> informuje Androida, że komponent może pobrać dane obrazu od dostawcy treści i je wyświetlić:

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

Najczęściej stosuje się filtry, które określają typ danych, ale nie identyfikator URI, ponieważ większość dostępnych danych jest rozpowszechniana przez dostawców treści.

Inną typową konfiguracją jest filtr ze schematem i typem danych. Na przykład element <data> podobny do tego informuje Androida, że komponent może pobrać 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 nie tylko w celu wykrycia komponentu docelowego do aktywowania, ale też uzyskania informacji o zbiorze komponentów urządzenia. Na przykład aplikacja Home wypełnia Menu z aplikacjami, znajdując wszystkie działania z filtrami intencji, które określają działanie ACTION_MAIN i kategorię CATEGORY_LAUNCHER. Dopasowanie jest udane tylko wtedy, gdy działania i kategorie w intencji są zgodne z filtrem, zgodnie z dokumentacją klasy IntentFilter.

Aplikacja może korzystać z dopasowywania intencji w sposób podobny do działania aplikacji Home. PackageManager zawiera zestaw metod query...(), które zwracają wszystkie komponenty, które mogą zaakceptować konkretną intencję, oraz podobną serię metod resolve...(), które określają najlepszy komponent reagowania na intencję. Na przykład funkcja queryIntentActivities() zwraca listę wszystkich działań, które mogą wykonać intencję przekazaną jako argument, a queryIntentServices() zwraca podobną listę usług. Żadna z tych metod nie aktywuje komponentów – wyświetla się tylko te, które mogą zareagować. Istnieje podobna metoda (queryBroadcastReceivers()) dla odbiorników.