Intent
to obiekt wiadomości, którego można użyć do wysłania żądania działania z innego komponentu aplikacji.
Chociaż intencje ułatwiają komunikację między komponentami na kilka sposobów, są 3 główne przypadki użycia:
- Rozpoczynanie aktywności
Element
Activity
reprezentuje jeden ekran w aplikacji. Możesz rozpocząć nowe wystąpienie obiektuActivity
, przekazującIntent
dostartActivity()
.Intent
opisuje działanie, które ma zostać uruchomione, i przenosi potrzebne dane.Jeśli chcesz otrzymać wynik aktywności po jej zakończeniu, wywołaj
startActivityForResult()
. Aktywność otrzymuje wynik jako osobny obiektIntent
w wywołaniu zwrotnymonActivityResult()
aktywności. Więcej informacji znajdziesz w przewodniku po ćwiczeniach. - Uruchamianie usługi
Service
to komponent, który wykonuje operacje w tle bez interfejsu użytkownika. W Androidzie 5.0 (poziom interfejsu API 21) i nowszych możesz uruchamiać usługę za pomocąJobScheduler
. Więcej informacji oJobScheduler
znajdziesz tutaj:API-reference documentation
.W przypadku wersji starszych niż 5.0 (poziom interfejsu API 21) możesz uruchomić usługę za pomocą metod klasy
Service
. Aby uruchomić usługę do wykonania operacji jednorazowej (takiej jak pobranie pliku), możesz przekazać żądanieIntent
dostartService()
.Intent
określa uruchomienie usługi i przenoszenie wszelkich niezbędnych danych.Jeśli usługa została zaprojektowana z myślą o interfejsie klient-serwer, możesz ją powiązać z innym komponentem, przekazując
Intent
dobindService()
. Więcej informacji znajdziesz w przewodniku Usługi. - Przesyłanie transmisji
Komunikat to wiadomość, którą może odebrać każda aplikacja. System dostarcza różne transmisje zdarzeń systemowych, np. podczas uruchamiania systemu lub rozpoczęcia ładowania urządzenia. Komunikat możesz przekazać do innych aplikacji, przekazując kod
Intent
do interfejsusendBroadcast()
lubsendOrderedBroadcast()
.
W dalszej części tej strony wyjaśniamy, jak działają intencje i jak z nich korzystać. Więcej informacji znajdziesz w sekcjach Korzystanie z innych aplikacji i Udostępnianie treści.
Typy intencji
Istnieją 2 rodzaje intencji:
- Intencje jawne określają, który komponent aplikacji spełni intencję, określając pełną właściwość
ComponentName
. Zwykle używasz wyraźnej intencji uruchomienia komponentu w aplikacji, ponieważ znasz nazwę klasy działania lub usługi, które chcesz uruchomić. Możesz na przykład rozpocząć nową aktywność w aplikacji w odpowiedzi na działanie użytkownika lub uruchomić usługę, która pobiera plik w tle. - Intencje niejawne nie wskazują konkretnego komponentu, ale deklarują ogólne działanie, które umożliwia komponentowi z innej aplikacji. Jeśli na przykład chcesz pokazać użytkownikowi lokalizację na mapie, możesz za pomocą niejawnej prośby o wyświetlenie na mapie określonej lokalizacji w innej aplikacji, która spełnia te wymagania.
Rysunek 1 przedstawia sposób użycia intencji podczas uruchamiania działania. Gdy obiekt Intent
jawnie nadaje nazwę konkretnemu komponentowi aktywności, system natychmiast go uruchamia.
Jeśli korzystasz z intencji niejawnej, system Android znajduje odpowiedni komponent i zaczyna przez porównanie zawartości intencji z filtrami intencji zadeklarowanymi w pliku manifestu innych aplikacji na urządzeniu. Jeśli intencja pasuje do filtra intencji, system uruchomi dany komponent i dostarczy go obiektowi Intent
. Jeśli zgodnych jest wiele filtrów intencji, system wyświetla okno, w którym użytkownik może wybrać aplikację.
Filtr intencji to wyrażenie w pliku manifestu aplikacji, które określa typ intencji, które komponent chce otrzymywać. Na przykład, jeśli zadeklarujesz filtr intencji dla danej aktywności, inne aplikacje będą mogły ją bezpośrednio uruchamiać z określonym zamiarem. Jeśli nie zadeklarujesz żadnych filtrów intencji dla danej aktywności, może ona zostać uruchomiona tylko z wyraźną intencją.
Uwaga: aby mieć pewność, że aplikacja jest bezpieczna, podczas uruchamiania Service
zawsze używaj wyraźnej intencji i nie deklaruj filtrów intencji dla swoich usług. Korzystanie z niejawnej intencji uruchomienia usługi stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa zareaguje na intencję, a użytkownik nie będzie w stanie zobaczyć, która usługa się uruchamia. Począwszy od Androida 5.0 (poziom interfejsu API 21) system zgłasza wyjątek, jeśli wywołujesz bindService()
z intencją niejawną.
Tworzenie intencji
Obiekt Intent
zawiera informacje używane przez system Android do określenia, którego komponentu użyć (np. dokładna nazwa komponentu lub kategoria komponentu, który powinien otrzymać intencję), oraz informacje, których komponent adresata używa do prawidłowego wykonania działania (np. działanie i dane, na których ma wykonać działanie).
Intent
zawiera te podstawowe informacje:
- Nazwa komponentu
- Nazwa komponentu, od którego ma się rozpocząć.
Ten krok jest opcjonalny, ale to kluczowa informacja tworząca intencję wyraźna. Oznacza to, że intencja powinna być dostarczana tylko do komponentu aplikacji zdefiniowanego przez nazwę komponentu. Bez nazwy komponentu intencja jest ukryta, a system decyduje, który z nich powinien ją otrzymać, na podstawie innych informacji o intencji (takich jak działanie, dane i kategoria (jak opisano poniżej). Jeśli chcesz uruchomić konkretny komponent w aplikacji, musisz podać jego nazwę.
Uwaga: rozpoczynając pisanie
Service
, zawsze należy podać nazwę komponentu. W przeciwnym razie nie będziesz mieć pewności, która usługa odpowie na intencję, a użytkownik nie będzie mógł zobaczyć, która usługa się uruchomi.To pole obiektu
Intent
jest obiektemComponentName
, który możesz określić za pomocą pełnej i jednoznacznej nazwy klasy komponentu docelowego, w tym nazwy pakietu aplikacji, np.com.example.ExampleActivity
. Nazwę komponentu możesz ustawić za pomocąsetComponent()
,setClass()
,setClassName()
lub konstruktoraIntent
. - Działanie
- Ciąg tekstowy określający działanie ogólne do wykonania (np. wyświetl lub wybierz).
W przypadku intencji związanych z transmisją jest to działanie, które miało miejsce i jest zgłaszane. Działanie w dużej mierze decyduje o strukturze reszty intencji, a zwłaszcza informacji zawartych w danych i dodatkach.
Możesz określić własne działania do wykorzystania przez intencje w aplikacji (lub do wykorzystania przez inne aplikacje do wywoływania komponentów w aplikacji), ale zazwyczaj określa się stałe działania zdefiniowane przez klasę
Intent
lub inne klasy platformy. Oto kilka typowych działań związanych z rozpoczynaniem działania:ACTION_VIEW
- Możesz użyć tego działania w intencji
startActivity()
, jeśli masz informacje, które aktywność może wyświetlić użytkownikowi, np. zdjęcie do wyświetlenia w aplikacji z galerią lub adres do wyświetlenia w aplikacji z mapami. ACTION_SEND
- Intencje są też nazywane intencją udostępniania. Używaj jej w intencji
startActivity()
, gdy masz dane, które użytkownik może udostępnić w innej aplikacji, np. w aplikacji do poczty e-mail lub aplikacji do udostępniania społecznościowego.
Więcej stałych definiujących działania ogólne znajdziesz w dokumentacji klasy
Intent
. Inne działania są definiowane w innym miejscu w ramach platformy Androida, np. wSettings
w przypadku działań, które otwierają określone ekrany w aplikacji Ustawienia w systemie.Działanie intencji możesz określić za pomocą konstruktora
Intent
lubsetAction()
.Jeśli definiujesz własne działania, pamiętaj, aby dodać prefiks nazwy pakietu aplikacji, jak pokazano w tym przykładzie:
Kotlin
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
Java
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
- Dane
- Identyfikator URI (obiekt
Uri
) odwołujący się do danych, na których ma zostać wykonana czynność, lub typ MIME tych danych. Typ dostarczanych danych zależy zazwyczaj od działania intencji. Jeśli na przykład działaniem jestACTION_EDIT
, dane powinny zawierać identyfikator URI dokumentu do edycji.Podczas tworzenia intencji często trzeba podać typ danych (typ MIME) oprócz identyfikatora URI. Na przykład działanie, które może wyświetlać obrazy, prawdopodobnie nie pozwoli na odtworzenie pliku audio, chociaż formaty URI mogą być podobne. Określenie typu MIME danych pomaga systemowi Android znaleźć komponent, który najlepiej spełnia Twoją intencję. Typ MIME można jednak czasem określić na podstawie identyfikatora URI, zwłaszcza gdy dane są związane z identyfikatorem URI
content:
. Identyfikator URIcontent:
wskazuje, że dane znajdują się na urządzeniu i są kontrolowane przezContentProvider
, co sprawia, że typ MIME danych jest widoczny dla systemu.Aby ustawić tylko identyfikator URI danych, wywołaj
setData()
. Aby ustawić tylko typ MIME, wywołajsetType()
. W razie potrzeby możesz ustawić obie te wartości za pomocą funkcjisetDataAndType()
.Uwaga: jeśli chcesz ustawić zarówno identyfikator URI, jak i typ MIME, nie wywołaj funkcji
setData()
isetType()
, ponieważ obie te wartości zerują wartości drugiego adresu. Do ustawiania zarówno identyfikatora URI, jak i typu MIME zawsze używaj poleceniasetDataAndType()
. - Category
- Ciąg tekstowy zawierający dodatkowe informacje o rodzaju komponentu, który powinien obsługiwać intencję. W intencji można umieścić dowolną liczbę opisów kategorii, ale większość intencji nie wymaga kategorii.
Oto kilka typowych kategorii:
CATEGORY_BROWSABLE
- Docelowa aktywność pozwala się uruchomić przez przeglądarkę internetową w celu wyświetlenia danych, do których odwołuje się link, na przykład obrazu lub e-maila.
CATEGORY_LAUNCHER
- Aktywność to początkowa aktywność związana z zadaniem i jest wyświetlana w menu z aplikacjami w systemie.
Pełną listę kategorii znajdziesz w opisie klasy
Intent
.Kategorię możesz określić za pomocą parametru
addCategory()
.
Wymienione powyżej właściwości (nazwa komponentu, działanie, dane i kategoria) reprezentują cechy definiujące intencję. Odczytując te właściwości, Android może zdecydować, który komponent aplikacji powinien uruchomić. Intencja może jednak zawierać dodatkowe informacje, które nie mają wpływu na sposób jego interpretacji w komponencie aplikacji. Intencja może też zawierać takie informacje:
- Dodatki
- Pary klucz-wartość zawierające dodatkowe informacje wymagane do wykonania żądanego działania.
Tak jak niektóre działania wykorzystują określone rodzaje identyfikatorów URI danych, niektóre z nich wymagają też specjalnych elementów dodatkowych.
Możesz dodawać dodatkowe dane za pomocą różnych metod
putExtra()
, z których każda akceptuje 2 parametry: nazwę klucza i wartość. Możesz też utworzyć obiektBundle
ze wszystkimi dodatkowymi danymi, a potem wstawićBundle
wIntent
przy użyciu poleceniaputExtras()
.Podczas tworzenia intencji wysłania e-maila za pomocą
ACTION_SEND
możesz na przykład określić adresata to za pomocą kluczaEXTRA_EMAIL
, a temat – kluczemEXTRA_SUBJECT
.Klasa
Intent
określa wiele stałychEXTRA_*
na potrzeby standardowych typów danych. Jeśli musisz zadeklarować własne dodatkowe klucze (na potrzeby intencji, które odbiera aplikacja), pamiętaj, aby dodać przedrostek nazwy pakietu aplikacji, tak jak w tym przykładzie:Kotlin
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
Java
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Uwaga: nie używaj danych
Parcelable
aniSerializable
do wysyłania intencji, którą spodziewasz się otrzymać inna aplikacja. Jeśli aplikacja próbuje uzyskać dostęp do danych w obiekcieBundle
, ale nie ma dostępu do klasy spakowanej lub zserializowanej, system zgłaszaRuntimeException
. - Flagi
- Flagi zdefiniowane są w klasie
Intent
, które funkcjonują jako metadane intencji. Flagi mogą informować system Android, jak uruchomić działanie (np. do którego zadania powinna ona należeć) i jak postępować z nią po jej uruchomieniu (np. czy należy ona do listy ostatnich aktywności).Więcej informacji znajdziesz w opisie metody
setFlags()
.
Przykład jednoznacznej intencji
Intencja bezpośrednia to taka, za pomocą której uruchamiasz określony komponent aplikacji, np. konkretną aktywność lub usługę. Aby utworzyć intencję bezpośrednią, podaj nazwę komponentu obiektu Intent
– pozostałe właściwości intencji są opcjonalne.
Jeśli np. utworzysz w aplikacji usługę o nazwie DownloadService
, która służy do pobierania pliku z internetu, możesz zacząć ją od tego kodu:
Kotlin
// Executed in an Activity, so 'this' is theContext
// 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 theContext
// The fileUrl is a string URL, such as "http://www.example.com/image.png" Intent downloadIntent = new Intent(this, DownloadService.class); downloadIntent.setData(Uri.parse
(fileUrl)); startService(downloadIntent);
Konstruktor Intent(Context, Class)
dostarcza aplikację Context
, a komponent – obiekt Class
. Dlatego ta intencja jawnie uruchamia klasę DownloadService
w aplikacji.
Więcej informacji o tworzeniu i uruchamianiu usługi znajdziesz w przewodniku Usługi.
Przykład niejawnej intencji
Intencja niejawna określa działanie, które może wywołać dowolną aplikację na urządzeniu, która może je wykonać. Intencje niejawne są przydatne, gdy aplikacja nie może wykonać danego działania, ale inne aplikacje prawdopodobnie mają taką możliwość i chcesz, aby użytkownik sam wybierał aplikację.
Jeśli na przykład masz treści, które chcesz udostępnić innym osobom, utwórz intencję za pomocą działania ACTION_SEND
i dodaj elementy określające treść do udostępnienia. Jeśli wywołujesz startActivity()
w związku z tym zamiarem, użytkownik może wybrać aplikację, za pomocą której chce udostępnić treści.
Kotlin
// Create the text message with a string. val sendIntent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, textMessage) type = "text/plain" } // Try to invoke the intent. try { startActivity(sendIntent) } catch (e: ActivityNotFoundException) { // Define what your app should do if no activity can handle the intent. }
Java
// Create the text message with a string. Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); sendIntent.setType("text/plain"); // Try to invoke the intent. try { startActivity(sendIntent); } catch (ActivityNotFoundException e) { // Define what your app should do if no activity can handle the intent. }
Po wywołaniu funkcji startActivity()
system sprawdza wszystkie zainstalowane aplikacje, aby określić, które z nich mogą obsłużyć tego rodzaju intencję (zamiar z działaniem ACTION_SEND
, który przenosi dane typu „text/plain”). Jeśli tylko jedna aplikacja może obsłużyć daną aplikację, zostanie ona otwarta natychmiast i zgodzi się na jej działanie. Jeśli żadna inna aplikacja nie jest w stanie go obsłużyć, może zarejestrować ActivityNotFoundException
, który występuje. Jeśli wiele działań zaakceptuje intencję, system wyświetli okno, takie jak na Rysunku 2, aby użytkownik mógł wybrać aplikację.
Więcej informacji o uruchamianiu innych aplikacji znajdziesz też w przewodniku o kierowaniu użytkownika do innej aplikacji.
Wymuszanie wyboru aplikacji
Jeśli na Twoją pośrednią intencję odpowiada więcej niż jedna aplikacja, użytkownik może wybrać aplikację, której chce użyć, i ustawić ją jako domyślną dla danego działania. Możliwość wyboru wartości domyślnej jest przydatna, gdy użytkownik prawdopodobnie chce używać tej samej aplikacji za każdym razem, np. przy otwieraniu strony internetowej (użytkownicy często preferują tylko jedną przeglądarkę).
Jeśli jednak wiele aplikacji może odpowiadać na intencję, a użytkownik może za każdym razem używać innej aplikacji, wyraźnie pokaż okno wyboru. Okno wyboru zawiera prośbę o wybranie aplikacji, której ma używać działanie (użytkownik nie może wybrać domyślnej aplikacji dla tego działania). Jeśli na przykład aplikacja wykonuje „udostępnianie” przy użyciu działania ACTION_SEND
, użytkownicy mogą zechcieć udostępniać treści za pomocą innej aplikacji zależnie od ich obecnej sytuacji, dlatego zawsze korzystaj z okna wyboru, jak pokazano na Rysunku 2.
Aby pokazać selektor, utwórz dokument Intent
za pomocą metody createChooser()
i przekaż go do startActivity()
, jak pokazano w poniższym przykładzie.
W tym przykładzie wyświetla się okno z listą aplikacji, które reagują na intencję przekazaną do metody createChooser()
i używają podanego tekstu jako tytułu okna.
Kotlin
val sendIntent = Intent(Intent.ACTION_SEND) ... // Always use string resources for UI text. // This says something like "Share this photo with" val title: String = resources.getString(R.string.chooser_title) // Create intent to show the chooser dialog val chooser: Intent = Intent.createChooser(sendIntent, title) // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(packageManager) != null) { startActivity(chooser) }
Java
Intent sendIntent = new Intent(Intent.ACTION_SEND); ... // Always use string resources for UI text. // This says something like "Share this photo with" String title = getResources().getString(R.string.chooser_title); // Create intent to show the chooser dialog Intent chooser = Intent.createChooser(sendIntent, title); // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(chooser); }
Wykrywanie uruchamiania niebezpiecznych intencji
Aplikacja może uruchamiać intencje dotyczące przechodzenia między komponentami w samej aplikacji lub wykonywania działania w imieniu innej aplikacji. Aby zwiększyć bezpieczeństwo platformy, Android 12 (poziom interfejsu API 31) i nowszy udostępnia funkcję debugowania, która ostrzega, jeśli aplikacja wykonuje niebezpieczne uruchomienie intencji. Aplikacja może na przykład wykonywać niebezpieczne uruchamianie intencji zagnieżdżonej, która jest przekazywana jako dodatkowa intencja w innej intencji.
Jeśli aplikacja wykona obie te czynności, system wykryje uruchomienie niebezpiecznej intencji i dochodzi do naruszenia StrictMode:
- Aplikacja wyodrębnia zagnieżdżoną intencję od dodatków w dostarczonej intencji.
- Aplikacja natychmiast uruchamia komponent aplikacji, używając tej zagnieżdżonej intencji, np. przekazuje intencję do
startActivity()
,startService()
lubbindService()
.
Więcej informacji o tym, jak rozpoznać taką sytuację i wprowadzić zmiany w aplikacji, znajdziesz w poście na blogu na temat intencji na Androidzie Nesting Intents w serwisie Medium.
Sprawdzanie, czy nie uruchomiono niebezpiecznych intencji
Aby sprawdzić, czy w aplikacji nie są uruchamiane niebezpieczne intencje, wywołaj detectUnsafeIntentLaunch()
podczas konfigurowania elementu VmPolicy
, jak pokazano w tym fragmencie kodu. Jeśli aplikacja wykryje naruszenie zasad StrictMode, możesz zatrzymać wykonywanie aplikacji, aby chronić potencjalnie poufne informacje.
Kotlin
fun onCreate() { StrictMode.setVmPolicy(VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()) }
Java
protected void onCreate() { StrictMode.setVmPolicy(new VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()); }
Odpowiedzialne korzystanie z intencji
Aby zminimalizować ryzyko uruchomienia niebezpiecznej intencji i naruszenia zasad StrictMode, postępuj zgodnie z tymi sprawdzonymi metodami.
Skopiuj w obrębie intencji tylko niezbędne elementy dodatkowe, a następnie przeprowadź proces oczyszczania i weryfikacji. Aplikacja może kopiować dodatki z jednej intencji do drugiej, co służy do uruchamiania nowego komponentu. Dzieje się tak, gdy aplikacja wywołuje metodę putExtras(Intent)
lub putExtras(Bundle)
.
Jeśli aplikacja wykonuje jedną z tych operacji, skopiuj tylko te dodatkowe, których oczekuje komponent odbierający. Jeśli druga intencja (która otrzymuje kopię) uruchomi komponent, który nie jest wyeksportowany, oczyść i zweryfikuj dodatki, zanim skopiujesz je do intencji uruchamiającej komponent.
Nie eksportuj komponentów aplikacji bez potrzeby. Jeśli np. zamierzasz uruchamiać komponent aplikacji za pomocą wewnętrznej intencji zagnieżdżonej, ustaw atrybut android:exported
tego komponentu na false
.
Użyj obiektu PendingIntent
zamiast intencji zagnieżdżonej. Dzięki temu, gdy inna aplikacja wyodrębnia PendingIntent
z zawartego w niej komponentu Intent
, może ona uruchomić PendingIntent
, korzystając z tożsamości Twojej aplikacji. Dzięki tej konfiguracji druga aplikacja może bezpiecznie uruchamiać w Twojej aplikacji dowolny komponent, w tym niewyeksportowany.
Diagram na rys. 2 pokazuje, w jaki sposób system przekazuje elementy sterujące aplikacją (klienta) do innej aplikacji (usługi) i z powrotem do aplikacji:
- Aplikacja tworzy intencję, która wywołuje działanie w innej aplikacji. W tej intencji dodajesz jako dodatkowy obiekt
PendingIntent
. Ta intencja oczekująca będzie wywoływać komponent w aplikacji. Ten komponent nie jest eksportowany. - Po otrzymaniu intencji aplikacji druga aplikacja wyodrębnia zagnieżdżony obiekt
PendingIntent
. - Druga aplikacja wywołuje metodę
send()
w obiekciePendingIntent
. - Po przekazaniu kontroli z powrotem do aplikacji system wywołuje oczekującą intencję, korzystając z kontekstu aplikacji.
Rysunek 2. Diagram komunikacji między aplikacjami podczas korzystania z zagnieżdżonej intencji oczekującej.
Odbieranie intencji ogólnej
Aby określić, jakie intencje niejawne może otrzymywać Twoja aplikacja, zadeklaruj co najmniej 1 filtr intencji dla każdego komponentu aplikacji za pomocą elementu <intent-filter>
w pliku manifestu.
Każdy filtr intencji określa typ akceptowanych intencji na podstawie działania, danych i kategorii intencji. System realizuje niejawną intencję w komponencie aplikacji tylko wtedy, gdy może ona przejść przez jeden z filtrów intencji.
Uwaga: intencja bezpośrednia jest zawsze dostarczana do celu, niezależnie od filtrów intencji deklarowanych przez komponent.
Komponent aplikacji powinien zadeklarować osobne filtry dla każdego unikalnego zadania, które może wykonywać.
Na przykład jedna aktywność w aplikacji z galerią obrazów może mieć 2 filtry: jeden do wyświetlania obrazu i drugi do edycji obrazu. Po rozpoczęciu aktywności sprawdza obiekt Intent
i na podstawie informacji w elemencie Intent
określa, jak ma działać (np. w celu wyświetlenia opcji edytora).
Każdy filtr intencji jest definiowany przez element <intent-filter>
w pliku manifestu aplikacji umieszczony w odpowiednim komponencie aplikacji (np. elemencie <activity>
).
W każdym komponencie aplikacji, który zawiera element <intent-filter>
, ustaw wartość android:exported
.
Ten atrybut wskazuje, czy komponent aplikacji jest dostępny dla innych aplikacji. W niektórych sytuacjach, np. w przypadku działań, których filtry intencji obejmują kategorię LAUNCHER
, warto ustawić wartość tego atrybutu na true
. W przeciwnym razie bezpieczniej jest ustawić ten atrybut na false
.
Ostrzeżenie: jeśli aktywność, usługa lub odbiornik w aplikacji używa filtrów intencji i nie ustawi wyraźnie wartości dla parametru android:exported
, aplikacji nie można zainstalować na urządzeniu z Androidem 12 lub nowszym.
W obrębie <intent-filter>
możesz określić typ intencji do zaakceptowania, korzystając z co najmniej 1 z tych 3 elementów:
<action>
- Deklaruje zaakceptowane działanie intencji w atrybucie
name
. Wartość musi być dosłowną wartością ciągu działania, a nie stałą klasy. <data>
- Deklaruje typ akceptowanych danych, używając co najmniej jednego atrybutu określającego różne aspekty identyfikatora URI danych (
scheme
,host
,port
,path
) i typu MIME. <category>
- Deklaruje zaakceptowaną kategorię intencji w atrybucie
name
. Wartość musi być dosłowną wartością ciągu działania, a nie stałą klasy.Uwaga: aby otrzymywać intencje niejawne, musisz uwzględnić kategorię
CATEGORY_DEFAULT
w filtrze intencji. MetodystartActivity()
istartActivityForResult()
traktują wszystkie intencje tak, jakby zadeklarowały kategorięCATEGORY_DEFAULT
. Jeśli nie zadeklarujesz tej kategorii w filtrze intencji, do Twojej aktywności nie będą trafiały żadne niejawne intencje.
Na przykład oto deklaracja aktywności z filtrem intencji, która odbiera intencję ACTION_SEND
, gdy typem danych jest tekst:
<activity android:name="ShareActivity" android:exported="false"> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity>
Możesz utworzyć filtr, który zawiera więcej niż 1 wystąpienie <action>
, <data>
lub <category>
.
Jeśli to zrobisz, musisz upewnić się, że komponent obsługuje wszystkie kombinacje tych elementów filtra.
Jeśli chcesz obsługiwać wiele rodzajów intencji, ale tylko w przypadku określonych kombinacji działań, danych i typów kategorii, musisz utworzyć kilka filtrów intencji.
Intencja niejawna jest testowana pod kątem filtra przez porównanie intencji z każdym z 3 elementów. Aby intencja mogła zostać wyświetlona do komponentu, musi przejść wszystkie 3 testy. Jeśli nie uda się dopasować któregoś z nich, Android nie dostarczy intencji do komponentu. Ponieważ jednak komponent może mieć wiele filtrów intencji, intencja, która nie przejdzie przez jeden z tych filtrów, może przedostać się przez inny filtr. Więcej informacji o sposobie, w jaki system rozpoznaje intencje, znajdziesz w poniższej sekcji o rozwiązaniu intencji.
Uwaga: użycie filtra intencji nie jest bezpiecznym sposobem, aby uniemożliwić innym aplikacjom uruchamianie komponentów. Choć filtry intencji ograniczają reagowanie komponentu tylko na określone rodzaje niejawnych intencji, to inna aplikacja może uruchomić komponent aplikacji, korzystając z intencji jawnej, jeśli to deweloper określi nazwy komponentów.
Jeśli zależy Ci, aby tylko Twoja aplikacja mogła uruchamiać jeden z komponentów, nie deklaruj filtrów intencji w pliku manifestu. Zamiast tego ustaw wartość atrybutu exported
na "false"
dla tego komponentu.
Podobnie, aby uniknąć przypadkowego uruchomienia polecenia Service
innej aplikacji, zawsze używaj wyraźnej intencji uruchomienia własnej usługi.
Uwaga: w przypadku wszystkich aktywności filtry intencji musisz zadeklarować w pliku manifestu.
Filtry odbiorników można jednak rejestrować dynamicznie, wywołując metodę registerReceiver()
. Następnie możesz wyrejestrować odbiorcę za pomocą unregisterReceiver()
. Dzięki temu aplikacja może nasłuchiwać określonych komunikatów tylko przez określony czas, gdy jest uruchomiona.
Przykładowe filtry
Aby zademonstrować niektóre działania filtra intencji, oto przykład z pliku manifestu aplikacji do udostępniania społecznościowego:
<activity android:name="MainActivity" android:exported="true"> <!-- This activity is the main entry, should appear in app launcher --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="ShareActivity" android:exported="false"> <!-- This activity handles "SEND" actions with text data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.google.panorama360+jpg"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter> </activity>
Pierwszym działaniem (MainActivity
) jest główny punkt wejścia aplikacji – działanie uruchamiane, gdy użytkownik po raz pierwszy uruchomi aplikację z ikoną programu uruchamiającego:
- Działanie
ACTION_MAIN
wskazuje, że jest to główny punkt wejścia i nie oczekuje żadnych danych intencji. - Kategoria
CATEGORY_LAUNCHER
oznacza, że ikona tej aktywności powinna znajdować się w systemie uruchamiającym aplikacje. Jeśli element<activity>
nie określa ikony z wartościąicon
, system użyje ikony z elementu<application>
.
Aby aktywność była widoczna w Menu z aplikacjami, muszą one być sparowane ze sobą.
Druga czynność, ShareActivity
, ma ułatwiać udostępnianie tekstu i multimediów. Użytkownicy mogą otworzyć tę aktywność, przechodząc do niej z poziomu MainActivity
, ale mogą też wpisywać ShareActivity
bezpośrednio z innej aplikacji, która wysyła niejawną intencję pasującą do jednego z 2 filtrów intencji.
Uwaga: typ MIME application/vnd.google.panorama360+jpg
to specjalny typ danych określający zdjęcia panoramiczne, które obsługują interfejsy API Google Panorama.
Dopasuj intencje do filtrów intencji innych aplikacji
Jeśli inna aplikacja jest kierowana na Androida 13 (poziom interfejsu API 33) lub nowszego, może obsługiwać intencje Twojej aplikacji tylko wtedy, gdy ta intencja odpowiada działaniom i kategoriom elementu <intent-filter>
w tej aplikacji. Jeśli system nie znajdzie dopasowania, zgłasza ActivityNotFoundException
.
Aplikacja wysyłająca musi obsługiwać ten wyjątek.
Podobnie jeśli zaktualizujesz aplikację w taki sposób, aby była kierowana na Androida w wersji 13 lub nowszej, wszystkie intencje pochodzące z aplikacji zewnętrznych będą dostarczane do wyeksportowanego komponentu aplikacji tylko wtedy, gdy ta intencja odpowiada działaniom i kategoriom elementu <intent-filter>
deklarowanych przez aplikację. To zachowanie występuje niezależnie od docelowej wersji pakietu SDK aplikacji wysyłającej.
Dopasowywanie intencji nie jest wymuszane w tych przypadkach:
- Intencje dostarczone do komponentów, które nie deklarują żadnych filtrów intencji.
- Intencje pochodzące z tej samej aplikacji.
- intencje pochodzące z systemu, czyli intencje wysyłane z „identyfikatora UID systemu” (uid=1000). Aplikacje systemowe to m.in.
system_server
i aplikacje z ustawieniemandroid:sharedUserId
naandroid.uid.system
. - Intencje pochodzące z katalogu głównego
Dowiedz się więcej o dopasowywaniu zamiarów.
Korzystanie z intencji oczekującej
Obiekt PendingIntent
otacza obiekt Intent
. Głównym celem PendingIntent
jest udzielanie zagranicznym aplikacji pozwolenia na używanie zawartego w niej elementu Intent
w taki sposób, jakby były wykonywane przez Twój proces aplikacji.
Główne przypadki użycia intencji oczekującej to:
- Zadeklarowanie intencji do wykonania, gdy użytkownik wykona działanie z użyciem powiadomienia (
NotificationManager
systemu Android wykonuje polecenieIntent
). - Zadeklarowanie intencji do wykonania, gdy użytkownik wykona działanie za pomocą Twojego widżetu aplikacji (aplikacja na ekranie głównym uruchamia
Intent
). - Zadeklarowanie intencji do wykonania o określonej godzinie w przyszłości (
AlarmManager
systemu Android wykonuje polecenieIntent
).
Każdy obiekt Intent
jest przeznaczony do obsługi przez konkretny typ komponentu aplikacji (Activity
, Service
lub BroadcastReceiver
), więc też PendingIntent
należy utworzyć z tym samym uwzględnieniem. Gdy używasz intencji oczekującej, aplikacja nie wykonuje intencji za pomocą wywołania, np. startActivity()
. Zamiast tego musisz zadeklarować odpowiedni typ komponentu podczas tworzenia komponentu PendingIntent
, wywołując odpowiednią metodę twórcy:
PendingIntent.getActivity()
dlaIntent
, który rozpoczyna działanieActivity
.PendingIntent.getService()
dlaIntent
, który rozpoczyna działanieService
.PendingIntent.getBroadcast()
dlaIntent
, który rozpoczyna działanieBroadcastReceiver
.
Jeśli Twoja aplikacja nie otrzymuje oczekujących intencji z innych aplikacji, powyższe metody tworzenia PendingIntent
to prawdopodobnie jedyne metody PendingIntent
, których będziesz potrzebować.
Każda metoda pobiera bieżącą aplikację Context
, element Intent
, który chcesz zapakować, i co najmniej 1 flagę określającą sposób użycia intencji (np. czy intencję można użyć więcej niż raz).
Więcej informacji o używaniu intencji oczekujących znajdziesz w dokumentacji poszczególnych przypadków użycia, np. w przewodnikach po interfejsie API Powiadomienia i Widżetów aplikacji.
Określ zmienność
Jeśli Twoja aplikacja jest kierowana na Androida 12 lub nowszego, musisz określić zmienność każdego tworzonego przez nią obiektu PendingIntent
. Aby zadeklarować, że dany obiekt PendingIntent
jest zmienny lub stały, użyj odpowiednio flagi PendingIntent.FLAG_MUTABLE
lub PendingIntent.FLAG_IMMUTABLE
.
Jeśli aplikacja spróbuje utworzyć obiekt PendingIntent
bez ustawienia flagi zmienności, system zgłosi IllegalArgumentException
, a w narzędziu Logcat pojawi się następujący komunikat:
PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.
W miarę możliwości twórz stałe oczekujące intencje
W większości przypadków aplikacja powinna tworzyć stałe obiekty PendingIntent
, jak pokazano w tym fragmencie kodu. Jeśli obiektu PendingIntent
nie można zmienić, inne aplikacje nie mogą modyfikować intencji, aby dostosować wynik jej wywołania.
Kotlin
val pendingIntent = PendingIntent.getActivity(applicationContext, REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE)
Java
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE);
Jednak w niektórych przypadkach użycia wymagane są jednak zmienne obiekty PendingIntent
:
- Obsługuje działania związane z odpowiedzią w powiadomieniach bezpośrednio. Bezpośrednia odpowiedź wymaga zmiany danych klipu w obiekcie PendingIntent, który jest z nią powiązany. Zwykle o taką zmianę prosisz, przekazując do metody
fillIn()
flagęFILL_IN_CLIP_DATA
. - Wiążę powiadomienia z platformą Android Auto za pomocą wystąpień
CarAppExtender
. - Umieszczanie wątków w dymkach przy użyciu wystąpień
PendingIntent
. Zmienny obiektPendingIntent
umożliwia systemowi stosowanie prawidłowych flag, np.FLAG_ACTIVITY_MULTIPLE_TASK
iFLAG_ACTIVITY_NEW_DOCUMENT
. - Żądanie informacji o lokalizacji urządzenia przez wywołanie
requestLocationUpdates()
lub podobne interfejsy API. zmienny obiektPendingIntent
pozwala systemowi dodawać elementy dodatkowe, które reprezentują zdarzenia cyklu życia lokalizacji. Zdarzenia te obejmują zmianę lokalizacji oraz udostępnienie dostawcy. - Planowanie alarmów w aplikacji
AlarmManager
. Zmienny obiektPendingIntent
pozwala systemowi dodać dodatkową intencjęEXTRA_ALARM_COUNT
. Ta wartość określa, ile razy został aktywowany powtarzający się alarm. Zawierając ten dodatkowy element, intencja może dokładnie powiadamiać aplikację o tym, czy wielokrotnie został aktywowany alarm, np. gdy urządzenie było uśpione.
Jeśli aplikacja tworzy zmienny obiekt PendingIntent
, zdecydowanie zalecamy użycie jawnej intencji i wypełnienie pola ComponentName
. Dzięki temu za każdym razem, gdy inna aplikacja wywołuje PendingIntent
i przekazuje do niej kontrolę, zawsze uruchamia się ten sam komponent w Twojej aplikacji.
Używanie intencji wyraźnych w ramach oczekujących intencji
Aby lepiej określić, jak inne aplikacje mogą korzystać z intencji oczekujących w Twojej aplikacji, zawsze okładaj oczekującą intencję z jawną intencją. Aby w pełni wykorzystać tę sprawdzoną metodę, wykonaj te czynności:
- Sprawdź, czy działanie, pakiet i pola komponentu intencji podstawowej są ustawione.
-
Aby utworzyć intencje oczekujące, użyj narzędzia
FLAG_IMMUTABLE
dodanego w Androidzie 6.0 (poziom interfejsu API 23). Ta flaga uniemożliwia aplikacjom otrzymującym polePendingIntent
wypełnianie niewypełnionych właściwości. JeśliminSdkVersion
Twojej aplikacji to22
lub mniej, możesz zapewnić bezpieczeństwo i zgodność, korzystając z tego kodu:if (Build.VERSION.SDK_INT >= 23) { // Create a PendingIntent using FLAG_IMMUTABLE. } else { // Existing code that creates a PendingIntent. }
Rozwiązanie intencji
Gdy system otrzyma pośrednią intencję rozpoczęcia działania, wyszukuje dla niej najlepszą aktywność, porównując ją z filtrami intencji na podstawie 3 aspektów:
- Action (Działanie).
- Dane (identyfikator URI i typ danych).
- Category [Kategoria]:
W kolejnych sekcjach opisujemy, w jaki sposób intencje są dopasowywane do odpowiednich komponentów zgodnie z deklaracją filtra intencji w pliku manifestu aplikacji.
Test działania
Aby określić akceptowane działania intencji, filtr intencji może zadeklarować zero lub więcej elementów <action>
. W tym przykładzie:
<intent-filter> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.VIEW" /> ... </intent-filter>
Aby przejść ten filtr, działanie określone w Intent
musi być zgodne z jednym z działań wymienionych w filtrze.
Jeśli filtr nie zawiera żadnych działań, nie ma nic do dopasowania intencji, więc wszystkie intencje zakończą się niepowodzeniem. Jeśli jednak Intent
nie określa działania, test zostanie zakończony, o ile filtr zawiera co najmniej jedno działanie.
Test kategorii
Aby określić akceptowane kategorie intencji, filtr intencji może zadeklarować zero lub więcej elementów <category>
. W tym przykładzie:
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> ... </intent-filter>
Aby intencja zaliczyła się do testu kategorii, każda kategoria w elemencie Intent
musi pasować do kategorii w filtrze. Odwrotność nie jest konieczna – filtr intencji może zadeklarować więcej kategorii niż określono w zasadzie Intent
i tak zostanie przesłany tag Intent
. Dlatego intencja bez kategorii zawsze przechodzi test, niezależnie od tego, jakie kategorie są zadeklarowane w filtrze.
Uwaga: Android automatycznie stosuje kategorię CATEGORY_DEFAULT
do wszystkich intencji niejawnych przekazanych do startActivity()
i startActivityForResult()
.
Jeśli chcesz, aby Twoja aktywność otrzymywała intencje niejawne, musi zawierać kategorię dla elementu "android.intent.category.DEFAULT"
w filtrach intencji, tak jak w poprzednim przykładzie <intent-filter>
.
Test danych
Aby określić akceptowane dane intencji, filtr intencji może zadeklarować zero lub więcej elementów <data>
. W tym przykładzie:
<intent-filter> <data android:mimeType="video/mpeg" android:scheme="http" ... /> <data android:mimeType="audio/mpeg" android:scheme="http" ... /> ... </intent-filter>
Każdy element <data>
może określać strukturę identyfikatora URI i typ danych (typ mediów MIME).
Każda część identyfikatora URI jest osobnym atrybutem: scheme
, host
, port
i path
:
<scheme>://<host>:<port>/<path>
Przykład poniżej pokazuje możliwe wartości tych atrybutów:
content://com.example.project:200/folder/subfolder/etc
W tym identyfikatorze URI schemat to content
, host to com.example.project
, port to 200
, a ścieżka to folder/subfolder/etc
.
Każdy z tych atrybutów w elemencie <data>
jest opcjonalny, ale występują zależności liniowe:
- Jeśli schemat nie zostanie określony, host będzie ignorowany.
- Jeśli host nie został określony, port jest ignorowany.
- Jeśli nie podasz zarówno schematu, jak i hosta, ścieżka będzie ignorowana.
Jeśli identyfikator URI w intencji jest porównywany w filtrze ze specyfikacją identyfikatora URI, jest on porównywany tylko z częściami identyfikatora URI uwzględnionymi w filtrze. Na przykład:
- Jeśli filtr określa tylko schemat, do niego pasują wszystkie identyfikatory URI z tym schematem.
- Jeśli filtr określa schemat i uprawnienie, ale nie ma ścieżki, wszystkie identyfikatory URI o tym samym schemacie i uprawnieniu przechodzą filtr niezależnie od swoich ścieżek.
- Jeśli filtr określa schemat, uprawnienia i ścieżkę, pomijają go tylko identyfikatory URI o tym samym schemacie, uprawnieniach i ścieżce.
Uwaga: specyfikacja ścieżki może zawierać symbol wieloznaczny (*), aby wymagać tylko częściowego dopasowania nazwy ścieżki.
Test danych porównuje identyfikator URI i typ MIME w intencji z identyfikatorem URI i typem MIME określonym w filtrze. Zasady są następujące:
- Intencja, która nie zawiera identyfikatora URI ani MIME, nie przechodzi testu tylko wtedy, gdy filtr nie zawiera żadnych identyfikatorów URI ani typów MIME.
- Intencja, która zawiera identyfikator URI, ale nie ma żadnego typu MIME (jawnego ani nie można go wywnioskować z identyfikatora URI), przechodzi test tylko wtedy, gdy jego identyfikator URI pasuje do formatu identyfikatora URI filtra, a filtr nie określa typu MIME.
- Intencja zawierająca typ MIME, ale nie identyfikator URI, przechodzi test tylko wtedy, gdy filtr zawiera ten sam typ MIME i nie określa formatu identyfikatora URI.
- Intencja, która zawiera zarówno identyfikator URI, jak i typ MIME (jawny lub wywnioskowany z identyfikatora URI), przechodzi tę część testu tylko wtedy, gdy pasuje on do wymienionego w filtrze. Przechodzi część testu o identyfikatorze URI, jeśli jej identyfikator URI pasuje do identyfikatora URI w filtrze albo gdy określony jest identyfikator URI
content:
lubfile:
, a filtr nie określa tego identyfikatora. Inaczej mówiąc, przyjmuje się, że komponent obsługuje danecontent:
ifile:
, jeśli jego filtr zawiera tylko typ MIME.
Uwaga: jeśli intencja określa identyfikator URI lub typ MIME, test danych zakończy się niepowodzeniem, jeśli w elemencie <intent-filter>
nie będzie elementów <data>
.
Ostatnia reguła (d) odzwierciedla oczekiwanie, że komponenty będą mogły pobierać dane lokalne od pliku lub dostawcy treści.
Dlatego ich filtry mogą uwzględniać tylko typ danych i nie muszą wyraźnie nazywać schematów content:
ani file:
.
W poniższym przykładzie element <data>
informuje Androida, że komponent może pobrać dane obrazu z dostawcy treści i je wyświetlić:
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
Filtry, które określają typ danych, ale nie identyfikator URI, są prawdopodobnie najczęściej stosowane, ponieważ większość dostępnych danych jest rozpowszechniana przez dostawców treści.
Kolejna często używana konfiguracja to filtr ze schematem i typem danych. Na przykład element <data>
taki jak poniżej informuje Androida, że może on pobierać dane wideo z sieci, by wykonać działanie:
<intent-filter> <data android:scheme="http" android:mimeType="video/*" /> ... </intent-filter>
Dopasowywanie intencji
Intencje są dopasowywane do filtrów intencji, aby nie tylko odkryć komponent docelowy do aktywowania, ale także dowiedzieć się czegoś o zbiorze komponentów na urządzeniu. Na przykład aplikacja Home wypełnia menu z aplikacjami, znajdując wszystkie aktywności z filtrami intencji, które określają działanie ACTION_MAIN
i kategorię CATEGORY_LAUNCHER
.
Dopasowanie jest skuteczne tylko wtedy, gdy działania i kategorie w intencji są zgodne z filtrem, zgodnie z opisem w dokumentacji klasy IntentFilter
.
Aplikacja może korzystać z dopasowywania intencji w sposób podobny do tego, co robi aplikacja Home.
PackageManager
ma zestaw metod query...()
, które zwracają wszystkie komponenty, które mogą zaakceptować konkretną intencję, i podobną serię metod resolve...()
, które określają, który komponent najlepiej odpowiada na intencję. Na przykład queryIntentActivities()
zwraca listę wszystkich działań, które mogą wykonać intencję przekazaną jako argument, a queryIntentServices()
zwraca podobną listę usług.
Żadna z nich nie aktywuje komponentów, a jedynie wyświetla te, które mogą zareagować. Istnieje podobna metoda (queryBroadcastReceivers()
) dla odbiorników.