Intencje i filtry intencji

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

  • Rozpoczynanie aktywności

    Symbol Activity reprezentuje pojedynczy ekran w aplikacji. Nową instancję elementu Activity możesz utworzyć, przekazując element Intent do elementu startActivity(). Intent opisuje aktywność, którą należy rozpocząć, i zawiera niezbędne dane.

    Jeśli chcesz otrzymać wynik działania po jego zakończeniu, wywołaj funkcję startActivityForResult(). Twoja aktywność otrzymuje wynik jako osobny obiekt Intent w wywołaniu zwrotnym onActivityResult(). Więcej informacji znajdziesz w przewodniku Działania.

  • 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 wersjach możesz uruchomić usługę za pomocą JobScheduler. Więcej informacji o JobScheduler znajdziesz w 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 niezbędne dane.

    Jeśli usługa jest 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.

  • Nadawanie transmisji

    Broadcast to wiadomość, którą może odebrać dowolna aplikacja. System dostarcza różne transmisje dotyczące zdarzeń systemowych, np. uruchomienia systemu lub rozpoczęcia ładowania urządzenia. Możesz dostarczyć transmisję do innych aplikacji, przekazując Intentdo 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 Interakcje z innymi aplikacjamiUdostępnianie treści.

Rodzaje intencji

Istnieją 2 rodzaje intencji:

  • Jawne intencje określają, który komponent której aplikacji zrealizuje intencję, poprzez podanie pełnego ComponentName. Zwykle używasz intencji jawnej, aby uruchomić komponent w swojej aplikacji, ponieważ znasz nazwę 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 określają konkretnego komponentu, ale deklarują ogólne działanie, które ma zostać wykonane, co umożliwia obsługę intencji przez komponent z innej aplikacji. Jeśli na przykład chcesz pokazać użytkownikowi lokalizację na mapie, możesz użyć niejawnego zamiaru, aby poprosić inną aplikację o wyświetlenie określonej lokalizacji na mapie.

Ilustracja 1 pokazuje, jak intencja jest używana podczas rozpoczynania aktywności. Gdy Intent zawiera nazwę konkretnego komponentu aktywności, system natychmiast go uruchamia.

Rysunek 1. Sposób, w jaki niejawny zamiar jest przekazywany przez system, aby rozpocząć inną aktywność: [1] Aktywność A tworzy 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 znajdziemy odpowiednik, [3] system rozpoczyna działanie dopasowywania (działanie B), wywołując jego metodę onCreate() i przekazując jej wartość 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 przekazuje mu obiekt Intent. Jeśli zgodnych jest kilka filtrów intencji, system wyświetla okno dialogowe, w którym użytkownik może wybrać aplikację, której chce użyć.

Filtr intencji to wyrażenie w pliku manifestu aplikacji, które określa typy intencji, jakie komponent chce otrzymywać. Na przykład deklarując filtr intencji dla aktywności, umożliwiasz innym aplikacjom bezpośrednie uruchamianie tej aktywności za pomocą określonego rodzaju intencji. Podobnie jeśli nie zadeklarujesz żadnych filtrów intencji dla aktywności, można ją uruchomić tylko za pomocą jawnej intencji.

Ostrzeżenie: aby zapewnić bezpieczeństwo aplikacji, zawsze używaj jawnego zamiaru podczas uruchamiania Service i nie deklaruj filtrów intencji dla usług. Używanie niejawnego zamiaru do uruchamiania usługi jest niebezpieczne, ponieważ nie można mieć pewności, która usługa odpowie na zamiar, a użytkownik nie widzi, która usługa się uruchamia. Począwszy od Androida 5.0 (interfejs API na poziomie 21), system zgłasza wyjątek, jeśli wywołasz bindService() za pomocą niejawnego zamiaru.

Tworzenie intencji

Obiekt Intent zawiera informacje, których system Android używa do określania, który komponent ma zostać uruchomiony (np. dokładna nazwa komponentu lub kategoria 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 zdefiniowanego przez nazwę komponentu. Jeśli nazwa komponentu nie jest podana, 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 – opisanych poniżej). Jeśli chcesz uruchomić konkretny komponent w aplikacji, podaj jego nazwę.

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

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

Czynność
Ciąg znaków określający ogólne działanie do wykonania (np. view lub pick).

W przypadku intencji transmisji jest to działanie, które zostało wykonane 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, które będą używane przez intencje w aplikacji (lub przez inne aplikacje do wywoływania komponentów w Twojej aplikacji), ale zwykle określasz 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 map.
ACTION_SEND
Znany też jako intencja share. Używaj go w intencji z startActivity(), gdy masz dane, które użytkownik może udostępnić za pomocą innej aplikacji, np. aplikacji do obsługi poczty e-mail lub aplikacji do udostępniania w mediach społecznościowych.

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

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

Jeśli definiujesz własne działania, pamiętaj, aby dodać nazwę pakietu aplikacji jako prefiks, 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), który odwołuje się do danych, na których mają być wykonywane działania, lub typ MIME tych danych. Rodzaj dostarczanych danych jest zwykle określany przez działanie intencji. Jeśli na przykład działanie to ACTION_EDIT, dane powinny zawierać adres URI dokumentu do edycji.

Podczas tworzenia intencji często ważne jest, aby oprócz identyfikatora URI określić typ danych (jego typ MIME). Na przykład aktywność, która może wyświetlać obrazy, prawdopodobnie nie będzie w stanie odtworzyć pliku audio, mimo że formaty URI mogą być podobne. Określenie typu MIME danych pomaga systemowi Android znaleźć najlepszy komponent do odebrania intencji. Typ MIME można jednak czasami wywnioskować z identyfikatora URI, zwłaszcza gdy dane są identyfikatorem URI content:. Identyfikator content: URI 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 funkcję setData(). Aby ustawić tylko typ MIME, wywołaj funkcję setType(). W razie potrzeby możesz ustawić oba parametry za pomocą znaku setDataAndType().

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

Kategoria
Ciąg znaków 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
Aktywność docelowa może być uruchamiana przez przeglądarkę internetową w celu wyświetlania danych, do których odwołuje się link, np. obrazu lub wiadomości e-mail.
CATEGORY_LAUNCHER
Aktywność jest początkową aktywnością zadania i jest wymieniona w menu aplikacji systemu.

Pełną listę kategorii znajdziesz w Intentopisie klasy.

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ę. Odczytując te 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 przekształcenia w komponent aplikacji. Intencja może też zawierać te informacje:

Dodatki
Pary klucz-wartość zawierające 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 dodawać dodatkowe dane za pomocą różnych putExtra() metod, z których każda przyjmuje 2 parametry: nazwę klucza i wartość. Możesz też utworzyć obiekt Bundle ze wszystkimi dodatkowymi danymi, a następnie wstawić obiekt Bundle do obiektu Intent za pomocą metody putExtras().

Na przykład podczas tworzenia intencji wysłania e-maila z ACTION_SEND możesz określić odbiorcę to za pomocą klucza EXTRA_EMAIL i określić subject 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 (dla intencji, które odbiera Twoja aplikacja), pamiętaj, aby dodać nazwę pakietu aplikacji jako prefiks, 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";

Ostrzeżenie: nie używaj danych Parcelable ani Serializable podczas wysyłania intencji, która ma być odebrana przez inną aplikację. Jeśli aplikacja próbuje uzyskać dostęp do danych w obiekcie Bundle, ale nie ma dostępu do klasy przekazanej lub serializowanej, system zgłasza wyjątek RuntimeException.

Flagi
Flagi są zdefiniowane w klasie Intent i pełnią funkcję metadanych intencji. Flagi mogą instruować system Android, jak uruchomić aktywność (np. do którego zadania ma należeć aktywność) i jak ją traktować po uruchomieniu (np. czy ma należeć do listy ostatnich aktywności).

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

Przykładowa intencja bezpośrednia

Intencja jawna służy do uruchamiania konkretnego komponentu aplikacji, np. określonej aktywności lub usługi w aplikacji. 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 masz usługę o nazwie DownloadService, która 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 ten zamiar 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 ogólna określa działanie, które może wywołać dowolną aplikację na urządzeniu, która jest w stanie je wykonać. Używanie niejawnego zamiaru jest przydatne, gdy Twoja aplikacja nie może wykonać działania, ale inne aplikacje prawdopodobnie mogą to zrobić 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 dodatkowe informacje określające treści do udostępnienia. Gdy wywołasz startActivity() z tą intencją, użytkownik będzie mógł wybrać aplikację, za pomocą której 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 funkcja startActivity(), system sprawdza wszystkie zainstalowane aplikacje, 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 tego zrobić, Twoja aplikacja może przechwycić występujący ActivityNotFoundException. Jeśli intencję akceptuje kilka działań, system wyświetla okno dialogowe, 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 Twoją intencję niejawną odpowiada więcej niż jedna aplikacja, użytkownik może wybrać, której z nich chce użyć, i ustawić ją jako domyślną dla danego działania. Możliwość wyboru domyślnej aplikacji jest przydatna podczas wykonywania czynności, do których użytkownik prawdopodobnie chce za każdym razem używać tej samej aplikacji, np. podczas otwierania strony internetowej (użytkownicy często wolą tylko jedną przeglądarkę).

Jeśli jednak na intencję może odpowiedzieć kilka aplikacji, a użytkownik może chcieć za każdym razem używać innej aplikacji, musisz wyraźnie wyświetlić okno wyboru. W oknie wyboru wyświetla się prośba o wybranie aplikacji, która ma wykonać działanie (użytkownik nie może wybrać aplikacji domyślnej dla tego działania). Jeśli na przykład Twoja aplikacja wykonuje działanie „udostępnij” za pomocą działania ACTION_SEND, użytkownicy mogą chcieć udostępnić treści za pomocą innej aplikacji w zależności od bieżącej sytuacji, więc zawsze używaj okna wyboru, jak pokazano na rysunku 2.

Aby wyświetlić selektor, utwórz obiekt Intent za pomocą metody createChooser() i przekaż go do metody startActivity(), jak pokazano w tym przykładzie. Ten przykład wyświetla okno dialogowe z listą aplikacji, które reagują na intencję przekazaną do metody createChooser(), i używa podanego tekstu jako tytułu okna dialogowego.

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, aby przechodzić między komponentami w jej obrębie 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, gdy aplikacja uruchamia intencję w niebezpieczny sposób. Na przykład aplikacja może niebezpiecznie uruchamiać zagnieżdżoną intencję, czyli intencję przekazywaną jako dodatkowa w innej intencji.

Jeśli Twoja aplikacja wykonuje obie te czynności, system wykrywa niebezpieczne uruchomienie intencji i dochodzi do naruszenia StrictMode:

  1. Aplikacja rozpakowuje zagnieżdżoną intencję z dodatków dostarczonej intencji.
  2. Aplikacja natychmiast uruchamia komponent za pomocą tej zagnieżdżonej intencji, np. przekazując ją do funkcji startActivity(), startService() lub bindService().

Więcej informacji o tym, jak rozpoznać tę sytuację i wprowadzić zmiany w aplikacji, znajdziesz w poście na blogu Android Nesting Intents w serwisie Medium.

Sprawdzanie niebezpiecznych uruchomień intencji

Aby sprawdzić, czy w aplikacji nie dochodzi do niebezpiecznych uruchomień intencji, podczas konfigurowania VmPolicy wywołaj detectUnsafeIntentLaunch() w sposób pokazany w poniższym fragmencie kodu. Jeśli aplikacja wykryje naruszenie trybu StrictMode, możesz zatrzymać jej działanie, aby chronić potencjalnie wrażliwe 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 niebezpiecznego zamiaru i naruszenia trybu StrictMode, postępuj zgodnie z tymi sprawdzonymi metodami.

Kopiuj tylko najważniejsze dodatki w ramach intencji i przeprowadzaj niezbędne czyszczenie i weryfikację. Aplikacja może kopiować dodatki z jednej intencji do innej intencji, która jest używana do uruchamiania nowego komponentu. Dzieje się tak, gdy aplikacja wywołuje funkcje putExtras(Intent) lub putExtras(Bundle). Jeśli Twoja aplikacja wykonuje jedną z tych operacji, kopiuj tylko dodatki, których oczekuje komponent odbierający. Jeśli inny zamiar (który otrzymuje kopię) uruchamia komponent, który nie jest eksportowany, oczyść i sprawdź dodatki przed skopiowaniem ich do zamiaru, który uruchamia komponent.

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

Zamiast zagnieżdżonego zamiaru używaj PendingIntent. Dzięki temu, gdy inna aplikacja rozpakuje PendingIntentIntent, w którym się znajduje, będzie mogła uruchomić PendingIntent, używając tożsamości Twojej aplikacji. Ta konfiguracja umożliwia innej aplikacji bezpieczne uruchomienie dowolnego komponentu, w tym komponentu nieeksportowanego, w Twojej aplikacji.

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

  1. Aplikacja tworzy intencję, która wywołuje aktywność w innej aplikacji. W 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 oczekujący zamiar, używając kontekstu aplikacji.

Rysunek 2. Diagram komunikacji między aplikacjami podczas korzystania z zagnieżdżonego oczekującego zamiaru.

Otrzymywanie intencji ogólnej

Aby reklamować, jakie niejawne intencje może odbierać Twoja aplikacja, zadeklaruj co najmniej 1 filtr intencji dla każdego komponentu aplikacji z elementem <intent-filter>pliku manifestu. Każdy filtr intencji określa typy intencji, które akceptuje, na podstawie działania, danych i kategorii intencji. System dostarcza do komponentu aplikacji niejawną intencję tylko wtedy, gdy może ona przejść przez jeden z filtrów intencji.

Uwaga: zamiar jawny jest zawsze dostarczany do miejsca docelowego, niezależnie od filtrów zamiaru zadeklarowanych przez komponent.

Komponent aplikacji powinien deklarować oddzielne 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 edytowania. Gdy aktywność się rozpoczyna, sprawdza Intent i na podstawie informacji w Intent (np. czy wyświetlać elementy sterujące edytora) decyduje o swoim działaniu.

Każdy filtr intencji jest zdefiniowany przez element <intent-filter> w pliku manifestu aplikacji, zagnieżdżony 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 zawierają 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 parametru android:exported, aplikacja nie będzie mogła zostać zainstalowana na urządzeniu z Androidem 12 lub nowszym.

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

<action>
Deklaruje zaakceptowanie działania intencji w atrybucie name. Wartość musi być dosłowną wartością ciągu znaków działania, a nie stałą klasy.
<data>
Deklaruje typ akceptowanych danych za pomocą co najmniej 1 atrybutu, który określa różne aspekty identyfikatora URI danych (scheme, host, port, path) i typ MIME.
<category>
Deklaruje zaakceptowaną kategorię intencji w atrybucie name. Wartość musi być dosłowną wartością ciągu znaków działania, a nie stałą klasy.

Uwaga: aby otrzymywać niejawne intencje, musisz uwzględnić kategorię CATEGORY_DEFAULT w filtrze intencji. Metody startActivity()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ą kierowane do Twojej aktywności.

Oto na przykład deklaracja aktywności z filtrem intencji, który umożliwia odbieranie intencji 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 symbolu <action>, <data> lub <category>. Jeśli tak zrobisz, musisz mieć pewność, że komponent poradzi sobie z dowolną kombinacją 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 domyślna jest testowana względem filtra przez porównanie jej z każdym z 3 elementów. Aby intencja została przekazana do komponentu, musi przejść wszystkie 3 testy. Jeśli nie uda się dopasować choćby 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 jeden z nich, może przejść przez inny. Więcej informacji o tym, jak system rozwiązuje intencje, znajdziesz w sekcji poniżej poświęconej rozwiązywaniu intencji.

Ostrzeżenie: używanie filtra intencji nie jest bezpiecznym sposobem na uniemożliwienie innym aplikacjom uruchamiania komponentów. Filtry intencji ograniczają komponent do reagowania tylko na określone rodzaje domniemanych intencji, ale inna aplikacja może potencjalnie uruchomić komponent Twojej aplikacji za pomocą jawnej intencji, jeśli deweloper określi nazwy komponentów. Jeśli zależy Ci na tym, 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 jawnego zamiaru, aby uruchomić własną usługę.

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

Przykładowe filtry

Aby zademonstrować niektóre zachowania filtra 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 do aplikacji – aktywność, która otwiera się, gdy użytkownik po raz pierwszy uruchamia aplikację za pomocą ikony w programie uruchamiającym:

  • Działanie ACTION_MAIN oznacza, że jest to główny punkt wejścia i nie oczekuje żadnych danych intencji.
  • Kategoria CATEGORY_LAUNCHER oznacza, że ikona tej aktywności powinna być umieszczona w menu aplikacji systemu. Jeśli element <activity> nie określa ikony za pomocą atrybutu icon, system używa ikony z elementu <application>.

Aby aktywność była widoczna w menu aplikacji, te 2 elementy muszą być ze sobą powiązane.

Drugie działanie, ShareActivity, ma ułatwiać udostępnianie tekstu i treści multimedialnych. Użytkownicy mogą przejść do tego działania, nawigując do niego z MainActivity, ale mogą też przejść do 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, 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ługiwać intencję Twojej aplikacji tylko wtedy, gdy pasuje ona do działań i kategorii elementu <intent-filter> w tej aplikacji. Jeśli system nie znajdzie dopasowania, zgłosi wyjątek ActivityNotFoundException. Aplikacja wysyłająca musi obsłużyć 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 będą zgodne z działaniami i kategoriami elementu <intent-filter> zadeklarowanego przez aplikację. To zachowanie występuje niezależnie od docelowej wersji pakietu SDK aplikacji wysyłającej.

W tych przypadkach dopasowywanie do 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ą wartość android:sharedUserId na android.uid.system.
  • Intencje pochodzące z katalogu głównego.

Dowiedz się więcej o dopasowywaniu do intencji.

Korzystanie z intencji oczekującej

Obiekt PendingIntent jest otoczką obiektu Intent. Głównym celem PendingIntent jest przyznanie zagranicznej aplikacji uprawnień do korzystania z zawartego w niej Intent tak, jakby był on wykonywany w procesie Twojej aplikacji.

Główne przypadki użycia oczekującego zamiaru to:

  • Deklarowanie intencji, która ma zostać wykonana, gdy użytkownik podejmie działanie związane z powiadomieniem (NotificationManager systemu Android wykonuje Intent).
  • Deklarowanie intencji, która ma zostać wykonana, gdy użytkownik wykona działanie w widżecie aplikacji (aplikacja ekranu głównego wykonuje Intent).
  • Deklarowanie intencji, która ma zostać wykonana w określonym czasie w przyszłości (system Android AlarmManager wykonuje Intent).

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

O ile Twoja aplikacja nie otrzymuje oczekujących intencji z innych aplikacji, powyższe metody tworzenia PendingIntent są prawdopodobnie jedynymi metodami, jakich kiedykolwiek będziesz potrzebować.PendingIntent

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

Więcej informacji o używaniu oczekujących intencji znajdziesz w dokumentacji poszczególnych przypadków użycia, np. w przewodnikach po interfejsach API PowiadomieniaWidż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 żadnej flagi zmienności, system zgłosi wyjątek IllegalArgumentException, a w narzędziu 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 oczekujące intencje.

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

W niektórych przypadkach użycia wymagane są jednak obiekty modyfikowalne PendingIntent:

  • Obsługa bezpośrednich odpowiedzi w powiadomieniach. Bezpośrednia odpowiedź wymaga zmiany danych klipu w obiekcie PendingIntent powiązanym z odpowiedzią. Zwykle o tę zmianę prosisz, przekazując wartość FILL_IN_CLIP_DATA jako flagę do metody fillIn().
  • Powiązywanie powiadomień z platformą Android Auto za pomocą instancji CarAppExtender.
  • Umieszczanie rozmów w dymkach za pomocą instancji PendingIntent. Obiekt modyfikowalny PendingIntent umożliwia systemowi zastosowanie odpowiednich flag, takich jak FLAG_ACTIVITY_MULTIPLE_TASK i FLAG_ACTIVITY_NEW_DOCUMENT.
  • Wysyłanie prośby o informacje o lokalizacji urządzenia przez wywołanie interfejsu API requestLocationUpdates() lub podobnych interfejsów API. Obiekt modyfikowalny PendingIntent umożliwia systemowi dodawanie dodatkowych informacji o intencjach, które reprezentują zdarzenia cyklu życia lokalizacji. Obejmują one zmianę lokalizacji i dostępność dostawcy.
  • Ustawianie alarmów za pomocą AlarmManager. Obiekt PendingIntent z możliwością zmiany umożliwia systemowi dodanie dodatkowego parametru EXTRA_ALARM_COUNT intencji. Ta dodatkowa informacja przedstawia liczbę uruchomień powtarzającego się alarmu. Dzięki temu dodatkowemu elementowi intencja może dokładnie powiadamiać aplikację o tym, czy powtarzający się alarm został uruchomiony wielokrotnie, np. gdy urządzenie było w trybie uśpienia.

Jeśli aplikacja tworzy obiekt modyfikowalny PendingIntent, zdecydowanie zalecamy użycie jawnego zamiaru i wypełnienie pola ComponentName. Dzięki temu, gdy inna aplikacja wywoła PendingIntent i przekaże kontrolę z powrotem do Twojej aplikacji, zawsze uruchomi się ten sam komponent.

Używanie intencji bezpośrednich w intencjach oczekujących

Aby lepiej określić, jak inne aplikacje mogą używać oczekujących intencji Twojej aplikacji, zawsze umieszczaj oczekującą intencję w intencji jawnej. Aby stosować tę sprawdzoną metodę, wykonaj te czynności:

  1. Sprawdź, czy pola działania, pakietu i komponentu podstawowej intencji są ustawione.
  2. Do tworzenia oczekujących intencji używaj metody FLAG_IMMUTABLE, która została dodana w Androidzie 6.0 (poziom interfejsu API 23). Ten flag uniemożliwia aplikacjom, które otrzymują PendingIntent, wypełnianie brakujących właściwości. Jeśli wartość minSdkVersion w Twojej aplikacji jest 22 lub niższa, możesz podać informacje o bezpieczeństwie i zgodności razem za pomocą 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 niejawną intencję rozpoczęcia aktywności, wyszuka najlepszą aktywność dla tej intencji, porównując ją z filtrami intencji na podstawie 3 aspektów:

  • Działanie.
  • dane (zarówno URI, jak i typ danych);
  • Category [Kategoria]:

W sekcjach poniżej opisujemy, jak intencje są dopasowywane do odpowiednich komponentów na podstawie deklaracji filtra intencji w pliku manifestu aplikacji.

Test działania

Aby określić akceptowane działania związane z intencją, filtr intencji może deklarować zero lub więcej elementów <action>, jak pokazano 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 parametrze Intent musi być zgodne z jednym z działań wymienionych w filtrze.

Jeśli filtr nie zawiera żadnych działań, nie ma niczego, do czego intencja mogłaby pasować, więc wszystkie intencje nie przejdą testu. Jeśli jednak Intentnie określa działania, test zostanie zaliczony, 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 pokazano 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 odpowiadać kategorii w filtrze. Nie jest to jednak konieczne – filtr intencji może deklarować więcej kategorii niż określono w Intent, a Intent nadal będzie przekazywać informacje. 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 startActivity()startActivityForResult(). Jeśli chcesz, aby Twoja aktywność odbierała intencje niejawne, musi ona 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 deklarować zero lub więcej elementów <data>, jak pokazano 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ę 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 istnieją zależności liniowe:

  • Jeśli nie podasz schematu, host zostanie zignorowany.
  • Jeśli nie podasz hosta, port zostanie zignorowany.
  • Jeśli nie podasz schematu ani hosta, ścieżka zostanie zignorowana.

Gdy identyfikator URI w intencji jest porównywany ze specyfikacją identyfikatora URI w filtrze, porównywane są tylko te części identyfikatora URI, które są uwzględnione w filtrze. Na 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 autorytet, ale nie ścieżkę, wszystkie identyfikatory URI z tym samym schematem i autorytetem przejdą filtr niezależnie od ścieżek.
  • Jeśli filtr określa schemat, autorytet i ścieżkę, tylko identyfikatory URI o tym samym schemacie, autorytecie i ścieżce przejdą przez filtr.

Uwaga: specyfikacja ścieżki może zawierać symbol wieloznaczny w postaci gwiazdki (*), aby 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. Obowiązują te 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 ma typu MIME (ani jawnego, ani możliwego do wywnioskowania z identyfikatora URI), przechodzi test tylko wtedy, gdy jej identyfikator URI pasuje do formatu identyfikatora URI filtra, a filtr również 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 test typu MIME tylko wtedy, gdy ten typ pasuje do typu wymienionego w filtrze. Test identyfikatora URI jest zaliczany, 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, komponent obsługuje dane content:file:, jeśli jego lista filtrów 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 ma elementów <data>.

Ostatnia reguła, czyli reguła (d), odzwierciedla oczekiwanie, że komponenty mogą pobierać dane lokalne z pliku lub dostawcy treści. Dlatego ich filtry mogą zawierać tylko typ danych i nie muszą jawnie określać schematów content:file:. Poniższy przykład pokazuje typowy przypadek, w którym element <data> informuje Androida, że komponent może pobierać dane obrazu z dostawcy treści i je wyświetlać:

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

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

Inną popularną konfiguracją jest filtr ze schematem i typem danych. Na przykład element <data> 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 do intencji

Intencje są dopasowywane do filtrów intencji nie tylko w celu wykrycia komponentu docelowego do aktywacji, ale także w celu uzyskania informacji o zestawie komponentów na urządzeniu. Na przykład aplikacja Home wypełnia program uruchamiający aplikacje, wyszukując wszystkie aktywności z filtrami intencji, które określają działanie ACTION_MAIN i kategorię CATEGORY_LAUNCHER. Dopasowanie jest możliwe tylko wtedy, gdy działania i kategorie w obiekcie Intent pasują do filtra, co opisano w dokumentacji klasy IntentFilter.

Aplikacja może używać dopasowywania intencji w sposób podobny do aplikacji Google Home. Klasa PackageManager ma zestaw metod query...(), które zwracają wszystkie komponenty mogące akceptować określony zamiar, oraz podobną serię metod resolve...(), które określają najlepszy komponent do reagowania na zamiar. 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 tych metod nie aktywuje komponentów. Wyświetlają one tylko te, które mogą odpowiadać. Podobna metoda, queryBroadcastReceivers(), jest dostępna w przypadku odbiorników.