Intencje i filtry intencji

Intent to obiekt do obsługi wiadomości, którego możesz użyć, aby poprosić o wykonanie działania inny komponent aplikacji. Chociaż intencje ułatwiają komunikację między komponentami na kilka sposobów, istnieją 3 podstawowe przypadki użycia:

  • Rozpoczynanie aktywności

    Activity reprezentuje pojedynczy ekran w aplikacji. Możesz uruchomić nową instancję Activity, przekazując Intent do startActivity(). Intent opisuje aktywność, która ma się rozpocząć, i zawiera wszystkie niezbędne 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 aktywnościach.

  • Uruchamianie usługi

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

    W przypadku wersji starszych niż Android 5.0 (poziom interfejsu API 21) możesz uruchomić usługę, korzystając z metod klasy Service. Możesz uruchomić usługę, aby wykonać jednorazową operację (np. pobrać plik), przekazując Intent do startService(). Intent opisuje usługę, którą należy uruchomić, i zawiera wszystkie niezbędne dane.

    Jeśli usługa została zaprojektowana z interfejsem klient-serwer, możesz powiązać ją z innym komponentem, 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ć dowolna aplikacja. System dostarcza różne komunikaty dotyczące zdarzeń systemowych, takich jak uruchomienie systemu lub rozpoczęcie ładowania urządzenia. Możesz dostarczyć 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ć. Więcej informacji znajdziesz w artykułach Interacting with Other Apps (Interakcja z innymi aplikacjami) i Sharing Content (Udostępnianie treści).

Typy intencji

Istnieją 2 rodzaje intencji:

  • Intencje jawne określają, który komponent danej aplikacji spełni intencję, przez podanie pełnej nazwy ComponentName. Zwykle używa się intencji jawnej do uruchomienia komponentu we własnej aplikacji, ponieważ znana jest nazwa klasy aktywności lub usługi, którą chcesz uruchomić. Możesz na przykład rozpocząć nową aktywność w aplikacji w odpowiedzi na działanie użytkownika lub uruchomić usługę, aby pobrać plik w tle.
  • Intencje ogólne nie wskazują konkretnego komponentu, ale deklarują ogólne działanie do wykonania, co pozwala na obsługę przez komponent z innej aplikacji. Jeśli na przykład chcesz pokazać użytkownikowi lokalizację na mapie, możesz użyć intencji niejawnej, aby poprosić inną aplikację o wyświetlenie określonej lokalizacji na mapie.

Rysunek 1 pokazuje, jak intencja jest używana podczas uruchamiania aktywności. Gdy obiekt Intent jednoznacznie wskazuje konkretny komponent aktywności, system natychmiast go uruchamia.

Rysunek 1. Jak intencja ogólna jest dostarczana przez system, aby rozpocząć inną Activity: [1] Activity A tworzy obiekt Intent z opisem działania i przekazuje go do startActivity(). [2] System Android wyszukuje we wszystkich aplikacjach filtr intencji, który pasuje do intencji. Gdy znajdzie dopasowanie, [3] system uruchamia aktywność dopasowania (aktywność B), wywołując metodę onCreate() i przekazując jej Intent.

Gdy używasz intencji niejawnej, system Android znajduje odpowiedni komponent do uruchomienia, porównując 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 zgodnych jest kilka 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 przez zadeklarowanie filtra intencji dla aktywności umożliwiasz innym aplikacjom bezpośrednie uruchamianie Twojej aktywności z określonym rodzajem intencji. Jeśli nie zadeklarujesz żadnych filtrów intencji dla aktywności, można ją uruchomić tylko za pomocą intencji jawnej.

Uwaga: aby zapewnić bezpieczeństwo aplikacji, zawsze używaj jawnej intencji podczas uruchamiania Service i nie deklaruj filtrów intencji dla usług. Używanie niejawnej intencji do uruchamiania usługi stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa odpowie na intencję, a użytkownik nie widzi, która usługa się uruchamia. Od Androida 5.0 (poziom interfejsu API 21) system zgłasza wyjątek, jeśli wywołasz bindService() z niejawną intencją.

Tworzenie intencji

Obiekt Intent zawiera informacje, których system Android używa do określenia, który komponent ma zostać uruchomiony (np. dokładną nazwę lub kategorię komponentu, który powinien otrzymać intencję), oraz informacje, których komponent odbiorcy używa do prawidłowego wykonania działania (np. działanie do wykonania i dane, na których ma zostać wykonane).

Główne informacje zawarte w Intent to:

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

Jest to opcjonalne, ale jest to kluczowa informacja, która sprawia, że intencja jest jawna, co oznacza, że powinna być dostarczana tylko do komponentu aplikacji określonego przez nazwę komponentu. Bez nazwy komponentu intencja jest niejawna, a system decyduje, który komponent powinien ją otrzymać, na podstawie innych informacji o intencji (takich jak działanie, dane i kategoria – opisane poniżej). Jeśli chcesz uruchomić konkretny komponent w aplikacji, musisz podać jego nazwę.

Uwaga: podczas uruchamiania Service zawsze podawaj nazwę komponentu. W przeciwnym razie nie można mieć pewności, która usługa odpowie na intencję, a użytkownik nie będzie widział, która usługa się uruchamia.

To pole Intent jest obiektem ComponentName, który możesz określić, używając 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 znaków określający ogólne działanie do wykonania (np. view lub pick).

W przypadku intencji rozgłaszania jest to działanie, które miało miejsce i jest zgłaszane. Działanie w dużej mierze określa strukturę pozostałej części intencji – zwłaszcza informacje zawarte w danych i dodatkach.

Możesz określić własne działania do użycia przez intencje w aplikacji (lub do użycia przez inne aplikacje do wywoływania komponentów w Twojej aplikacji), ale zwykle określa się stałe działania zdefiniowane przez klasę Intent lub inne klasy platformy. Oto kilka typowych działań, które możesz wykonać, aby rozpocząć aktywność:

ACTION_VIEW
Użyj tego działania w intencji z startActivity(), gdy 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 mapy.
ACTION_SEND
Znana również jako intencja udostępniania. Powinna być używana w intencji z 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 społecznościowego.

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

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

Jeśli definiujesz własne działania, pamiętaj, aby jako prefiks dodać nazwę pakietu aplikacji, 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órych 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łanie to ACTION_EDIT, dane powinny zawierać identyfikator URI dokumentu do edycji.

Podczas tworzenia intencji często ważne jest, aby oprócz identyfikatora URI określić typ danych (typ MIME). Na przykład aktywność, która może wyświetlać obrazy, prawdopodobnie nie będzie mogła odtworzyć pliku audio, mimo że formaty identyfikatorów URI mogą być podobne. Określenie typu MIME danych pomaga systemowi Android znaleźć najlepszy komponent do odebrania intencji. Czasami jednak typ MIME można wywnioskować z identyfikatora URI, zwłaszcza gdy dane są identyfikatorem URI content:. Identyfikator URI content: wskazuje, że dane znajdują się na urządzeniu i są kontrolowane przez 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 wartości za pomocą setDataAndType().

Uwaga: jeśli chcesz ustawić zarówno identyfikator URI, jak i typ MIME, nie wywołuj metod setData() i setType(), ponieważ każda z nich unieważnia wartość drugiej. Zawsze używaj setDataAndType(), aby ustawić zarówno identyfikator URI, jak i typ MIME.

Kategoria
Ciąg znaków zawierający dodatkowe informacje o rodzaju komponentu, który powinien obsłużyć 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
Aktywność docelowa może zostać uruchomiona przez przeglądarkę, aby wyświetlić dane, do których prowadzi link, np. obraz lub e-mail.
CATEGORY_LAUNCHER
Aktywność jest początkową aktywnością zadania i jest wymieniona w programie uruchamiającym aplikacje systemu.

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

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

Wymienione powyżej właściwości (nazwa komponentu, działanie, dane i kategoria) reprezentują cechy definiujące intencję. Dzięki odczytaniu tych właściwości 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 jej rozpoznawania przez komponent aplikacji. Intencja może też dostarczać te informacje:

Dodatki
Pary klucz-wartość, które zawierają dodatkowe informacje wymagane do wykonania żądanego działania. Podobnie jak niektóre działania używają określonych rodzajów identyfikatorów URI danych, niektóre działania używają też określonych dodatków.

Możesz dodać 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 następnie wstawić Bundle do Intent za pomocą putExtras().

Na przykład podczas tworzenia intencji wysłania e-maila za pomocą ACTION_SEND możesz określić odbiorcę do za pomocą klucza EXTRA_EMAIL, a temat za pomocą klucza EXTRA_SUBJECT.

Klasa Intent określa wiele stałych EXTRA_* dla standardowych typów danych. Jeśli musisz zadeklarować własne dodatkowe klucze (w przypadku intencji, które otrzymuje Twoja aplikacja), pamiętaj, aby jako prefiks podać nazwę pakietu aplikacji, 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 podczas wysyłania intencji, którą ma 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 błąd RuntimeException.

Flagi
Flagi są zdefiniowane w klasie Intent i działają jako metadane intencji. Flagi mogą instruować system Android, jak uruchomić aktywność (np. do którego zadania powinna należeć) i jak ją traktować po uruchomieniu (np. czy powinna znajdować się na liście ostatnich aktywności).

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

Przykład wyraźnej intencji

Intencja jawna służy do uruchamiania konkretnego komponentu aplikacji, np. określonej aktywności lub usługi. Aby utworzyć intencję jawną, zdefiniuj nazwę komponentu dla obiektu Intent – wszystkie inne właściwości intencji są opcjonalne.

Jeśli na przykład w aplikacji utworzysz 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 aplikacji Context, a komponentowi – obiekt Class. W związku z tym ta intencja bezpośrednio uruchamia klasę DownloadService w aplikacji.

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

Przykładowa intencja niejawna

Intencja ogólna określa działanie, które może wywołać dowolną aplikację na urządzeniu, która może wykonać to działanie. Użycie intencji niejawnej jest przydatne, gdy aplikacja nie może wykonać działania, ale inne aplikacje prawdopodobnie mogą i chcesz, aby użytkownik wybrał, której aplikacji użyć.

Jeśli na przykład masz treści, które użytkownik ma udostępnić innym osobom, utwórz intencję z działaniem ACTION_SEND i dodaj elementy dodatkowe, które określają treści do udostępnienia. Gdy wywołasz startActivity() z tą intencją, użytkownik będzie mógł 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.
}

Gdy wywoływana jest metoda startActivity(), system sprawdza wszystkie aplikacje instalowane, aby określić, które z nich mogą obsłużyć ten rodzaj intencji (intencję z działaniem ACTION_SEND i danymi „text/plain”). Jeśli jest tylko jedna aplikacja, która może obsłużyć intencję, zostanie ona natychmiast otwarta i otrzyma intencję. Jeśli żadna inna aplikacja nie może go obsłużyć, Twoja aplikacja może przechwycić błąd ActivityNotFoundException. Jeśli intencję akceptuje kilka aktywności, system wyświetla okno takie jak na rysunku 2, aby użytkownik mógł wybrać aplikację, której chce użyć.

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

Rysunek 2. Okno wyboru.

Wymuszanie wyboru aplikacji

Jeśli na intencję niejawną odpowiada więcej niż 1 aplikacja, użytkownik może wybrać, której aplikacji chce użyć, i ustawić ją jako domyślną dla danego działania. Możliwość wybrania domyślnej aplikacji jest przydatna, gdy użytkownik wykonuje działanie, do którego prawdopodobnie za każdym razem chce używać tej samej aplikacji, np. otwiera stronę internetową (użytkownicy często wolą korzystać tylko z jednej przeglądarki).

Jeśli jednak na intencję może odpowiedzieć kilka aplikacji, a użytkownik może chcieć za każdym razem używać innej, należy wyraźnie wyświetlić okno wyboru. W oknie wyboru użytkownik jest proszony o wybranie aplikacji, której chce użyć do wykonania działania (nie może wybrać aplikacji domyślnej). Jeśli na przykład aplikacja wykonuje działanie „udostępnij” za pomocą ACTION_SEND, użytkownicy mogą chcieć udostępnić treści za pomocą innej aplikacji w zależności od sytuacji, dlatego zawsze należy używać okna wyboru, jak pokazano na rysunku 2.

Aby wyświetlić selektor, utwórz Intent za pomocą createChooser() i przekaż go do startActivity(), jak pokazano w tym przykładzie. W tym przykładzie wyświetlane jest okno z listą aplikacji, które odpowiadają intencji przekazanej do metody createChooser(), a podany tekst jest używany jako tytuł 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, aby przechodzić między komponentami w aplikacji lub wykonywać działania w imieniu innej aplikacji. Aby zwiększyć bezpieczeństwo platformy, Android 12 (poziom API 31) i nowsze wersje udostępniają funkcję debugowania, która ostrzega, jeśli aplikacja wykonuje niebezpieczne uruchomienie intencji. Na przykład aplikacja może niebezpiecznie uruchamiać intencję zagnieżdżoną, czyli intencję przekazywaną jako dodatkową w innej intencji.

Jeśli aplikacja wykonuje obie te czynności, system wykrywa niebezpieczne uruchomienie intencji i naruszenie StrictMode:

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

Więcej informacji o tym, jak rozpoznać tę sytuację i wprowadzić zmiany w aplikacji, znajdziesz w poście na blogu na temat zagnieżdżonych intencji na Androidzie na Medium.

Sprawdzanie, czy nie ma niebezpiecznych uruchomień intencji

Aby sprawdzić, czy w aplikacji nie występują niebezpieczne uruchomienia intencji, wywołaj detectUnsafeIntentLaunch() podczas konfigurowania VmPolicy, jak pokazano w tym fragmencie kodu. Jeśli aplikacja wykryje naruszenie StrictMode, możesz zatrzymać jej działanie, 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());
}

Odpowiedzialniejsze korzystanie z intencji

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

Kopiuj tylko niezbędne dodatki w intencjach i przeprowadzaj wszelkie niezbędne operacje oczyszczania i weryfikacji. Aplikacja może kopiować dodatki z jednej intencji do innej, która służy do uruchamiania nowego komponentu. Dzieje się tak, gdy aplikacja wywołuje metodę putExtras(Intent) lub putExtras(Bundle). Jeśli Twoja aplikacja wykonuje jedną z tych operacji, skopiuj tylko te dodatki, których oczekuje komponent odbierający. Jeśli druga intencja (która otrzymuje kopię) uruchamia komponent, który nie jest eksportowany, przed skopiowaniem dodatków do intencji uruchamiającej komponent należy je oczyścić i zweryfikować.

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 zagnieżdżonej intencji użyj PendingIntent. Dzięki temu, gdy inna aplikacja rozpakuje obiekt PendingIntent z zawierającego go obiektu Intent, będzie mogła uruchomić obiekt PendingIntent, używając tożsamości Twojej aplikacji. Ta konfiguracja pozwala innej aplikacji bezpiecznie uruchomić dowolny komponent w Twojej aplikacji, w tym komponent nieeksportowany.

Diagram na rysunku 2 pokazuje, jak system przekazuje kontrolę z Twojej aplikacji (klienta) do innej aplikacji (usługi) i z powrotem do Twojej aplikacji:

  1. Aplikacja tworzy intencję, która wywołuje aktywność w innej aplikacji. W ramach tej intencji dodajesz obiekt PendingIntent jako dodatkowy. Ta intencja oczekująca wywołuje komponent w aplikacji, który nie jest eksportowany.
  2. Po otrzymaniu intencji aplikacji druga aplikacja wyodrębnia zagnieżdżony obiekt PendingIntent.
  3. Inna aplikacja wywołuje metodę send() na obiekcie PendingIntent.
  4. Po przekazaniu kontroli z powrotem do aplikacji system wywołuje intencję oczekującą, używając kontekstu aplikacji.

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

Odbieranie intencji niejawnej

Aby poinformować, jakie intencje niejawne może odbierać 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 dostarcza intencję niejawną do komponentu aplikacji tylko wtedy, gdy intencja może przejść przez jeden z filtrów intencji.

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

Komponent aplikacji powinien deklarować osobne filtry dla każdego unikalnego zadania, które może wykonać. Na przykład jedna aktywność w aplikacji galerii obrazów może mieć 2 filtry: jeden do wyświetlania obrazu, a drugi do jego edycji. Po rozpoczęciu aktywności sprawdza ona Intent i decyduje, jak się zachować, na podstawie informacji w Intent (np. czy 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 (np. w elemencie <activity>).

W każdym komponencie aplikacji, który zawiera element <intent-filter>, wyraźnie ustaw wartość android:exported. Ten atrybut wskazuje, czy komponent aplikacji jest dostępny dla innych aplikacji. W niektórych sytuacjach, np. w przypadku aktywności, których filtry intencji obejmują kategorię LAUNCHER, warto ustawić ten atrybut 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 ma wyraźnie ustawionej wartości android:exported, aplikacji nie będzie można zainstalować na urządzeniu z Androidem 12 lub nowszym.

Wewnątrz elementu <intent-filter> możesz określić typ intencji do zaakceptowania, używając co najmniej jednego z tych 3 elementów:

<action>
Deklaruje zaakceptowane działanie intencji w atrybucie name. Wartość musi być ciągiem znaków działania, a nie stałą klasy.
<data>
Deklaruje typ akceptowanych danych, używając co najmniej 1 atrybutu, który określa różne aspekty identyfikatora URI danych (scheme, host, port, path) i typu MIME.
<category>
Deklaruje kategorię intencji zaakceptowaną w atrybucie name. Wartość musi być ciągiem znaków 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 deklarowały kategorię CATEGORY_DEFAULT. Jeśli nie zadeklarujesz tej kategorii w filtrze intencji, żadne intencje niejawne nie będą kierować do Twojej aktywności.

Oto przykład deklaracji aktywności z filtrem intencji, który odbiera 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, który zawiera więcej niż 1 wystąpienie <action>, <data> lub <category>. Jeśli to zrobisz, musisz mieć pewność, że komponent może obsłużyć wszystkie kombinacje tych elementów filtra.

Jeśli chcesz obsługiwać wiele rodzajów intencji, ale tylko w określonych kombinacjach działania, danych i typu kategorii, musisz utworzyć wiele filtrów intencji.

Intencja niejawna jest testowana pod kątem filtra przez porównanie jej z każdym z 3 elementów. Aby intencja została dostarczona do komponentu, musi przejść wszystkie 3 testy. Jeśli nie pasuje nawet do jednego z nich, system Android nie dostarczy intencji do komponentu. Komponent może jednak mieć wiele filtrów intencji, więc intencja, która nie przejdzie przez jeden z filtrów komponentu, może przejść przez inny. Więcej informacji o tym, jak system rozpoznaje intencje, znajdziesz w sekcji Rozpoznawanie intencji poniżej.

Uwaga: użycie filtra intencji nie jest bezpiecznym sposobem na uniemożliwienie innym aplikacjom uruchamiania Twoich komponentów. Chociaż filtry intencji ograniczają komponent do reagowania tylko na określone rodzaje intencji niejawnych, inna aplikacja może potencjalnie uruchomić komponent Twojej 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 Twoich 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 Service innej aplikacji, zawsze używaj jawnej intencji do uruchamiania własnej usługi.

Uwaga: w przypadku wszystkich aktywności musisz zadeklarować filtry intencji w pliku manifestu. Filtry odbiorników komunikatów można jednak rejestrować dynamicznie, wywołując metodę registerReceiver(). Następnie możesz wyrejestrować odbiornik za pomocą metody 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 zachowania filtrów intencji, oto przykład z pliku manifestu aplikacji do udostępniania 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 – aktywność, która otwiera się, gdy użytkownik po raz pierwszy uruchamia aplikację za pomocą ikony 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 tej aktywności powinna zostać umieszczona w systemowym menu z aplikacjami. Jeśli element <activity> nie określa ikony za pomocą icon, system używa ikony z elementu <application>.

Aby aktywność pojawiła się w menu z aplikacjami, te 2 elementy muszą być ze sobą sparowane.

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

Uwaga: typ MIME application/vnd.google.panorama360+jpg to specjalny typ danych, który określa zdjęcia panoramiczne. Możesz je obsługiwać za pomocą interfejsów API Google Panorama.

Dopasowywanie intencji do filtrów intencji innych aplikacji

Jeśli inna aplikacja jest kierowana na Androida 13 (API na poziomie 33) lub nowszego, może obsłużyć intencję Twojej aplikacji tylko wtedy, gdy intencja pasuje do działań i kategorii elementu <intent-filter> w tej innej aplikacji. Jeśli system nie znajdzie dopasowania, zgłosi błąd ActivityNotFoundException. Aplikacja wysyłająca musi obsłużyć ten wyjątek.

Podobnie, jeśli zaktualizujesz aplikację, aby była kierowana na Androida 13 lub nowszego, wszystkie intencje pochodzące z aplikacji zewnętrznych będą dostarczane do wyeksportowanego komponentu Twojej aplikacji tylko wtedy, gdy będą zgodne z działaniami i kategoriami 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 wymuszane:

  • Intencje dostarczane 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 obejmują system_server i aplikacje, które ustawiają android:sharedUserId na android.uid.system.
  • Intencje pochodzące z roota.

Dowiedz się więcej o dopasowywaniu intencji.

Używanie intencji oczekującej

Obiekt PendingIntent jest otoczką obiektu Intent. Głównym celem elementu PendingIntent jest przyznanie uprawnień aplikacji zewnętrznej do używania zawartego w nim elementu Intent tak, jakby był on wykonywany z procesu Twojej aplikacji.

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

  • Deklarowanie intencji, która ma zostać wykonana, gdy użytkownik wykona działanie z powiadomieniem (NotificationManager systemu Android wykonuje Intent).
  • Deklarowanie intencji, która ma zostać wykonana, gdy użytkownik wykona działanie za pomocą widżetu aplikacji (aplikacja na ekranie głównym wykonuje Intent).
  • Deklarowanie intencji do wykonania w określonym czasie w przyszłości (AlarmManager systemu Android wykonuje Intent).

Każdy obiekt Intent jest przeznaczony do obsługi przez określony typ komponentu aplikacji (Activity, Service lub BroadcastReceiver). Podobnie należy tworzyć obiekty PendingIntent. Gdy używasz intencji oczekującej, aplikacja nie wykonuje jej za pomocą wywołania takiego jak startActivity(). Zamiast tego musisz zadeklarować zamierzony typ komponentu podczas tworzenia obiektu PendingIntent, wywołując odpowiednią metodę kreatora:

Jeśli Twoja aplikacja nie odbiera intencji oczekujących z innych aplikacji, powyższe metody tworzenia obiektu PendingIntent są prawdopodobnie jedynymi metodami PendingIntent, których będziesz potrzebować.

Każda metoda przyjmuje bieżącą aplikację Context, obiekt Intent, który chcesz opakować, oraz co najmniej 1 flagę określającą, jak należy używać intencji (np. czy można jej użyć więcej niż raz).

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

Określanie zmienności

Jeśli Twoja 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 lub niezmienny, 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 wygeneruje błąd IllegalArgumentException, a w Logcat pojawi 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.

W miarę możliwości twórz niezmienne intencje oczekujące

W większości przypadków aplikacja powinna tworzyć niezmienne obiekty PendingIntent, jak pokazano w tym fragmencie kodu. Jeśli obiekt PendingIntent jest niezmienny, 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 wymagane są obiekty PendingIntent, które można modyfikować:

  • obsługę bezpośrednich odpowiedzi w powiadomieniach, Bezpośrednia odpowiedź wymaga zmiany danych klipu w obiekcie PendingIntent powiązanym z odpowiedzią. Zwykle prośbę o tę zmianę wysyła się, przekazując FILL_IN_CLIP_DATA jako flagę do metody fillIn().
  • powiązanie powiadomień z platformą Android Auto za pomocą instancji CarAppExtender;
  • umieszczanie rozmów w dymkach za pomocą instancji PendingIntent; Zmienny obiekt PendingIntent umożliwia systemowi stosowanie prawidłowych flag, takich jak FLAG_ACTIVITY_MULTIPLE_TASK i FLAG_ACTIVITY_NEW_DOCUMENT.
  • żądanie informacji o lokalizacji urządzenia przez wywołanie interfejsu API requestLocationUpdates() lub podobnego. Zmienny obiekt PendingIntent umożliwia systemowi dodawanie dodatkowych intencji, które reprezentują zdarzenia cyklu życia lokalizacji. Te zdarzenia obejmują zmianę lokalizacji i dostępność dostawcy.
  • Planowanie alarmów za pomocą AlarmManager. Zmienny obiekt PendingIntent umożliwia systemowi dodanie dodatkowej intencji EXTRA_ALARM_COUNT. Ten dodatkowy element reprezentuje liczbę uruchomień alarmu powtarzanego. Dzięki temu dodatkowemu elementowi intencja może dokładnie powiadomić aplikację, czy alarm cykliczny został uruchomiony wielokrotnie, np. gdy urządzenie było w trybie uśpienia.

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

Używanie intencji jawnych w intencjach oczekujących

Aby lepiej określić, jak inne aplikacje mogą używać intencji oczekujących Twojej aplikacji, zawsze opakowuj intencję oczekującą w intencję jawną. Aby ułatwić sobie stosowanie tej sprawdzonej metody:

  1. Upewnij się, że działanie, pakiet oraz pola komponentu podstawowej intencji są ustawione.
  2. Aby tworzyć intencje oczekujące, używaj metody FLAG_IMMUTABLE dodanej w Androidzie 6.0 (poziom interfejsu API 23). Ta flaga uniemożliwia aplikacjom, które otrzymują PendingIntent, wypełnianie niewypełnionych właściwości. Jeśli minSdkVersion aplikacji to 22 lub mniej, możesz zapewnić bezpieczeństwo i zgodność, używając tego kodu:

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

Rozpoznawanie intencji

Gdy system otrzyma intencję niejawną, aby rozpocząć aktywność, wyszukuje najlepszą aktywność dla tej intencji, porównując ją z filtrami intencji na podstawie 3 aspektów:

  • Działanie.
  • Dane (zarówno identyfikator URI, jak 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ć akceptowane działania intencji, filtr intencji może zadeklarować co najmniej 1 element <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 Intent musi odpowiadać jednemu z działań wymienionych w filtrze.

Jeśli filtr nie zawiera żadnych działań, intencja nie ma z czym się dopasować, więc wszystkie intencje nie przechodzą testu. Jeśli jednak Intent nie określa działania, przechodzi test, o ile filtr zawiera co najmniej 1 działanie.

Test kategorii

Aby określić akceptowane kategorie intencji, filtr intencji może zadeklarować co najmniej 1 element <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 przeszła test kategorii, każda kategoria w Intent musi pasować do kategorii w filtrze. Odwrotna sytuacja nie jest konieczna – filtr intencji może deklarować więcej kategorii niż określono w Intent, a Intent nadal będzie przekazywany. Dlatego intencja bez kategorii zawsze przechodzi ten test, niezależnie od tego, jakie kategorie są zadeklarowane w filtrze.

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

Test danych

Aby określić akceptowane dane intencji, filtr intencji może zadeklarować co najmniej 1 element <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 i typ danych (typ MIME). Każda część identyfikatora URI jest osobnym atrybutem: scheme, host, port i path:

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

Poniższy przykład 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 występują zależności liniowe:

  • Jeśli nie określono schematu, host jest ignorowany.
  • Jeśli nie podano hosta, port jest ignorowany.
  • Jeśli nie podano schematu ani hosta, ścieżka jest ignorowana.

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

  • Jeśli filtr określa tylko schemat, wszystkie identyfikatory URI z tym schematem pasują do filtra.
  • Jeśli filtr określa schemat i urząd, ale nie ścieżkę, wszystkie identyfikatory URI z tym samym schematem i urzędem przechodzą przez filtr, niezależnie od ich ścieżek.
  • Jeśli filtr określa schemat, urząd i ścieżkę, przechodzą przez niego tylko identyfikatory URI z tym samym schematem, urzędem i ścieżką.

Uwaga: specyfikacja ścieżki może zawierać gwiazdkę (*), która wymaga tylko częściowego dopasowania nazwy ścieżki.

Test danych porównuje zarówno identyfikator URI, jak i typ MIME w intencji z identyfikatorem URI i typem MIME określonym w filtrze. Oto zasady:

  1. Intencja, która nie zawiera identyfikatora URI ani typu MIME, przechodzi 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 możliwego do wywnioskowania z identyfikatora URI), przechodzi test tylko wtedy, gdy jej identyfikator URI jest zgodny z formatem identyfikatora URI filtra, a filtr nie określa typu MIME.
  3. Intencja, która zawiera typ MIME, ale nie zawiera identyfikatora 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 możliwy do wywnioskowania z identyfikatora URI), przechodzi część testu dotyczącą typu MIME tylko wtedy, gdy ten typ jest zgodny z typem wymienionym w filtrze. Część testu dotycząca identyfikatora URI jest zaliczana, jeśli identyfikator URI pasuje do identyfikatora URI w filtrze lub jeśli ma identyfikator URI content: lub file:, a filtr nie określa identyfikatora URI. Innymi słowy, zakłada 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 nie powiedzie się, jeśli w elemencie <intent-filter> nie ma elementów <data>.

Ostatnia reguła (d) odzwierciedla oczekiwanie, że komponenty mogą pobierać dane produktów dostępnych lokalnie z pliku lub mechanizmu dostarczania treści. Dlatego ich filtry mogą zawierać tylko typ danych i nie muszą wyraźnie wymieniać schematów content: i file:. Poniższy przykład pokazuje typowy przypadek, w którym element <data> informuje Androida, że komponent może pobierać dane obrazu z mechanizmu dostarczania treści i je wyświetlać:

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

Filtry, które określają typ danych, ale nie identyfikator URI, są prawdopodobnie najczęstsze, ponieważ większość dostępnych danych jest dostarczana przez mechanizmy dostarczania treści.

Inną typową konfiguracją jest filtr ze schematem i typem danych. Na przykład element <data> taki jak poniżej informuje Androida, że komponent może pobierać dane wideo z sieci, aby 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 po to, aby wykryć komponent docelowy do aktywacji, ale także po to, aby dowiedzieć się czegoś o zestawie komponentów na urządzeniu. 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 pasują do filtra, zgodnie z opisem w dokumentacji klasy IntentFilter.

Twoja aplikacja może używać dopasowywania intencji w podobny sposób jak aplikacja Google Home. PackageManager ma zestaw metod query...(), które zwracają wszystkie komponenty mogące akceptować konkretną intencję, oraz podobną serię metod resolve...(), które określają najlepszy komponent do reagowania na intencję. Na przykład queryIntentActivities() zwraca listę wszystkich aktywności, które mogą wykonać intencję przekazaną jako argument, a queryIntentServices() zwraca podobną listę usług. Żadna z metod nie aktywuje komponentów, tylko wyświetla listę tych, które mogą odpowiedzieć. Istnieje podobna metoda, queryBroadcastReceivers(), dla odbiorników rozgłoszeniowych.