Un Intent
è un oggetto di messaggistica che puoi utilizzare per richiedere un'azione da un altro componente dell'app.
Sebbene gli intent facilitino la comunicazione tra i componenti in diversi modi, esistono tre
casi d'uso fondamentali:
- Avviare un'attività
Un
Activity
rappresenta una singola schermata in un'app. Puoi avviare una nuova istanza di unActivity
passando unIntent
astartActivity()
. L'elementoIntent
descrive l'attività da avviare e trasporta tutti i dati necessari.Se vuoi ricevere un risultato dall'attività al termine, chiama
startActivityForResult()
. La tua attività riceve il risultato come oggettoIntent
separato nel callbackonActivityResult()
dell'attività. Per ulteriori informazioni, consulta la guida Attività. - Avvio di un servizio
Un
Service
è un componente che esegue operazioni in background senza un'interfaccia utente. Con Android 5.0 (livello API 21) e versioni successive, puoi avviare un servizio conJobScheduler
. Per ulteriori informazioni suJobScheduler
, consulta la relativaAPI-reference documentation
.Per le versioni precedenti ad Android 5.0 (livello API 21), puoi avviare un servizio utilizzando i metodi della classe
Service
. Puoi avviare un servizio per eseguire un'operazione una tantum (ad esempio il download di un file) passando unIntent
astartService()
.Intent
descrive il servizio per avviare e trasporta tutti i dati necessari.Se il servizio è progettato con un'interfaccia client-server, puoi eseguire il binding al servizio da un altro componente passando un
Intent
abindService()
. Per saperne di più, consulta la guida Servizi. - Pubblicare una trasmissione
Una trasmissione è un messaggio che qualsiasi app può ricevere. Il sistema trasmette varie broadcasting per gli eventi di sistema, ad esempio quando il sistema si avvia o il dispositivo inizia a caricarsi. Puoi trasmettere una trasmissione ad altre app passando un
Intent
asendBroadcast()
osendOrderedBroadcast()
.
Il resto di questa pagina spiega come funzionano e come utilizzare gli intent. Per informazioni correlate, consulta Interazione con altre app e Condivisione di contenuti.
Tipi di intent
Esistono due tipi di intent:
- Gli intent espliciti specificano il componente dell'applicazione che soddisferà l'intent, specificando un
ComponentName
completo. In genere, per avviare un componente nella tua app utilizzerai un'intent esplicita, perché conosci il nome della classe dell'attività o del servizio che vuoi avviare. Ad esempio, potresti avviare una nuova attività all'interno della tua app in risposta a un'azione dell'utente o avviare un servizio per scaricare un file in background. - Gli intent impliciti non indicano un componente specifico, ma dichiarano un'azione generale da eseguire, che consente a un componente di un'altra app di gestirla. Ad esempio, se vuoi mostrare all'utente una posizione su una mappa, puoi utilizzare un'intenzione implicita per richiedere a un'altra app idonea di mostrare una posizione specificata su una mappa.
La Figura 1 mostra come viene utilizzato un'intent quando viene avviata un'attività. Quando l'oggetto
Intent
nomina esplicitamente un componente dell'attività specifico, il sistema
lo avvia immediatamente.
Quando utilizzi un intent implicito, il sistema Android trova il componente appropriato per iniziare
confrontando i contenuti dell'intent con i filtri di intent dichiarati nel file manifest di altre app sul
dispositivo. Se l'intent corrisponde a un filtro intent, il sistema avvia il componente e lo fornisce
all'oggetto Intent
. Se sono compatibili più filtri di intent, il sistema visualizza una finestra di dialogo in modo che l'utente possa scegliere l'app da utilizzare.
Un filtro per intent è un'espressione nel file manifest di un'app che specifica il tipo di intent che il componente desidera ricevere. Ad esempio, se dichiari un filtro per intent per un'attività, consenti ad altre app di avviare direttamente la tua attività con un determinato tipo di intent. Analogamente, se non dichiari filtri di intent per un'attività, questa può essere avviata solo con un intent esplicito.
Attenzione: per garantire la sicurezza della tua app, utilizza sempre un'intent esplicita quando avvii un Service
e non dichiarare filtri intent per i tuoi servizi. L'utilizzo di un intent implicito per avviare un servizio è un rischio per la sicurezza perché non puoi sapere con certezza quale servizio risponderà all'intento e l'utente non può vedere quale servizio viene avviato. A partire da Android 5.0 (livello API 21), il sistema
genera un'eccezione se chiami bindService()
con un intent implicito.
Creazione di un'intenzione
Un oggetto Intent
contiene informazioni che il sistema Android utilizza per determinare quale componente iniziare (ad esempio il nome esatto o la categoria del componente che dovrebbe ricevere l'intent), oltre a informazioni che il componente destinatario utilizza per eseguire correttamente l'azione (ad esempio l'azione da eseguire e i dati su cui agire).
Le informazioni principali contenute in un Intent
sono le seguenti:
- Nome del componente
- Il nome del componente da avviare.
Questo valore è facoltativo, ma è l'informazione fondamentale che rende un intent esplicito, il che significa che l'intent deve essere inviato solo al componente dell'app definito dal nome del componente. Senza un nome del componente, l'intent è implicito e il sistema decide quale componente deve riceverlo in base alle altre informazioni sull'intent (ad esempio azione, dati e categoria, descritti di seguito). Se devi avviare un componente specifico nella tua app, devi specificare il nome del componente.
Nota: quando avvii un
Service
, specifica sempre il nome del componente. In caso contrario, non puoi sapere con certezza quale servizio risponderà all'intent e l'utente non può vedere quale servizio viene avviato.Questo campo di
Intent
è un oggettoComponentName
, che puoi specificare utilizzando un nome di classe completamente qualificato del componente di destinazione, incluso il nome del pacchetto dell'app, ad esempiocom.example.ExampleActivity
. Puoi impostare il nome del componente consetComponent()
,setClass()
,setClassName()
o con il costruttoreIntent
. - Azione
- Una stringa che specifica l'azione generica da eseguire (ad esempio visualizza o scegli).
Nel caso di un'intenzione di trasmissione, si tratta dell'azione che si è verificata e che viene segnalata. L'azione determina in gran parte la struttura del resto dell'intent, in particolare le informazioni contenute nei dati e negli extra.
Puoi specificare le tue azioni per l'utilizzo da parte degli intent all'interno della tua app (o da parte di altre app per richiamare i componenti nella tua app), ma in genere specifichi costanti di azioni definite dalla classe
Intent
o da altre classi del framework. Ecco alcune azioni comuni per avviare un'attività:ACTION_VIEW
- Utilizza questa azione in un'intenzione con
startActivity()
quando hai alcune informazioni che un'attività può mostrare all'utente, ad esempio una foto da visualizzare in un'app galleria o un indirizzo da visualizzare in un'app di mappe. ACTION_SEND
- Chiamato anche intent di condivisione, devi utilizzarlo in un intent con
startActivity()
quando hai alcuni dati che l'utente può condividere tramite un'altra app, ad esempio un'app di posta elettronica o di condivisione social.
Consulta il riferimento della classe
Intent
per ulteriori costanti che definiscono le azioni generiche. Altre azioni sono definite in altri punti del framework di Android, ad esempio inSettings
per le azioni che aprono schermate specifiche nell'app Impostazioni del sistema.Puoi specificare l'azione per un'intenzione con
setAction()
o con un costruttoreIntent
.Se definisci le tue azioni, assicurati di includere il nome del pacchetto dell'app come prefisso, come mostrato nell'esempio seguente:
Kotlin
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
Java
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
- Dati
- L'URI (un oggetto
Uri
) che fa riferimento ai dati su cui eseguire l'azione e/o al tipo MIME di questi dati. Il tipo di dati forniti è generalmente dettato dall'azione dell'intent. Ad esempio, se l'azione èACTION_EDIT
, i dati devono contenere l'URI del documento da modificare.Quando crei un'intenzione, spesso è importante specificare il tipo di dati (il tipo MIME) oltre all'URI. Ad esempio, un'attività in grado di visualizzare immagini probabilmente non sarà in grado di riprodurre un file audio, anche se i formati degli URI potrebbero essere simili. La specifica del tipo MIME dei dati consente al sistema Android di trovare il componente migliore per ricevere l'intent. Tuttavia, a volte il tipo MIME può essere dedotto dall'URI, in particolare quando i dati sono un URI
content:
. Un URIcontent:
indica che i dati si trovano sul dispositivo e sono controllati da unContentProvider
, il che rende il tipo MIME dei dati visibile al sistema.Per impostare solo l'URI dei dati, chiama
setData()
. Per impostare solo il tipo MIME, chiamasetType()
. Se necessario, puoi impostare entrambi esplicitamente consetDataAndType()
.Attenzione: se vuoi impostare sia l'URI sia il tipo MIME, non chiama
setData()
esetType()
perché annullano ciascuno il valore dell'altro. Usa sempresetDataAndType()
per impostare sia il tipo URI sia il tipo MIME. - Categoria
- Una stringa contenente informazioni aggiuntive sul tipo di componente
che deve gestire l'intent. In un'intenzione è possibile inserire un numero qualsiasi di descrizioni di categoria, ma la maggior parte delle intenzioni non richiede una categoria.
Ecco alcune categorie comuni:
CATEGORY_BROWSABLE
- L'attività target può essere avviata da un browser web per visualizzare i dati a cui fa riferimento un link, ad esempio un'immagine o un messaggio email.
CATEGORY_LAUNCHER
- L'attività è l'attività iniziale di un'attività ed è elencata nel avviatore di applicazioni del sistema.
Consulta la descrizione della classe
Intent
per l'elenco completo delle categorie.Puoi specificare una categoria con
addCategory()
.
Queste proprietà elencate sopra (nome del componente, azione, dati e categoria) rappresentano le caratteristiche che definiscono un'intenzione. Leggendo queste proprietà, il sistema Android è in grado di risolvere quale componente dell'app deve avviare. Tuttavia, un intent può trasferire informazioni aggiuntive che non influiscono sulla sua risoluzione in un componente dell'app. Un'intenzione può anche fornire le seguenti informazioni:
- Extra
- Coppie chiave-valore che contengono informazioni aggiuntive necessarie per completare
l'azione richiesta.
Così come alcune azioni utilizzano particolari tipi di URI di dati, anche altre utilizzano particolari extra.
Puoi aggiungere altri dati con vari metodi
putExtra()
, ognuno dei quali accetta due parametri: il nome della chiave e il valore. Puoi anche creare un oggettoBundle
con tutti i dati aggiuntivi, quindi inserireBundle
inIntent
conputExtras()
.Ad esempio, durante la creazione di un intent per inviare un'email con
ACTION_SEND
, puoi specificare il destinatario to con la chiaveEXTRA_EMAIL
e l'oggetto con la chiaveEXTRA_SUBJECT
.La classe
Intent
specifica molte costantiEXTRA_*
per i tipi di dati standardizzati. Se devi dichiarare le tue chiavi aggiuntive (per gli intent ricevuti dalla tua app), assicurati di includere il nome del pacchetto dell'app come prefisso, come mostrato nell'esempio seguente:Kotlin
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
Java
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Attenzione: non utilizzare i dati
Parcelable
oSerializable
quando invii un'intenzione che prevedi di ricevere un'altra app. Se un'app tenta di accedere ai dati di un oggettoBundle
, ma non ha accesso alla classe pacchettizzata o serializzata, il sistema genera unRuntimeException
. - Flag Nella classe
- flag che fungono da metadati per l'intent. I flag possono indicare al sistema Android come avviare un'attività (ad esempio a quale
attività appartiene) e come gestirla dopo l'avvio (ad esempio se appartiene all'elenco delle attività
recenti).
Per ulteriori informazioni, consulta il metodo
setFlags()
.
Intent
sono definiti i Esempio di intent esplicito
Un intent esplicito è quello che utilizzi per avviare un componente dell'app specifico, ad esempio un'attività o un servizio specifico nella tua app. Per creare un intent esplicito, definisci il nome del componente per l'oggetto Intent
. Tutte le altre proprietà dell'intent sono facoltative.
Ad esempio, se hai creato un servizio nella tua app denominato DownloadService
, pensato per scaricare un file dal web, puoi avviarlo con il seguente codice:
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);
Il costruttore Intent(Context, Class)
fornisce all'app Context
e al componente un oggetto Class
. Di conseguenza, questo intent avvia esplicitamente la classe DownloadService
nell'app.
Per ulteriori informazioni sulla creazione e sull'avvio di un servizio, consulta la guida Servizi.
Esempio di intent implicito
Un intent implicito specifica un'azione che può richiamare qualsiasi app sul dispositivo in grado di eseguire l'azione. L'utilizzo di un intent implicito è utile quando l'app non può eseguire l'azione, ma probabilmente altre app possono farlo e vorresti che l'utente scelga l'app da utilizzare.
Ad esempio, se hai contenuti che vuoi che l'utente condivida con altre persone,
crea un'intenzione
con l'azione ACTION_SEND
e aggiungi elementi aggiuntivi che specificano i contenuti da condividere. Quando chiami
startActivity()
con questo intent, l'utente può
scegliere un'app tramite la quale condividere i contenuti.
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. }
Quando viene chiamato startActivity()
, il sistema esamina tutte le app installate per determinare quali possono gestire questo tipo di intent (un intent con l'azione ACTION_SEND
e che trasporta dati "text/plain"). Se esiste un'unica app in grado di gestirlo, questa si apre immediatamente e riceve l'intent. Se nessun'altra app è in grado di gestirlo, la tua app può rilevare il problema
ActivityNotFoundException
che si verifica. Se più attività accettano l'intent, il sistema visualizza una finestra di dialogo come quella mostrata nella Figura 2, in modo che l'utente possa scegliere quale app utilizzare.
Nella guida sono disponibili ulteriori informazioni sull'avvio di altre app anche nella guida su come inviare l'utente a un'altra app.
Forzare un selettore di app
Quando esiste più di un'app che risponde al tuo intent implicito, l'utente può selezionare l'app da utilizzare e impostarla come predefinita per l'azione. La possibilità di selezionare un valore predefinito è utile quando si esegue un'azione per la quale l'utente probabilmente vuole utilizzare sempre la stessa app, ad esempio quando apre una pagina web (gli utenti spesso preferiscono un solo browser web).
Tuttavia, se più app possono rispondere all'intento e l'utente potrebbe voler utilizzare un'app diversa ogni volta, devi mostrare esplicitamente una finestra di dialogo di selezione. La finestra di dialogo di scelta chiede all'utente di selezionare l'app da utilizzare per l'azione (l'utente non può selezionare un'app predefinita per l'azione). Ad esempio, quando la tua app esegue l'azione "Condividi" con l'azione ACTION_SEND
, gli utenti potrebbero voler condividere utilizzando un'app diversa a seconda della situazione attuale, quindi devi sempre utilizzare la finestra di dialogo di scelta, come mostrato in Figura 2.
Per mostrare il selettore, crea un Intent
utilizzando createChooser()
e passalo a startActivity()
, come mostrato nell'esempio seguente.
Questo esempio mostra una finestra di dialogo con un elenco di app che rispondono all'intent passato al metodo createChooser()
e utilizza il testo fornito come titolo della finestra di dialogo.
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); }
Rileva i lanci di intent non sicuri
La tua app potrebbe avviare intent per spostarsi tra i componenti al suo interno o per eseguire un'azione per conto di un'altra app. Per migliorare la sicurezza della piattaforma, Android 12 (livello API 31) e versioni successive forniscono una funzionalità di debug che ti avvisa se la tua app esegue un avvio non sicuro di un intent. Ad esempio, la tua app potrebbe eseguire un lancio non sicuro di un intent nidificato, ovvero un intent che viene passato come extra in un altro intent.
Se la tua app esegue entrambe le seguenti azioni, il sistema rileva un lancio di intent non sicuro e si verifica una violazione di tipo StrictMode:
- La tua app estrae un intent nidificato dagli extra di un intent inviato.
- L'app avvia immediatamente un componente dell'app utilizzando l'intent nidificato, ad esempio passando l'intent a
startActivity()
,startService()
obindService()
.
Per maggiori dettagli su come identificare questa situazione e apportare modifiche alla tua app, leggi il post del blog sugli Android Nesting Intent su Medium.
Verificare la presenza di lanci di intent non sicuri
Per verificare la presenza di lanci di intent non sicuri nella tua app, chiama
detectUnsafeIntentLaunch()
quando configuri VmPolicy
, come mostrato nello snippet di codice seguente. Se la tua app rileva una violazione di StrictMode, ti consigliamo di interrompere l'esecuzione dell'app per proteggere informazioni potenzialmente sensibili.
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()); }
Utilizzare le intenzioni in modo più responsabile
Per ridurre al minimo la possibilità del lancio di un intent non sicuro e di una violazione di StrictMode, segui queste best practice.
Copia solo gli extra essenziali all'interno degli intent ed esegui le necessarie operazioni di pulizia e convalida. La tua app potrebbe copiare gli extra da un intent a
un altro intent utilizzato per avviare un nuovo componente. Questo si verifica quando la tua app chiama putExtras(Intent)
o putExtras(Bundle)
.
Se l'app esegue una di queste operazioni, copia solo le operazioni extra previste dal
componente di ricezione. Se l'altro intent (che riceve la copia) avvia un componente che non è esportato, esegui la sanitizzazione e la convalida degli extra prima di copiarli nell'intent che avvia il componente.
Non esportare i componenti dell'app inutilmente. Ad esempio, se intendi avviare un componente dell'app utilizzando un intent interno nidificato, imposta l'attributo android:exported
del componente su false
.
Utilizza un PendingIntent
anziché un intento nidificato. In questo modo, quando un'altra app decompila il PendingIntent
del suo Intent
contenente, può avviare il PendingIntent
utilizzando l'identità della tua app. Questa configurazione consente all'altra app di avviare in sicurezza qualsiasi componente, incluso un componente non esportato, nella tua app.
Il diagramma nella figura 2 mostra in che modo il sistema passa il controllo dall'app (client) a un'altra app (di servizio) e torna alla tua app:
- La tua app crea un intent che richiama un'attività in un'altra app. All'interno di questo intent, aggiungi un oggetto
PendingIntent
come extra. Questo intent in attesa invoca un componente nella tua app che non viene esportato. - Una volta ricevuto l'intent della tua app, l'altra app estrae l'oggetto
PendingIntent
nidificato. - L'altra app richiama il metodo
send()
sull'oggettoPendingIntent
. - Dopo aver passato il controllo all'app, il sistema richiama l'intent in attesa utilizzando il contesto dell'app.
Figura 2. Diagramma della comunicazione tra app quando si utilizza un intento pending nidificato.
Ricevere un intent implicito
Per pubblicizzare gli intent impliciti che la tua app può ricevere, dichiara uno o più filtri per intent per ciascuno dei componenti dell'app con un elemento <intent-filter>
nel file manifest.
Ogni filtro intent specifica il tipo di intent accettati in base all'azione, ai dati e alla categoria dell'intent. Il sistema invia un'intenzione implicita al componente dell'app solo se l'intenzione può passare attraverso uno dei tuoi filtri di intent.
Nota:un'intenzione esplicita viene sempre pubblicata nel relativo target, indipendentemente dai filtri di intent dichiarati dal componente.
Un componente dell'app deve dichiarare filtri separati per ogni attività univoca che può svolgere.
Ad esempio, un'attività in un'app di galleria di immagini può avere due filtri: uno per visualizzare un'immagine e un altro per modificarla. Quando l'attività viene avviata, esamina il Intent
e decide come comportarsi in base alle informazioni al suo interno (ad esempio se mostrare o meno i controlli dell'editor).Intent
Ogni filtro per intent è definito da un elemento <intent-filter>
nel file manifest dell'app, nidificato nel componente dell'app corrispondente (ad esempio
un elemento <activity>
).
In ogni componente dell'app che include un elemento <intent-filter>
, imposta esplicitamente un valore per android:exported
.
Questo attributo indica se il componente dell'app è accessibile ad altre app. In alcune
situazioni, ad esempio nelle attività i cui filtri per intent includono la categoria
LAUNCHER
, è utile impostare questo attributo su true
. In caso contrario, è più sicuro impostare questo attributo su false
.
Avviso:se un'attività, un servizio o un broadcast receiver nella tua app utilizza i filtri per intent e non imposta esplicitamente il valore per android:exported
, la tua app non può essere installata su un dispositivo che esegue Android 12 o versioni successive.
All'interno di <intent-filter>
,
puoi specificare il tipo di intenti da accettare utilizzando uno o più
di questi tre elementi:
<action>
- Dichiara l'azione intent accettata nell'attributo
name
. Il valore deve essere il valore letterale della stringa di un'azione, non la costante di classe. <data>
- Dichiara il tipo di dati accettati utilizzando uno o più attributi che specificano vari aspetti dell'URI dei dati (
scheme
,host
,port
,path
) e del tipo MIME. <category>
- Dichiara la categoria di intent accettata nell'attributo
name
. Il valore deve essere il valore letterale di una stringa di un'azione, non la costante di classe.Nota:per ricevere intent impliciti, devi includere la categoria
CATEGORY_DEFAULT
nel filtro per intent. I metodistartActivity()
estartActivityForResult()
trattano tutti gli intenti come se avessero dichiarato la categoriaCATEGORY_DEFAULT
. Se non dichiari questa categoria nel filtro per intent, nessun intent implicito verrà risolto nella tua attività.
Ad esempio, di seguito è riportata una dichiarazione di attività con un filtro intent per ricevere un intento ACTION_SEND
quando il tipo di dati è di tipo testo:
<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>
Puoi creare un filtro che includa più di un'istanza di
<action>
,
<data>
o
<category>
.
In questo caso, devi assicurarti che il componente possa gestire qualsiasi combinazione di questi elementi di filtro.
Quando vuoi gestire più tipi di intent, ma solo in combinazioni specifiche di tipo di azione, dati e categoria, devi creare più filtri di intent.
Un'intenzione implicita viene testata rispetto a un filtro confrontandola con ciascuno dei tre elementi. Per essere pubblicato nel componente, l'intent deve superare tutti e tre i test. Se non riesce a corrispondere nemmeno a uno di questi, il sistema Android non fornirà l'intent al componente. Tuttavia, poiché un componente può avere più filtri di intent, un intent che non viene applicato a uno dei filtri di un componente potrebbe essere applicato a un altro filtro. Ulteriori informazioni su come il sistema risolve le intenzioni sono riportate nella sezione di seguito sulla risoluzione delle intenzioni.
Attenzione: l'utilizzo di un filtro intent non è un modo sicuro per impedire ad altre app di avviare i tuoi componenti. Sebbene i filtri intent impediscano a un componente di rispondere solo a determinati tipi di intent impliciti, un'altra app può potenzialmente avviare il componente dell'app utilizzando un intent esplicito se lo sviluppatore determina i nomi dei componenti.
Se è importante che soltanto la tua app sia in grado di avviare uno dei tuoi componenti,
non dichiarare i filtri per intent nel file manifest. Imposta invece l'attributo
exported
su "false"
per quel componente.
Analogamente, per evitare di eseguire inavvertitamente un'Service
di un'altra app, utilizza sempre un'intent esplicita per avviare il tuo servizio.
Nota: per tutte le attività, devi dichiarare i filtri di intent nel file manifest.
Tuttavia, i filtri per i ricevitori di trasmissione possono essere registrati dinamicamente chiamando
registerReceiver()
. Potrai quindi annullare la registrazione del destinatario con unregisterReceiver()
. In questo modo, la tua app può monitorare trasmissioni specifiche solo durante un periodo di tempo specificato mentre è in esecuzione.
Filtri di esempio
Per dimostrare alcuni dei comportamenti dei filtri intent, ecco un esempio proveniente dal file manifest di un'app di condivisione social:
<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>
La prima attività, MainActivity
, è l'entry point principale dell'app, ovvero l'attività che si apre quando l'utente avvia inizialmente l'app con l'icona del programma di avvio:
- L'azione
ACTION_MAIN
indica che si tratta del punto di contatto principale e non prevede dati sull'intent. - La categoria
CATEGORY_LAUNCHER
indica che l'icona di questa attività deve essere posizionata in Avvio applicazioni del sistema. Se l'elemento<activity>
non specifica un'icona conicon
, il sistema utilizza l'icona dell'elemento<application>
.
Questi due elementi devono essere accoppiati affinché l'attività venga visualizzata nell'avvio app.
La seconda attività, ShareActivity
, è pensata per facilitare la condivisione di contenuti di testo e multimediali. Sebbene gli utenti possano accedere a questa attività passando da MainActivity
,
possono anche accedere a ShareActivity
direttamente da un'altra app che emette un intento implicito corrispondente a uno dei due filtri per intent.
Nota:il tipo MIME,
application/vnd.google.panorama360+jpg
, è un tipo di dato speciale che specifica
le foto panoramiche, che puoi gestire con le API Google
panorama.
Abbinare gli intent ai filtri per intent di altre app
Se un'altra app ha come target Android 13 (livello API 33) o versioni successive, può gestire l'intent della tua app solo se corrisponde alle azioni e alle categorie di un elemento <intent-filter>
nell'altra app. Se il sistema non trova una corrispondenza, genera un ActivityNotFoundException
.
L'app di invio deve gestire questa eccezione.
Analogamente, se aggiorni l'app in modo che abbia come target Android 13 o versioni successive, tutti gli intent provenienti da app esterne vengono inviati a un componente esportato dell'app solo se l'intent corrisponde alle azioni e alle categorie di un elemento <intent-filter>
dichiarato dall'app. Questo comportamento si verifica indipendentemente dalla versione dell'SDK target dell'app di invio.
La corrispondenza dell'intenzione non viene applicata nei seguenti casi:
- Intent inviati a componenti che non dichiarano filtri intent.
- Intent provenienti dalla stessa app.
- Intent provenienti dal sistema, ovvero inviati dall'"UID di sistema" (uid=1000). Le app di sistema includono
system_server
e le app che impostanoandroid:sharedUserId
suandroid.uid.system
. - Intent che hanno origine dalla directory principale.
Scopri di più sulla corrispondenza all'intenzione.
Utilizzare un intent in attesa
Un oggetto PendingIntent
è un wrapper per un oggetto Intent
. Lo scopo principale di un PendingIntent
è concedere l'autorizzazione a un'applicazione esterna
per utilizzare il Intent
contenuto come se fosse eseguito dal processo della stessa
app.
I principali casi d'uso per un'intenzione in attesa sono i seguenti:
- Dichiarazione di un'intent da eseguire quando l'utente esegue un'azione con la tua notifica
(
NotificationManager
del sistema Android esegueIntent
). - Dichiarazione di un intent da eseguire quando l'utente esegue un'azione con il widget dell'app (l'app della schermata Home esegue
Intent
). - Dichiarazione di un intent da eseguire in un momento futuro specificato (il
AlarmManager
del sistema Android esegue ilIntent
).
Proprio come ogni oggetto Intent
è progettato per essere gestito da un tipo specifico di componente dell'app (Activity
, Service
o BroadcastReceiver
), anche un PendingIntent
deve essere creato con la stessa considerazione. Quando utilizzi un'intenzione in attesa, la tua app non lo fa
eseguire l'intenzione con una chiamata come startActivity()
. Devi invece dichiarare il tipo di componente previsto quando crei PendingIntent
chiamando il rispettivo metodo di creazione:
PendingIntent.getActivity()
per unIntent
che avviaActivity
.PendingIntent.getService()
per unIntent
che avvia unService
.PendingIntent.getBroadcast()
per unIntent
che avvia unBroadcastReceiver
.
A meno che la tua app non riceva intent in attesa da altre app,
i metodi precedenti per creare un PendingIntent
sono probabilmente gli unici metodi
PendingIntent
di cui avrai bisogno.
Ogni metodo utilizza l'app corrente Context
, il Intent
che vuoi aggregare e uno o più flag che specificano la modalità di utilizzo dell'intent (ad esempio se può essere utilizzato più di una volta).
Per ulteriori informazioni sull'utilizzo degli intent in attesa, consulta la documentazione per ogni rispettivo caso d'uso, ad esempio nelle guide dell'API Notifiche e App Widgets.
Specifica la mutabilità
Se la tua app ha come target Android 12 o versioni successive, devi specificare la mutabilità di ogni oggetto PendingIntent
creato dalla tua app. Per dichiarare che un determinato oggetto PendingIntent
è mutabile o immutabile, utilizza rispettivamente il flag PendingIntent.FLAG_MUTABLE
o PendingIntent.FLAG_IMMUTABLE
.
Se la tua app tenta di creare un oggetto PendingIntent
senza impostare alcun flag di mutabilità, il sistema genera un
IllegalArgumentException
e
viene visualizzato il seguente messaggio in Logcat:
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.
Crea intent in attesa immutabili, se possibile
Nella maggior parte dei casi, l'app deve creare oggetti PendingIntent
immutabili, come mostrato nel seguente snippet di codice. Se un oggetto PendingIntent
è immutabile,
altre app non possono modificare l'intent per regolare il risultato dell'invocazione dell'intent.
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);
Tuttavia, alcuni casi d'uso richiedono invece oggetti PendingIntent
mutabili:
- Supporto delle azioni di risposta diretta nelle notifiche. La risposta diretta richiede una modifica dei dati del clip nell'oggetto PendingIntent associato alla risposta. In genere, richiedi questa modifica passando
FILL_IN_CLIP_DATA
come flag al metodofillIn()
. - Associare le notifiche al framework Android Auto utilizzando istanze di
CarAppExtender
. - Inserire conversazioni in bolli utilizzando le istanze di
PendingIntent
. Un oggettoPendingIntent
mutabile consente al sistema di applicare i flag corretti, ad esempioFLAG_ACTIVITY_MULTIPLE_TASK
eFLAG_ACTIVITY_NEW_DOCUMENT
. - Richiesta di informazioni sulla posizione del dispositivo chiamando
requestLocationUpdates()
o API simili. L'oggettoPendingIntent
modificabile consente al sistema di aggiungere elementi extra di intent che rappresentano gli eventi del ciclo di vita delle località. Questi eventi includono un cambio di località e la disponibilità di un fornitore. - Programmazione delle sveglie utilizzando
AlarmManager
. L'oggetto mutabilePendingIntent
consente al sistema di aggiungere l'intent extraEXTRA_ALARM_COUNT
. Questo extra indica il numero di volte in cui è stata attivata una sveglia ripetuta. Contiene questo contenuto aggiuntivo, l'intent può comunicare con precisione a un'app se una sveglia ricorrente è stata attivata più volte, ad esempio quando il dispositivo era in sospensione.
Se la tua app crea un oggetto PendingIntent
mutabile, ti consigliamo vivamente di utilizzare un intent esplicito e di compilare il campo ComponentName
. In questo modo, ogni volta che un'altra app richiama PendingIntent
e restituisce il controllo alla tua app, viene sempre avviato lo stesso componente della tua app.
Utilizzare intent espliciti all'interno di intent in attesa
Per definire meglio il modo in cui altre app possono utilizzare gli intent in attesa della tua app, aggrega sempre un intent in attesa intorno a un intent esplicito. Per seguire questa best practice, svolgi i seguenti passaggi:
- Verifica che i campi relativi all'azione, al pacchetto e al componente dell'intent di base siano impostati.
-
Utilizza
FLAG_IMMUTABLE
, aggiunto in Android 6.0 (livello API 23), per creare intent in attesa. Questo flag impedisce alle app che ricevono unPendingIntent
di compilare proprietà non compilate. Se lominSdkVersion
della tua app è22
o inferiore, puoi garantire sicurezza e compatibilità insieme usando il seguente codice:if (Build.VERSION.SDK_INT >= 23) { // Create a PendingIntent using FLAG_IMMUTABLE. } else { // Existing code that creates a PendingIntent. }
Risoluzione dell'intenzione
Quando il sistema riceve un'intenzione implicita di avviare un'attività, cerca l'attività migliore per l'intenzione confrontandola con i filtri di intent in base a tre aspetti:
- Azione.
- Dati (sia URI che tipo di dati).
- Categoria.
Le seguenti sezioni descrivono in che modo gli intent vengono abbinati ai componenti appropriati in base alla dichiarazione del filtro per intent nel file manifest di un'app.
Test azione
Per specificare le azioni intent accettate, un filtro per intent può dichiarare zero o più elementi
<action>
, come mostrato nell'esempio seguente:
<intent-filter> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.VIEW" /> ... </intent-filter>
Per superare questo filtro, l'azione specificata in Intent
deve corrispondere a una delle azioni elencate nel filtro.
Se il filtro non elenca nessuna azione, non c'è nulla a cui
un intento possa corrispondere, quindi tutti gli intent non superano il test. Tuttavia, se un Intent
non specifica un'azione, supera il test purché il filtro
contenga almeno un'azione.
Test categoria
Per specificare le categorie di intent accettate, un filtro per intent può dichiarare zero o più elementi <category>
, come mostrato nell'esempio seguente:
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> ... </intent-filter>
Affinché un'intenzione superi il test di categoria, ogni categoria in Intent
deve corrispondere a una categoria nel filtro. Non è necessario l'inverso: il filtro per intent potrebbe
dichiarare più categorie rispetto a quelle specificate in Intent
e
Intent
continuerà a essere valido. Pertanto, un'intenzione senza categorie supera sempre questo test, indipendentemente dalle categorie dichiarate nel filtro.
Nota: Android applica automaticamente la categoria CATEGORY_DEFAULT
a tutti gli intent impliciti passati a startActivity()
e startActivityForResult()
.
Se vuoi che la tua attività riceva intent impliciti, deve includere una categoria per "android.intent.category.DEFAULT"
nei filtri intent, come mostrato nell'esempio <intent-filter>
precedente.
Test dei dati
Per specificare i dati dell'intent accettati, un filtro intent può dichiarare zero o più elementi
<data>
, come mostrato nell'esempio seguente:
<intent-filter> <data android:mimeType="video/mpeg" android:scheme="http" ... /> <data android:mimeType="audio/mpeg" android:scheme="http" ... /> ... </intent-filter>
Ogni elemento <data>
può specificare una struttura URI e un tipo di dati (tipo di media MIME).
Ogni parte dell'URI è un attributo distinto: scheme
, host
, port
e path
:
<scheme>://<host>:<port>/<path>
L'esempio seguente mostra i valori possibili per questi attributi:
content://com.example.project:200/folder/subfolder/etc
In questo URI, lo schema è content
, l'host è com.example.project
,
la porta è 200
e il percorso è folder/subfolder/etc
.
Ognuno di questi attributi è facoltativo in un elemento <data>
,
ma ci sono dipendenze lineari:
- Se non viene specificato uno schema, l'host viene ignorato.
- Se non viene specificato un host, la porta viene ignorata.
- Se non sono specificati né lo schema né l'host, il percorso viene ignorato.
Quando l'URI in un'intenzione viene confrontato con una specifica URI in un filtro, viene confrontato solo con le parti dell'URI incluse nel filtro. Ad esempio:
- Se un filtro specifica solo uno schema, tutti gli URI con questo schema corrispondono al filtro.
- Se un filtro specifica uno schema e un'autorità ma non un percorso, tutti gli URI con lo stesso schema e la stessa autorità superano il filtro, indipendentemente dai relativi percorsi.
- Se un filtro specifica uno schema, un'autorità e un percorso, solo gli URI con lo stesso schema, la stessa autorità e lo stesso percorso superano il filtro.
Nota: una specifica di percorso può contenere un asterisco (*) come carattere jolly per richiedere solo una corrispondenza parziale del nome del percorso.
Il test dei dati confronta sia l'URI sia il tipo MIME nell'intent con un URI e un tipo MIME specificati nel filtro. Le regole sono le seguenti:
- Un'intenzione che non contiene né un URI né un tipo MIME supera il test solo se il filtro non specifica URI o tipi MIME.
- Un'intenzione che contiene un URI, ma nessun tipo MIME (né esplicito né deducibile dall'URI), supera il test solo se il suo URI corrisponde al formato URI del filtro e anche il filtro non specifica un tipo MIME.
- Un'intent che contiene un tipo MIME, ma non un URI, supera il test solo se il filtro elenca lo stesso tipo MIME e non specifica un formato URI.
- Un intent che contiene sia un URI sia un tipo MIME (esplicito o deducibile dall'URI) passa la parte del test relativa al tipo MIME solo se quel tipo corrisponde a un tipo elencato nel filtro. Supera la parte del test relativa all'URI se il suo URI corrisponde a un URI nel filtro o se ha un URI
content:
ofile:
e il filtro non specifica un URI. In altre parole, si presume che un componente supporti i daticontent:
efile:
se il suo filtro elenca solo un tipo MIME.
Nota:se un'intenzione specifica un URI o un tipo MIME, il test dei dati non andrà a buon fine se non sono presenti elementi <data>
in <intent-filter>
.
Questa ultima regola, (d), riflette l'aspettativa
che i componenti siano in grado di recuperare i dati locali da un file o un fornitore di contenuti.
Pertanto, i relativi filtri possono elencare un solo tipo di dati e non è necessario nominare esplicitamente gli schemi content:
e file:
.
L'esempio seguente mostra un caso tipico in cui un elemento <data>
indica ad Android che il componente può recuperare i dati delle immagini da un fornitore di contenuti
e visualizzarli:
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
I filtri che specificano un tipo di dati, ma non un URI, sono forse i più comuni perché la maggior parte dei dati disponibili viene fornita dai fornitori di contenuti.
Un'altra configurazione comune è un filtro con uno schema e un tipo di dati. Ad esempio, un elemento <data>
come il seguente indica ad Android che il componente può recuperare i dati video dalla rete per eseguire l'azione:
<intent-filter> <data android:scheme="http" android:mimeType="video/*" /> ... </intent-filter>
Corrispondenza di intent
Gli intent vengono abbinati ai filtri per intent non solo per trovare un componente di destinazione da attivare, ma anche per scoprire qualcosa sull'insieme di componenti sul dispositivo. Ad esempio, l'app Home compila l'Avvio app trovando tutte le attività con filtri per intent che specificano l'azione ACTION_MAIN
e la categoria CATEGORY_LAUNCHER
.
Una corrispondenza ha esito positivo solo se le azioni e le categorie in Intent corrispondono
con il filtro, come descritto nella documentazione per la classe
IntentFilter
.
La tua applicazione può utilizzare la corrispondenza degli intent in modo simile a quanto fa l'app Home.
PackageManager
ha un insieme di metodi query...()
che restituiscono tutti i componenti in grado di accettare un determinato intento e
una serie simile di metodi resolve...()
che determinano il componente migliore per rispondere a un intento. Ad esempio,
queryIntentActivities()
restituisce un elenco di tutte le attività che possono eseguire
l'intent passato come argomento e queryIntentServices()
restituisce un elenco simile di servizi.
Nessuno dei due metodi attiva i componenti, ma elenca solo quelli che possono rispondere. Esiste un metodo simile,
queryBroadcastReceivers()
, per i broadcast receiver.