Intent e filtri di intent

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 un Activity passando un Intent a startActivity(). L'elemento Intent descrive l'attività da avviare e trasporta tutti i dati necessari.

    Se vuoi ricevere un risultato dell'attività al termine dell'attività, chiama il numero startActivityForResult(). L'attività riceve il risultato come oggetto Intent separato nel callback onActivityResult() 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 con JobScheduler. Per ulteriori informazioni su JobScheduler, consulta la relativa API-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 un valore Intent a startService(). Intent descrive il servizio per 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 a bindService(). Per ulteriori informazioni, consulta la guida sui servizi.

  • Pubblicare una trasmissione

    Una trasmissione è un messaggio che può ricevere qualsiasi app. Il sistema invia varie trasmissioni per gli eventi di sistema, ad esempio all'avvio del sistema o all'avvio della ricarica del dispositivo. Puoi trasmettere una trasmissione ad altre app passando Intent a sendBroadcast() o sendOrderedBroadcast().

Il resto di questa pagina spiega come funzionano gli intent e come utilizzarli. Per informazioni correlate, consulta gli articoli 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'intento, specificando un valore ComponentName completo. In genere userai 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 nominano un componente specifico, ma dichiarano invece 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 in grado 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 indica in modo esplicito un componente dell'attività specifico, il sistema avvia immediatamente quel componente.

Figura 1. Modalità di invio di un intent implicito tramite il sistema per avviare un'altra attività: [1] l'attività A crea un elemento Intent con una descrizione dell'azione e lo passa a startActivity(). [2] Il sistema Android cerca in tutte le app un filtro per intent corrispondente all'intent. Quando viene trovata una corrispondenza, [3] il sistema avvia l'attività di corrispondenza (Attività B) richiamando il suo metodo onCreate() e trasmettendo il valore Intent.

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 per intent, il sistema avvia il componente e lo pubblica nell'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 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. Allo stesso modo, se non dichiari alcun filtro per intent per un'attività, questa può essere avviata solo con un intent esplicito.

Attenzione:per assicurarti che la tua app sia sicura, usa sempre un intent esplicito quando avvii un Service e non dichiarare i filtri per 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.

Sviluppo 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 componente
Il nome del componente da iniziare.

Questa informazione è facoltativa, ma è l'informazione fondamentale che rende un intent esplicita, ovvero l'intent deve essere inviato solo al componente dell'app definito dal nome del componente. Senza il nome di un componente, l'intent è implicito e il sistema decide quale componente dovrebbe ricevere l'intent 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 nella tua app, devi specificarne il nome.

Nota: quando avvii Service, specifica sempre il nome del componente. In caso contrario, non puoi sapere con certezza quale servizio risponderà all'intento e l'utente non potrà vedere quale servizio viene avviato.

Questo campo dell'Intent è un oggetto ComponentName, che puoi specificare utilizzando un nome completo della classe del componente di destinazione, incluso il nome del pacchetto dell'app, ad esempio com.example.ExampleActivity. Puoi impostare il nome del componente con setComponent(), setClass(), setClassName() o con il costruttore Intent.

Azione
Una stringa che specifica l'azione generica da eseguire (ad esempio visualizza o scegli).

Nel caso di un intent di trasmissione, questa è l'azione che si è verificata e viene segnalata. L'azione determina in gran parte il modo in cui è strutturato il resto dell'intento, in particolare le informazioni contenute nei dati e negli extra.

Puoi specificare le tue azioni da usare per gli intent all'interno dell'app (o da altre app per richiamare i componenti nell'app), ma di solito 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 possono essere mostrate all'utente da un'attività, ad esempio una foto da visualizzare in un'app Galleria o un indirizzo da visualizzare in un'app per la mappa.
ACTION_SEND
Noto anche come intent di condivisione, dovresti utilizzarlo in un intent con startActivity() se hai dati che l'utente può condividere tramite un'altra app, ad esempio un'app email o un'app di condivisione sui 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 in Settings per le azioni che aprono schermate specifiche nell'app Impostazioni del sistema.

Puoi specificare l'azione per un intent con setAction() o con un costruttore Intent.

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 intervenire 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 si crea 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. Specificare il tipo MIME dei tuoi dati aiuta il sistema Android a 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 URI content: indica che i dati si trovano sul dispositivo e sono controllati da un ContentProvider, 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, chiama setType(). Se necessario, puoi impostarli entrambi in modo esplicito con setDataAndType().

Attenzione:se vuoi impostare sia il tipo di URI sia il tipo MIME, non chiamare setData() e setType(), perché ognuna annulla il valore dell'altro. Usa sempre setDataAndType() 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 intent è possibile inserire un numero qualsiasi di descrizioni di categoria, ma la maggior parte degli intent 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 nell'Avvio applicazioni del sistema.

Per l'elenco completo delle categorie, consulta la descrizione del corso Intent.

Puoi specificare una categoria con addCategory().

Le proprietà elencate sopra (nome componente, azione, dati e categoria) rappresentano le caratteristiche che definiscono un intent. Leggendo queste proprietà, il sistema Android è in grado di risolvere il componente dell'app da avviare. Tuttavia, un intent può trasferire informazioni aggiuntive che non influiscono sulla sua risoluzione in un componente dell'app. Un intent può anche fornire le seguenti informazioni:

Extra
Coppie chiave-valore che trasportano le informazioni aggiuntive necessarie per compiere l'azione richiesta. Così come alcune azioni utilizzano particolari tipi di URI di dati, anche altre utilizzano particolari extra.

Puoi aggiungere ulteriori dati con vari metodi putExtra(), ognuno dei quali accetta due parametri: il nome della chiave e il valore. Puoi anche creare un oggetto Bundle con tutti i dati aggiuntivi, quindi inserire Bundle in Intent con putExtras().

Ad esempio, durante la creazione di un intent per inviare un'email con ACTION_SEND, puoi specificare il destinatario to con la chiave EXTRA_EMAIL e l'oggetto con la chiave EXTRA_SUBJECT.

La classe Intent specifica molte costanti EXTRA_* 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 o Serializable quando invii un intent che ti aspetti di ricevere da un'altra app. Se un'app tenta di accedere ai dati di un oggetto Bundle, ma non ha accesso alla classe pacchettizzata o serializzata, il sistema genera un RuntimeException.

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 appartiene all'elenco delle attività recenti).

Per ulteriori informazioni, consulta il metodo setFlags().

Esempio di intent esplicito

Un intent esplicito è quello che utilizzi per avviare uno specifico componente dell'app, ad esempio un'attività o un servizio specifico nell'app. Per creare un intent esplicito, definisci il nome del componente per l'oggetto Intent. Tutte le altre proprietà per intent sono facoltative.

Ad esempio, se nella tua app hai creato un servizio denominato DownloadService, progettato per scaricare un file dal web, puoi iniziarlo con il seguente codice:

Kotlin

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
val downloadIntent = Intent(this, DownloadService::class.java).apply {
    data = Uri.parse(fileUrl)
}
startService(downloadIntent)

Java

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

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 in grado di 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 vuoi che l'utente condivida dei contenuti con altre persone, crea un intent con l'azione ACTION_SEND e aggiungi degli extra 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 di testo). Se c'è solo un'app in grado di gestirla, questa si apre immediatamente e ne viene definito l'intento. 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 mostra 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.

Figura 2. Una finestra di dialogo di selezione.

Forzare un selettore di app

Se più app rispondono al tuo intent implicito, l'utente può selezionare quale app utilizzare e impostarla come scelta predefinita per l'azione. La possibilità di selezionare un valore predefinito è utile quando si esegue un'azione per cui 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 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 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 "Condividi" con l'azione ACTION_SEND, gli utenti potrebbero voler condividere contenuti utilizzando un'app diversa a seconda della loro situazione attuale, quindi dovresti sempre utilizzare la finestra di dialogo del selettore, come mostrato nella 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 navigare tra i componenti all'interno dell'app 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 lancio 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:

  1. La tua app separa un intent nidificato dagli extra di un intent caricato.
  2. La tua app avvia immediatamente un componente dell'app utilizzando l'intent nidificato, ad esempio passando l'intent a startActivity(), startService() o bindService().

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.

Controlla la presenza di lanci di intent non sicuri

Per verificare la presenza di lanci per 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 di StrictMode, potresti 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());
}

Usare gli intent 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 operazioni di sanificazione e convalida necessarie. 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 non esportato, esegui la sanificazione e convalida gli extra prima di copiarli nell'intent che avvia il componente.

Non esportare i componenti della tua app inutilmente. Ad esempio, se intendi avviare un componente dell'app utilizzando un intent nidificato interno, imposta l'attributo android:exported del componente su false.

Utilizza un elemento PendingIntent anziché un intent nidificato. In questo modo, quando un'altra app separa l'elemento PendingIntent di 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 dall'app (client) a un'altra app (di servizio) e torna alla tua app:

  1. 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 richiama un componente nella tua app; questo componente non viene esportato.
  2. Dopo aver ricevuto l'intent dell'app, l'altra app estrae l'oggetto PendingIntent nidificato.
  3. L'altra app richiama il metodo send() sull'oggetto PendingIntent.
  4. 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 ciascuno dei componenti 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 invia un intent implicito al componente dell'app solo se l'intent può trasmettere attraverso uno dei tuoi filtri per intent.

Nota: un intent esplicito viene sempre inviato al relativo target, indipendentemente dai filtri per intent dichiarati dal componente.

Un componente dell'app deve dichiarare filtri separati per ciascun 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 filtro per modificare un'immagine. All'avvio dell'attività, l'attività controlla 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 ricevitore per un'attività, un servizio o un broadcast nella tua app utilizza filtri per intent e non imposta esplicitamente il valore per android:exported, l'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ù dei seguenti tre elementi:

<action>
Dichiara che 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 accettato utilizzando uno o più attributi che specificano vari aspetti dell'URI di dati (scheme, host, port, path) e del tipo MIME.
<category>
Dichiara che la categoria di intent è accettata nell'attributo name. Il valore deve essere il valore letterale della 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 metodi startActivity() e startActivityForResult() trattano tutti gli intent come se avessero dichiarato la categoria CATEGORY_DEFAULT. Se non dichiari questa categoria nel filtro per intent, nessun intent implicito verrà risolto per la 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 è 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 essere certo che il componente sia in grado di gestire tutte le combinazioni di questi elementi di filtro.

Quando 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 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 per intent, un intent che non passa attraverso uno dei filtri di un componente potrebbe passare attraverso un altro filtro. Per ulteriori informazioni su come il sistema risolve gli intent, consulta la sezione sulla risoluzione degli intent di seguito.

Attenzione : l'utilizzo di un filtro per intent non è un modo sicuro per impedire ad altre app di avviare i componenti. Sebbene i filtri per intent limitino un componente in modo che risponda 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.

Allo stesso modo, per evitare di eseguire inavvertitamente l'elemento Service di un'app diversa, utilizza 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 broadcast receiver possono essere registrati in modo dinamico chiamando registerReceiver(). Potrai quindi annullare la registrazione del destinatario con unregisterReceiver(). In questo modo la tua app può ascoltare trasmissioni specifiche solo per un periodo di tempo specificato mentre è in esecuzione.

Filtri di esempio

Per dimostrare alcuni dei comportamenti dei filtri per intent, ecco un esempio tratto dal file manifest di un'app di 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 avvia 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 sugli intent.
  • La categoria CATEGORY_LAUNCHER indica che l'icona di questa attività deve essere posizionata in Avvio applicazioni del sistema. Se per l'elemento <activity> non è specificata un'icona con icon, il sistema utilizza l'icona dell'elemento <application>.

Per visualizzare l'attività in Avvio applicazioni, è necessario che questi due elementi siano accoppiati.

La seconda attività, ShareActivity, ha lo scopo di facilitare la condivisione di testi e contenuti multimediali. Anche se gli utenti potrebbero 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 di Google panorama.

Abbina 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 dell'app solo se il tuo 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.

Allo stesso modo, 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 tale intent corrisponde alle azioni e alle categorie di un elemento <intent-filter> dichiarato dalla tua app. Questo comportamento si verifica indipendentemente dalla versione dell'SDK di destinazione dell'app di invio.

Nei seguenti casi, la corrispondenza dell'intent non viene applicata in modo forzato:

  • Intent pubblicati a componenti che non dichiarano filtri per intent.
  • Intent provenienti dall'interno della stessa app.
  • Intent provenienti dal sistema, ovvero gli intent inviati dall'"UID di sistema" (uid=1000). Le app di sistema includono system_server e le app che impostano android:sharedUserId su android.uid.system.
  • Intent che hanno origine dalla directory principale.

Scopri di più sulla corrispondenza dell'intent.

Utilizzo di un intent in attesa

Un oggetto PendingIntent è un wrapper attorno a un oggetto Intent. Lo scopo principale di un PendingIntent è concedere a un'applicazione esterna l'autorizzazione a utilizzare il Intent contenuto come se fosse stato eseguito dalla procedura della tua app.

I principali casi d'uso relativi a un intento in sospeso includono quanto segue:

  • Dichiarazione di un intent da eseguire quando l'utente esegue un'azione con la tua notifica (il NotificationManager del sistema Android esegue Intent).
  • Dichiarazione di un intent da eseguire quando l'utente esegue un'azione con il widget dell'app (l'app nella schermata Home esegue Intent).
  • Dichiarazione di un intent da eseguire in un momento futuro specificato (il AlarmManager del sistema Android esegue il Intent).

Così come ogni oggetto Intent è progettato per essere gestito da un tipo specifico di componente dell'app (Activity, Service o BroadcastReceiver), è necessario creare anche un elemento PendingIntent prendendo la stessa considerazione. Quando utilizzi un intent in attesa, la tua app non esegui l'intent con una chiamata come startActivity(). Devi dichiarare il tipo di componente previsto quando crei PendingIntent chiamando il rispettivo metodo dell'autore:

A meno che la tua app riceva intent in attesa da altre app, i metodi precedenti per creare un elemento PendingIntent sono probabilmente gli unici PendingIntent metodi 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 sospeso, 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 dall'app. Per dichiarare che un determinato oggetto PendingIntent è modificabile 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 quando possibile

Nella maggior parte dei casi, l'app dovrebbe creare oggetti PendingIntent immutabili, come mostrato nel seguente snippet di codice. Se un oggetto PendingIntent è immutabile, le altre app non possono modificare l'intent per regolare il risultato della chiamata 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 modificabili:

  • Supportare le azioni di risposta diretta nelle notifiche. La risposta diretta richiede una modifica ai dati del clip nell'oggetto PendingIntent associato alla risposta. In genere, richiedi questa modifica passando FILL_IN_CLIP_DATA come flag al metodo fillIn().
  • Associazione delle notifiche al framework Android Auto mediante istanze di CarAppExtender.
  • Inserire conversazioni in bolli utilizzando le istanze di PendingIntent. Un oggetto PendingIntent modificabile consente al sistema di applicare i flag corretti, come FLAG_ACTIVITY_MULTIPLE_TASK e FLAG_ACTIVITY_NEW_DOCUMENT.
  • Richiedere informazioni sulla posizione del dispositivo chiamando requestLocationUpdates() o API simili. L'oggetto PendingIntent modificabile consente al sistema di aggiungere elementi 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 provider.
  • Programmazione delle sveglie utilizzando AlarmManager. L'oggetto PendingIntent modificabile consente al sistema di aggiungere l'intent EXTRA_ALARM_COUNT extra. Questo extra rappresenta il numero di volte in cui è stata attivata una sveglia ricorrente. 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 modificabile, ti consigliamo vivamente di utilizzare un intent esplicito e di compilare il ComponentName. In questo modo, ogni volta che un'altra app richiama PendingIntent e ritrasmette il controllo alla tua app, viene sempre avviato lo stesso componente nell'app.

Utilizza 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, segui questi passaggi:

  1. Verifica che i campi relativi all'azione, al pacchetto e al componente dell'intent di base siano impostati.
  2. Utilizza l'elemento FLAG_IMMUTABLE, aggiunto in Android 6.0 (livello API 23), per creare intent in attesa. Questo flag impedisce alle app che ricevono un PendingIntent di compilare le proprietà non compilate. Se lo minSdkVersion 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'intent confrontandola con i filtri per 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 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 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 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 possa superare il test della 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 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 la tua attività riceva intent impliciti, devi includere una categoria per "android.intent.category.DEFAULT" nei filtri per intent, come mostrato nell'esempio <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 non sono specificati sia lo schema sia l'host, 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 quello 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, autorità e 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 il tipo URI e MIME nell'intent con un URI e il tipo MIME specificati nel filtro. Le regole sono le seguenti:

  1. Un intent che non contiene né un URI né un tipo MIME supera il test solo se il filtro non specifica alcun URI o tipo MIME.
  2. Un intent che contiene un URI ma nessun tipo MIME (né esplicito né dedotto dall'URI) supera il test solo se il suo URI corrisponde al formato URI del filtro e il filtro non specifica allo stesso modo un tipo MIME.
  3. 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.
  4. Un intent che contiene sia un URI sia un tipo MIME (esplicito o dedotto dall'URI) passa la parte del test relativa al tipo MIME solo se quel tipo corrisponde a un tipo elencato nel filtro. Passa la parte URI del test se il suo URI corrisponde a un URI nel filtro o se ha un URI content: o file: e il filtro non specifica un URI. In altre parole, si presume che un componente supporti i dati content: e file: se il suo filtro elenca solo un tipo MIME.

Nota: se un intent 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>.

Quest'ultima regola, la regola (d), riflette l'aspettativa che i componenti siano in grado di ottenere dati locali da un file o da un fornitore di contenuti. Di conseguenza, i loro 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> comunica ad Android che il componente può recuperare dati 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 dispensata 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 scoprire un componente target da attivare, ma anche per scoprire qualcosa del set di componenti del 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 all'app Home. PackageManager ha un insieme di metodi query...() che restituiscono tutti i componenti in grado di accettare un determinato 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 e queryIntentServices() restituisce un elenco di servizi simile. Nessun metodo attiva i componenti: si limitano a elencare quelli che possono rispondere. Esiste un metodo simile, queryBroadcastReceivers(), per i broadcast receiver.