Un Intent
è un oggetto di messaggistica che puoi utilizzare per richiedere un'azione
da un altro componente dell'app.
Anche se gli intent facilitano 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()
.Intent
descrive l'attività da avviare e trasporta tutti i dati necessari.Se vuoi ricevere un risultato dall'attività al termine, chiama il numero
startActivityForResult()
. La tua attività riceve il risultato come oggettoIntent
separato nel callbackonActivityResult()
dell'attività. Per ulteriori informazioni, consulta la guida Attività. - Avviare 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 (come il download di un file) passando unIntent
astartService()
.Intent
descrive il servizio da avviare e trasporta tutti i dati necessari.Se il servizio è progettato con un'interfaccia client-server, puoi associarlo al servizio da un altro componente passando un
Intent
abindService()
. Per ulteriori informazioni, consulta la guida ai servizi. - Trasmettere una trasmissione
Un broadcast è un messaggio che può essere ricevuto da qualsiasi app. Il sistema fornisce varie trasmissioni per gli eventi di sistema, ad esempio quando il sistema si avvia o il dispositivo inizia a ricaricarsi. Puoi trasmettere una trasmissione ad altre app trasmettendo un
Intent
asendBroadcast()
osendOrderedBroadcast()
.
Il resto di questa pagina spiega come funzionano gli intent e come utilizzarli. Per informazioni correlate, consulta le sezioni Interazione con altre app e Condivisione di contenuti.
Tipi di intent
Esistono due tipi di intent:
- Gli intent espliciti specificano quale componente di quale applicazione soddisferà l'intent, specificando un
ComponentName
completo. In genere utilizzi un intent esplicito per avviare un componente nella tua app, perché conosci il nome della classe dell'attività o del servizio che vuoi avviare. Ad esempio, potresti avviare una nuova attività all'interno dell'app in risposta a un'azione dell'utente o avviare un servizio per scaricare un file in background. - Gli intent impliciti non assegnano un nome a un componente specifico, bensì dichiarano un'azione generale da eseguire, che consente a un componente di un'altra app di gestirlo. Ad esempio, se vuoi mostrare all'utente una posizione su una mappa, puoi utilizzare un intent implicito per richiedere che un'altra app capace mostri 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 in modo esplicito un componente di attività specifico, il sistema avvia immediatamente quel componente.
Quando utilizzi un intent implicito, il sistema Android trova il componente appropriato con cui iniziare confrontando i contenuti dell'intent con i filtri per intent dichiarati nel file manifest di altre app sul dispositivo. Se l'intent corrisponde a un filtro per intent, il sistema avvia quel componente e lo invia all'oggetto Intent
. Se sono compatibili più filtri per intent, il sistema
mostra una finestra di dialogo in modo che l'utente possa scegliere l'app da usare.
Un filtro di intent è un'espressione nel file manifest di un'app che specifica il tipo di intent che il componente vorrebbe 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 intento. Allo stesso modo, se non dichiari alcun filtro di intent per un'attività, l'attività può essere avviata solo con un intent esplicito.
Attenzione: per assicurarti che la tua app sia sicura, usa sempre un intent esplicito quando avvii Service
e non dichiarare filtri per intent per i tuoi servizi. L'utilizzo di un intent implicito per avviare un servizio rappresenta un rischio per la sicurezza perché non puoi avere la certezza di quale servizio risponderà all'intent 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 utilizzate dal sistema Android per determinare il componente da avviare (ad esempio l'esatto nome del componente o la categoria del componente che deve ricevere l'intent), oltre alle 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 componente
- Il nome del componente da iniziare.
Questa informazione è facoltativa, ma è l'informazione fondamentale che rende un intent esplicito, il che significa che l'intent deve essere pubblicato solo per il componente dell'app definito dal nome del componente. Senza un nome, l'intent è implicito e il sistema decide quale componente deve riceverlo in base alle altre informazioni sull'intent (ad esempio l'azione, i dati e la categoria descritti di seguito). Se devi avviare un componente specifico nell'app, devi specificarne il nome.
Nota:quando avvii un
Service
, specifica sempre il nome del componente. In caso contrario, non puoi essere certo di quale servizio risponderà all'intent e l'utente non potrà vedere quale servizio viene avviato.Questo campo
Intent
è un oggettoComponentName
che puoi specificare utilizzando un nome completo della classe 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 (come view o pick).
Nel caso di un intent di trasmissione, si tratta dell'azione che ha avuto luogo e che viene segnalata. L'azione determina in gran parte il modo in cui è strutturato il resto dell'intent, in particolare le informazioni contenute nei dati e negli extra.
Puoi specificare le tue azioni da utilizzare per gli intent all'interno dell'app (oppure per altre app per richiamare componenti nell'app), ma in genere specifichi le costanti di azione definite dalla classe
Intent
o da altre classi di framework. Di seguito sono riportate alcune azioni comuni per avviare un'attività:ACTION_VIEW
- Utilizza questa azione in un intent 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
- Noto anche come intent di condivisione, dovresti utilizzarlo in un intent con
startActivity()
quando disponi di alcuni dati che l'utente può condividere tramite un'altra app, come un'app email o un'app di condivisione sui social.
Consulta il riferimento della classe
Intent
per altre costanti che definiscono azioni generiche. Altre azioni sono definite altrove nel framework di Android, ad esempio inSettings
per le azioni che aprono schermate specifiche nell'app Impostazioni del sistema.Puoi specificare l'azione per un intent con
setAction()
o con un costruttoreIntent
.Se definisci le tue azioni, assicurati di includere come prefisso il nome del pacchetto dell'app, 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 operazioni e/o il 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 intent, spesso è importante specificare il tipo di dati (il tipo MIME) oltre al relativo URI. Ad esempio, un'attività in grado di visualizzare immagini probabilmente non sarà in grado di riprodurre un file audio, anche se i formati URI potrebbero essere simili. Se specifichi il tipo MIME dei tuoi dati, consenti al sistema Android di trovare il componente migliore per ricevere il tuo 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
, in modo che il tipo MIME dei dati sia visibile al sistema.Per impostare solo l'URI dei dati, chiama
setData()
. Per impostare solo il tipo MIME, chiamasetType()
. Se necessario, puoi impostarli entrambi esplicitamente consetDataAndType()
.Attenzione: se vuoi impostare sia il tipo URI sia il tipo MIME, non chiamare
setData()
esetType()
perché ciascuno annulla il valore dell'altro. Usa sempresetDataAndType()
per impostare il tipo URI e MIME. - Categoria
- Una stringa contenente informazioni aggiuntive sul tipo di componente
che dovrebbe gestire l'intent. In un intent può essere inserito un numero qualsiasi
di descrizioni di categorie, ma la maggior parte degli intent non richiede una categoria.
Ecco alcune categorie comuni:
CATEGORY_BROWSABLE
- L'attività target permette di essere avviata da un browser web per mostrare i dati a cui fa riferimento un link, come un'immagine o un messaggio email.
CATEGORY_LAUNCHER
- L'attività è l'attività iniziale di un'attività ed è elencata in Avvio applicazioni del sistema.
Consulta la descrizione della classe
Intent
per l'elenco completo delle categorie.Puoi specificare una categoria con
addCategory()
.
Le proprietà elencate in precedenza (nome del componente, azione, dati e categoria) rappresentano le caratteristiche distintive di un intent. Leggendo queste proprietà, il sistema Android può risolvere da quale componente dell'app avviare. Tuttavia, un intent può includere informazioni aggiuntive che non influiscono sul modo in cui viene risolto in un componente dell'app. Un intent può anche fornire le seguenti informazioni:
- Extra
- Coppie chiave-valore che contengono informazioni aggiuntive necessarie per eseguire l'azione richiesta.
Proprio come alcune azioni utilizzano tipi specifici di URI di dati, alcune azioni usano anche particolari extra.
Puoi aggiungere altri dati con vari metodi
putExtra()
, ciascuno dei quali accetta due parametri: il nome della chiave e il valore. Puoi anche creare un oggettoBundle
con tutti i dati aggiuntivi, poi inserireBundle
inIntent
conputExtras()
.Ad esempio, quando crei un intent per inviare un'email con
ACTION_SEND
, puoi specificare il destinatario a con la chiaveEXTRA_EMAIL
e specificare 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
per inviare un intent che ti aspetti venga ricevuto da un'altra app. Se un'app tenta di accedere ai dati in un oggettoBundle
, ma non ha accesso alla classe parceled o serializzata, il sistema genera unRuntimeException
. - Flag
- I flag sono definiti nella classe
Intent
e fungono da metadati per l'intent. I flag possono indicare al sistema Android come avviare un'attività (ad esempio, a quale attività deve appartenere) e come gestirla dopo l'avvio (ad esempio, se rientra nell'elenco delle attività recenti).Per saperne di più, vedi il metodo
setFlags()
.
Esempio di intent esplicito
Un intent esplicito è un intent da utilizzare per avviare uno specifico componente dell'app, ad esempio
un'attività o un servizio specifici 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 nella tua app un servizio, denominato DownloadService
, progettato 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 l'app Context
e il 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 la tua 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 dei contenuti che vuoi che l'utente condivida con altre persone, crea un intent con l'azione ACTION_SEND
e aggiungi extra che specificano i contenuti da condividere. Quando chiami
startActivity()
con questo intento, l'utente può
scegliere un'app tramite cui 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 di tipo "testo/normale"). Se solo un'app può gestirlo, l'app si apre immediatamente e gli viene assegnato l'intent. Se nessun'altra app può gestirlo, la tua app può rilevare l'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 l'app da utilizzare.
Puoi trovare ulteriori informazioni sull'avvio di altre app anche nella guida su come inviare l'utente a un'altra app.
Forzare un selettore di app
Se più app risponde al tuo intento implicito, l'utente può selezionare l'app da utilizzare e impostarla come scelta predefinita per l'azione. La possibilità di selezionare un'app predefinita è utile quando si esegue un'azione per la quale l'utente probabilmente vuole utilizzare la stessa app ogni volta, ad esempio quando apre una pagina web (gli utenti spesso preferiscono un solo browser web).
Tuttavia, se più app sono in grado di rispondere all'intent e l'utente potrebbe voler usare ogni volta un'app diversa, devi mostrare esplicitamente una finestra di dialogo di selezione. La finestra di dialogo del selettore 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 la "condivisione" con l'azione ACTION_SEND
, gli utenti potrebbero voler condividere usando un'app diversa a seconda della loro situazione attuale, quindi dovresti sempre usare la finestra di dialogo di selezione, come mostrato nella Figura 2.
Per visualizzare 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 trasmesso 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 lanci di intent non sicuri
La tua app potrebbe avviare intent per navigare tra i componenti all'interno dell'app o 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 lancio non sicuro di un intent. Ad esempio, la tua app potrebbe eseguire un avvio non sicuro di un intent nidificato, un intent che viene trasmesso come extra in un altro intent.
Se la tua app esegue entrambe le azioni seguenti, il sistema rileva un avvio di intent non sicuro e si verifica una violazione di tipo StrictMode:
- L'app separa un intent nidificato dagli extra di un intent pubblicato.
- L'app avvia immediatamente un componente
app utilizzando quell'intent nidificato,
ad esempio passando l'intent in
startActivity()
,startService()
obindService()
.
Per ulteriori dettagli su come identificare la situazione e apportare modifiche alla tua app, leggi il post del blog relativo agli intent di Nest per Android su Medium.
Controlla la presenza di avvii per intent non sicuri
Per verificare la presenza di avvii di intent non sicuri nella tua app, chiama
detectUnsafeIntentLaunch()
quando configuri VmPolicy
, come mostrato nel seguente snippet di codice. Se
la tua app rileva una violazione della modalità StrictMode, ti consigliamo di interrompere l'esecuzione dell'app per
proteggere le 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()); }
Usa le intenzioni in modo più responsabile
Per ridurre al minimo la possibilità di un lancio di intent non sicuro e di una violazione della modalità StrictMode, segui queste best practice.
Copia solo gli extra essenziali all'interno degli intent ed esegui tutte le operazioni di sanitizzazione e convalida necessarie. L'app potrebbe copiare gli extra da un intent a
un altro intent utilizzato per avviare un nuovo componente. Questo si verifica quando l'app chiama putExtras(Intent)
o putExtras(Bundle)
.
Se la tua app esegue una di queste operazioni, copia solo gli extra che il componente di ricezione si aspetta. Se l'altro intent (che riceve la copia) avvia un componente non esportato, esegui la sanitizzazione e convalida gli extra prima di copiarli nell'intent che avvia il componente.
Non esportare componenti dell'app inutilmente. Ad esempio, se vuoi lanciare un componente dell'app utilizzando un intent nidificato interno, imposta l'attributo android:exported
di quel componente su false
.
Utilizza un PendingIntent
anziché un
intent nidificato. In questo modo, quando un'altra app separa il PendingIntent
dell'elemento contenente Intent
, l'altra app può avviare 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 dalla tua app (client) a un'altra app (di servizio) e poi di nuovo alla tua app:
- L'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 richiama un componente nella tua app; il componente non viene esportato. - Dopo aver ricevuto l'intent della tua app, l'altra app estrae l'oggetto
PendingIntent
nidificato. - L'altra app richiama il metodo
send()
nell'oggettoPendingIntent
. - Dopo aver ritrasmesso il controllo alla tua app, il sistema richiama l'intent in sospeso utilizzando il contesto dell'app.
Figura 2. Diagramma della comunicazione tra app quando si utilizza un intent in attesa nidificato.
Ricezione di un intent implicito
Per pubblicizzare gli intent impliciti che la tua app può ricevere, dichiara uno o più filtri per intent per
ogni componente dell'app con un elemento <intent-filter>
nel file manifest.
Ogni filtro per intent specifica il tipo di intent che accetta in base all'azione, ai dati e alla categoria dell'intent. Il sistema fornisce un intent implicito al componente dell'app solo se l'intent può passare attraverso uno dei filtri per intent.
Nota: un intent esplicito viene sempre inviato al relativo target, indipendentemente da eventuali filtri per intent dichiarati dal componente.
Un componente dell'app deve dichiarare filtri separati per ogni job univoco che può eseguire.
Ad esempio, un'attività in un'app Galleria immagini può avere due filtri: un filtro
per visualizzare un'immagine e un altro per modificare un'immagine. Quando l'attività inizia,
controlla l'Intent
e decide come comportarsi in base alle informazioni
in Intent
(ad esempio mostrare o meno i controlli dell'editor).
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 ricevitore
di trasmissione nella tua app utilizza filtri per intent e non imposta esplicitamente il valore
di android:exported
, la tua app non può essere installata su un dispositivo
con Android 12 o versioni successive.
All'interno di <intent-filter>
, puoi specificare il tipo di intent 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 stringa letterale 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 stringa letterale 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 intent 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, ecco una dichiarazione di attività con un filtro per intent per ricevere un intent ACTION_SEND
quando il tipo di dati è di 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 sia in grado di gestire tutte
le combinazioni di questi elementi di filtro.
Se vuoi gestire più tipi di intent, ma solo in combinazioni specifiche di azione, dati e tipo di categoria, devi creare più filtri per intent.
Un intent implicito viene testato rispetto a un filtro confrontando l'intent con ciascuno dei tre elementi. Per essere pubblicato al componente, l'intent deve superare tutti e tre i test. Se non riesce a far corrispondere nemmeno uno di questi, il sistema Android non trasmetterà l'intent al componente. Tuttavia, poiché un componente può avere più filtri per intent, un intent che non passa attraverso uno dei filtri di un componente potrebbe farlo passare in un altro filtro. Nella sezione di seguito sulla risoluzione degli intent vengono fornite ulteriori informazioni su come il sistema risolve gli intent.
Attenzione : l'utilizzo di un filtro per intent non è un modo sicuro per impedire ad altre app di avviare i componenti. Anche se i filtri per intent limitano un componente a 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 solo la tua app sia in grado di avviare uno dei componenti, non dichiarare i filtri per intent nel manifest. Invece, imposta l'attributo exported
su "false"
per quel componente.
Analogamente, per evitare di eseguire inavvertitamente il Service
di un'altra app, usa sempre un intent esplicito per avviare il tuo servizio.
Nota:
per tutte le attività, devi dichiarare i filtri per intent nel file manifest.
Tuttavia, i filtri per i ricevitori di annunci possono essere registrati in modo dinamico chiamando
registerReceiver()
. Puoi quindi annullare la registrazione del destinatario con unregisterReceiver()
. In questo modo l'app può ascoltare trasmissioni specifiche solo durante un determinato periodo di tempo mentre l'app è in esecuzione.
Filtri di esempio
Per dimostrare alcuni dei comportamenti dei filtri per intent, ecco un esempio tratto dal file manifest di un'app per la condivisione sui 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
, è il punto di ingresso principale dell'app, ovvero l'attività che si apre quando l'utente lancia inizialmente l'app con l'icona in Avvio applicazioni:
- L'azione
ACTION_MAIN
indica che questo è il punto di ingresso principale e non prevede dati sull'intent. - La categoria
CATEGORY_LAUNCHER
indica che l'icona di questa attività deve essere posizionata nell'Avvio applicazioni del sistema. Se l'elemento<activity>
non specifica un'icona conicon
, il sistema utilizza l'icona dell'elemento<application>
.
Questi due dispositivi devono essere accoppiati per consentire la visualizzazione dell'attività in Avvio app.
La seconda attività, ShareActivity
, ha lo scopo di facilitare la condivisione di testi e contenuti multimediali. Anche se gli utenti possono accedere a questa attività da MainActivity
, possono anche inserire ShareActivity
direttamente da un'altra app che genera un intent implicito che corrisponde a uno dei due filtri per intent.
Nota: il tipo MIME application/vnd.google.panorama360+jpg
è un tipo di dati speciale che specifica le foto panoramiche, che puoi gestire con le API Google panoramiche.
Associa 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 l'intent 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.
In modo simile, se aggiorni la tua 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 della tua app solo se questo intent corrisponde alle azioni e
categorie di un elemento <intent-filter>
dichiarato dalla tua app. Questo comportamento si verifica a prescindere dalla versione dell'SDK di destinazione dell'app di invio.
Nei seguenti casi, la corrispondenza di intent non viene applicata:
- Intent inviati a componenti che non dichiarano filtri per intent.
- Intent provenienti dall'interno della stessa app.
- Intent provenienti dal sistema, ovvero intent inviati dall'"UID di sistema" (uid=1000). Le app di sistema includono
system_server
e le app che hanno impostatoandroid:sharedUserId
suandroid.uid.system
. - Intent che hanno origine dalla directory principale.
Scopri di più sulla corrispondenza dell'intenzione.
Utilizzo di un intent in attesa
Un oggetto PendingIntent
è un wrapper attorno a un oggetto Intent
. Lo scopo principale di un'istanza PendingIntent
è concedere a un'applicazione esterna l'autorizzazione a utilizzare l'elemento Intent
contenuto come se fosse stato eseguito dal processo della tua app.
Ecco i principali casi d'uso relativi a un intent in sospeso:
- Dichiarazione di un intent da eseguire quando l'utente esegue un'azione con la tua notifica (l'
NotificationManager
del sistema Android esegueIntent
). - Dichiarazione di un intent da eseguire quando l'utente esegue un'azione con il
widget per app
(l'app nella schermata Home esegue l'
Intent
). - Dichiarazione di un intent da eseguire in un determinato momento futuro (il
AlarmManager
del sistema Android esegue l'Intent
).
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 intent in attesa, l'app non lo esegue con una chiamata come startActivity()
. Devi invece dichiarare il tipo di componente previsto quando crei PendingIntent
richiamando il rispettivo metodo creator:
PendingIntent.getActivity()
per unIntent
che avvia unActivity
.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 sopra indicati per creare un PendingIntent
sono probabilmente gli unici
metodi PendingIntent
di cui avrai bisogno.
Ogni metodo prende l'attuale app Context
,
l'Intent
di cui vuoi eseguire il wrapping e uno o più flag che specificano
come deve essere utilizzato l'intent (ad esempio se l'intent può essere utilizzato più di una volta).
Per ulteriori informazioni sull'utilizzo degli intent in attesa, consulta la documentazione relativa a ciascuno dei rispettivi casi d'uso, ad esempio nelle guide API Notifiche e Widget app.
Specifica la mutabilità
Se la tua app ha come target Android 12 o versioni successive, devi specificare la
mutabilità di ogni oggetto PendingIntent
creato dall'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 un flag di modificabilità, il sistema genera un IllegalArgumentException
e in Logcat viene visualizzato il seguente messaggio:
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.
Quando possibile, crea intent in attesa immutabili
Nella maggior parte dei casi, l'app dovrebbe creare oggetti PendingIntent
immutabili, come mostrato nello snippet di codice riportato di seguito. Se un oggetto PendingIntent
è immutabile,
le altre app non possono modificare l'intent per regolare il risultato della chiamata
all'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 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, per richiedere questa modifica puoi passare
FILL_IN_CLIP_DATA
come flag al metodofillIn()
. - Associazione delle notifiche al framework di Android Auto, utilizzando le istanze di
CarAppExtender
. - Inserire le conversazioni in fumetti utilizzando istanze di
PendingIntent
. Un oggettoPendingIntent
mutabile consente al sistema di applicare i flag corretti, comeFLAG_ACTIVITY_MULTIPLE_TASK
eFLAG_ACTIVITY_NEW_DOCUMENT
. - Richiedere informazioni sulla posizione del dispositivo chiamando
requestLocationUpdates()
o API simili. L'oggettoPendingIntent
modificabile consente al sistema di aggiungere extra di intent che rappresentano gli eventi del ciclo di vita delle località. Questi eventi includono una modifica della località e la disponibilità di un fornitore. - Programmare le sveglie con
AlarmManager
. L'oggettoPendingIntent
modificabile consente al sistema di aggiungere l'intent extra diEXTRA_ALARM_COUNT
. Questo extra rappresenta il numero di volte in cui è stata attivata una sveglia ricorrente. Con questo extra, l'intent può notificare con precisione a un'app se una sveglia ricorrente è stata attivata più volte, ad esempio quando il dispositivo era in sospensione.
Se l'app crea un oggetto PendingIntent
modificabile, ti consigliamo vivamente di utilizzare un intent esplicito e compilare il campo ComponentName
. In questo modo, ogni volta che
un'altra app richiama PendingIntent
e passa di nuovo il controllo alla tua app, lo stesso componente dell'app si avvia sempre.
Utilizza intent espliciti all'interno di intent in attesa
Per definire meglio in che modo 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, segui questi passaggi:
- Verifica che i campi relativi ad azione, pacchetto e componenti dell'intent di base siano impostati.
-
Utilizza
FLAG_IMMUTABLE
, aggiunto in Android 6.0 (livello API 23) per creare gli intent in attesa. Questo flag impedisce alle app che ricevono unPendingIntent
di compilare le proprietà non compilate. Se il valore diminSdkVersion
della tua app è22
o inferiore, puoi garantire sicurezza e compatibilità contemporaneamente utilizzando 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 intent implicito di avviare un'attività, cerca l'attività migliore per l'intent confrontandola con i filtri per intent in base a tre aspetti:
- Azione.
- Dati (URI e 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 delle azioni
Per specificare le azioni per 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 alcuna azione, non c'è nulla a cui un intent deve corrispondere, quindi tutti gli intent non superano il test. Tuttavia, se 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 intent superi il test della categoria, a ogni categoria in Intent
deve corrispondere a una categoria nel filtro. Non è necessario eseguire l'inverso: il filtro per intent potrebbe
dichiarare più categorie di quelle specificate in Intent
e l'Intent
va comunque a buon fine. Pertanto, un intent 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 trasmessi a startActivity()
e startActivityForResult()
.
Se vuoi che l'attività riceva intent impliciti, deve
includere una categoria per "android.intent.category.DEFAULT"
nei relativi filtri per intent, come
mostrato nell'esempio di <intent-filter>
precedente.
Test dei dati
Per specificare i dati di intent accettati, un filtro per 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 multimediale MIME).
Ogni parte dell'URI è un attributo
separato: scheme
, host
, port
e path
:
<scheme>://<host>:<port>/<path>
L'esempio seguente mostra i possibili valori 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
.
Ciascuno 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 lo schema e l'host non sono specificati, il percorso viene ignorato.
Quando l'URI in un intent viene confrontato con una specifica URI in un filtro, viene confrontato solo con le parti dell'URI incluse nel filtro. Ecco alcuni esempi:
- 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 nessun percorso, tutti gli URI con lo stesso schema e la stessa autorità superano il filtro, indipendentemente dai percorsi.
- Se un filtro specifica uno schema, un'autorità e un percorso, solo gli URI con lo stesso schema, autorità e percorso superano il filtro.
Nota: una specifica del 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 che il tipo MIME nell'intent con un tipo URI e un tipo MIME specificati nel filtro. Le regole sono le seguenti:
- Un intent che non contiene né un URI né un tipo MIME supera il test solo se il filtro non specifica nessun URI o tipo MIME.
- Un intent che contiene un URI ma nessun tipo MIME (né esplicito né deducibile dall'URI) supera il test solo se il relativo URI corrisponde al formato URI del filtro e, analogamente, 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 tipo MIME del test solo se quel tipo corrisponde a un tipo elencato nel filtro. Passa la parte dell'URI del test 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 intent specifica un tipo URI o MIME, il test dei dati avrà esito negativo se non ci sono elementi <data>
in <intent-filter>
.
Quest'ultima regola, la regola (d), riflette l'aspettativa che i componenti possano ottenere i dati locali da un file o un fornitore di contenuti.
Di conseguenza, i filtri possono elencare solo un tipo di dati e non devono assegnare un nome esplicito agli schemi content:
e file:
.
L'esempio seguente mostra un caso tipico in cui un elemento <data>
indica ad Android che il componente può ottenere dati di immagine 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 confrontati con i filtri per intent non solo per scoprire un componente target da attivare, ma anche per scoprire qualcosa nell'insieme di componenti sul dispositivo. Ad esempio, l'app Home completa in 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 nell'intent corrispondono al filtro, come descritto nella documentazione per la classe IntentFilter
.
La tua applicazione può utilizzare la corrispondenza di intent in un modo simile a quello dell'app Home.
PackageManager
ha un insieme di metodi query...()
che restituiscono tutti i componenti che possono accettare un particolare intent e
una serie simile di metodi resolve...()
che determinano il
componente migliore per rispondere a un intent. Ad esempio, queryIntentActivities()
restituisce un elenco di tutte le attività che possono eseguire l'intent passato come argomento, mentre 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 ricevitori di trasmissioni.