Intent e filtri di intent

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, ci sono tre casi d'uso fondamentali:

  • Avviare un'attività

    L'elemento Activity rappresenta una singola schermata di un'app. Puoi avviare una nuova di un Activity passando un Intent a startActivity(). Intent descrive l'attività da avviare e contiene tutti i dati necessari.

    Se vuoi ricevere un risultato dall'attività al termine, chiama 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 maggiori informazioni su JobScheduler, consulta la pagina corrispondente.

    Per le versioni precedenti ad Android 5.0 (livello API 21), puoi avviare un servizio utilizzando della classe Service. Puoi avviare un servizio per eseguire un'operazione una tantum (ad esempio il download di un file) passando un 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 eseguire il binding al servizio da un altro componente passando un Intent a bindService(). 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 a sendBroadcast() o sendOrderedBroadcast().

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'intento, specificando un valore 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 menzionano un componente specifico, ma dichiarano un'azione generale. che viene gestito da un componente di un'altra app. Ad esempio, se vuoi mostrare all'utente una posizione su una mappa, potete utilizzare un intento implicito per richiedere che un altro 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 in modo esplicito un componente di attività specifico, ovvero il sistema avvia immediatamente quel componente.

Figura 1. In che modo viene considerato un intent implicito inviati tramite il sistema per iniziare un'altra attività: [1] l'attività A crea un Intent con una descrizione dell'azione e la passa a startActivity(). [2] Il sistema Android cerca tutti app per un filtro per intent che corrisponde all'intent. Quando viene trovata una corrispondenza, [3] il sistema avvia l'attività corrispondente (Attività B) richiamando il relativo metodo onCreate() e passandogli 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 nella 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 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à, permetti ad altre app di iniziare direttamente la tua attività con un determinato tipo di intento. Allo stesso modo, se non dichiari alcun filtro per intent per un'attività, quest'ultima può essere avviata solo con un intento 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 implicita per avviare un servizio rappresenta un pericolo per la sicurezza perché non puoi sapere con certezza 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 implicita.

Creazione di un'intenzione

Un oggetto Intent contiene informazioni utilizzate dal sistema Android per determinare quale componente avviare (ad esempio il nome esatto del componente o la categoria del componente che deve ricevere l'intent), oltre alle informazioni utilizzate dal componente destinatario per eseguire correttamente l'azione (ad esempio l'azione da eseguire e i dati su cui intervenire).

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 il nome di un componente, l'intent è implicito e la decide quale componente deve ricevere l'intent in base alle altre informazioni sull'intent (come l'azione, i dati e la categoria descritti di seguito). Se devi avviare un componente specifico nella tua app, devi specificare il nome del componente.

Nota: quando avvii 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 oggetto ComponentName, che puoi specificare utilizzando un nome di classe completamente qualificato 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 Intent.

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

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 affinché possano essere utilizzate dagli intent all'interno della tua app (o da altre app per richiamare i componenti dell'app), ma in genere specifichi le costanti di azione definita dalla classe Intent o da altre classi framework. Ecco alcune azioni comuni per avviare un'attività:

ACTION_VIEW
Utilizza questa azione in un intent con startActivity() quando hai a disposizione alcune informazioni che un'attività può essere mostrata all'utente, come una foto da visualizzare in un'app Galleria o un indirizzo a visualizzali 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 alla classe Intent per altre costanti che definiscono azioni generiche. Sono definite altre azioni altrove nel framework 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 illustrato nell'esempio seguente:

KotlinJava
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Dati
L'URI (un oggetto Uri) che fa riferimento ai dati a cui essere intervenuto su e/o il tipo MIME dei dati. Il tipo di dati fornito è generalmente dettato dall'azione dell'intent. Per Ad esempio, se l'azione è ACTION_EDIT, i dati devono contenere la macro 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à per riprodurre un file audio, anche se i formati URI potrebbero essere simili. Specificare il tipo MIME dei tuoi dati aiuta Android a individuare il componente migliore per ricevere l'intento. Tuttavia, a volte il tipo MIME può essere dedotto dall'URI, in particolare quando i dati sono 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 impostare entrambi esplicitamente con setDataAndType().

Attenzione: se vuoi impostare sia l'URI sia il tipo MIME, non chiama setData() e setType() perché annullano ciascuno il valore dell'altro. Usa sempre setDataAndType() per impostarli entrambi URI e 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 consente di 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.

Leggi la descrizione del corso Intent per l'elenco completo categorie.

Puoi specificare una categoria con addCategory().

Queste proprietà elencate sopra (nome componente, azione, dati e categoria) rappresentano che definiscono le caratteristiche di un'intenzione. Leggendo queste proprietà, il sistema Android è in grado di risolvere quale componente dell'app deve avviare. Tuttavia, un'intent può contenere informazioni aggiuntive che non influiscono sul modo in cui viene risolta in un componente dell'app. Un'intenzione può fornire anche le seguenti informazioni:

Extra
Coppie chiave-valore che contengono informazioni aggiuntive necessarie per eseguire l'azione richiesta. Così come alcune azioni utilizzano tipi specifici di URI dati, alcune azioni utilizzano anche extra specifici.

Puoi aggiungere altri dati con vari metodi di putExtra(), accettano due parametri: il nome della chiave e il valore. Puoi anche creare un oggetto Bundle con tutti i dati aggiuntivi, quindi inserireBundle in Intent con putExtras().

Ad esempio, quando crei un'intenzione per inviare un'email con ACTION_SEND, puoi specificare il destinatario a con la chiave EXTRA_EMAIL e il oggetto con la chiave EXTRA_SUBJECT.

La classe Intent specifica molte costanti EXTRA_* per tipi di dati standardizzati. Se devi dichiarare le tue chiavi aggiuntive (per intent che riceve l'app), assicurati di includere il nome del pacchetto dell'app come prefisso, come illustrato nell'esempio seguente:

KotlinJava
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

Attenzione: non usare Parcelable o Dati di Serializable quando invii un intent previsto un'altra app da ricevere. Se un'app tenta di accedere ai dati in un oggetto Bundle ma non alla classe parceled o serializzata, il sistema genera RuntimeException.

Flag
I flag sono definiti nella classe Intent e fungono da metadati per l'intento. I flag possono indicare al sistema Android come avviare un'attività (ad esempio, attività l'attività deve appartenere e come trattarla dopo il suo lancio (ad esempio se appartiene all'elenco dei attività).

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 per un'attività o un servizio specifico nella tua app. Per creare un intento esplicito, definisci il nome del componente per l'oggetto Intent, le altre proprietà di intent sono facoltative.

Ad esempio, se nella tua app hai creato un servizio chiamato DownloadService, progettato per scaricare un file dal Web, puoi avviarlo con il seguente codice:

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

Intent(Context, Class) fornisce l'app Context e componente di un oggetto Class. Di conseguenza, questo intent avvia esplicitamente la classe DownloadService nell'app.

Per ulteriori informazioni su come creare e avviare un servizio, consulta 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 implicita è utile quando la tua app non può eseguire l'azione, ma altre app probabilmente possono farlo e vuoi che l'utente scelga quale app 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 extra che specificano i contenuti da condividere. Quando chiami startActivity() con questo intento, l'utente può scegli un'app con cui condividere i contenuti.

KotlinJava
// 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.
}
// 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 ActivityNotFoundException che si verifica. Se più attività accettano l'intent, il sistema mostra una finestra di dialogo come quella della Figura 2, in modo che l'utente possa scegliere l'app da utilizzare.

Nella guida sono disponibili anche ulteriori informazioni sull'avvio di altre app sull'invio all'utente di un'altra app.

Figura 2. Una finestra di dialogo di selezione.

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 usare un altro dell'app, dovresti mostrare esplicitamente una finestra di dialogo. La finestra di dialogo del selettore chiede l'utente può selezionare l'app da utilizzare per l'azione (l'utente non può selezionare un'app predefinita 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 sua situazione attuale, quindi dovresti sempre usare 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.

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

Rilevare l'avvio 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 avvio non sicuro di un intent nidificato, che viene passato come extra in un altro intento.

Se la tua app esegue entrambe le seguenti azioni, il sistema rileva un lancio di intent e una violazione di StrictMode si verifica:

  1. La tua app separa un intent nidificato dagli extra di un intent inserito.
  2. La tua app avvia immediatamente un'app utilizzando quell'intent nidificato, come trasferire l'intento startActivity(), startService(), o bindService()

Per ulteriori dettagli su come identificare questa situazione e apportare modifiche alla tua app, leggi il post del blog sulla Nesting di Android Intenzioni 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 nello snippet di codice riportato di seguito. Se rileva una violazione di StrictMode, potresti interrompere l'esecuzione dell'app e proteggere le informazioni potenzialmente sensibili.

KotlinJava
fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build())
}
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 e esegui eventuali operazioni necessarie igienizzazione e convalida. La tua app potrebbe copiare gli extra da un intent a un altro intent usato per avviare un nuovo componente. Questo accade quando la tua app chiama putExtras(Intent) o putExtras(Bundle). Se la tua app esegue una di queste operazioni, copia solo gli extra previsti dal componente di destinazione. Se l'altro intent (che riceve la copia) avvia un componente che non è esportato, sanificare convalidare gli extra prima di copiarli nell'intento che avvia di strumento di authoring.

Non esportare i componenti della tua app inutilmente. Ad esempio, se avviare un componente dell'app utilizzando un intent nidificato interno, impostare l'attributo android:exported del componente a 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 dal tuo (client) app a un'altra app (di servizio) e tornare alla tua app:

  1. La tua app crea un intent che richiama un'attività in un'altra app. Entro questo intent, aggiungi un oggetto PendingIntent come extra. Questo intent in attesa richiama un componente nella tua app; questo componente non viene esportato.
  2. Quando riceve l'intent dell'app, l'altra estrae i valori nidificati PendingIntent oggetto.
  3. L'altra app richiama il metodo send() sull'oggetto PendingIntent.
  4. Dopo aver ritrasferito il controllo all'app, il sistema richiama lo stato usando il contesto della tua app.

Figura 2. Diagramma della comunicazione tra app quando si utilizza un oggetto Pending nidificato l'intento.

Ricezione di un intent implicito

Per pubblicizzare gli intent impliciti che la tua app può ricevere, dichiara uno o più filtri per intent per per ciascuno dei componenti della tua app con un <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 intent esplicito viene sempre consegnato al suo target, a prescindere dai filtri di intent dichiarati dal componente.

Un componente dell'app deve dichiarare filtri separati per ogni job univoco che può fare. 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. Quando inizia l'attività, controlla Intent e decide come comportarsi in base alle informazioni in Intent (ad esempio per mostrare o meno i controlli dell'editor).

Ogni filtro per intent viene definito da un elemento <intent-filter>. nel file manifest dell'app, nidificato nel componente dell'app corrispondente (ad esempio come <activity> ).

In ogni componente dell'app che include un elemento <intent-filter>, imposta esplicitamente un valore android:exported Questo attributo indica se il componente dell'app è accessibile ad altre app. In alcuni ad esempio le attività i cui filtri per intent includono 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 che l'azione intent accettata nell'attributo name. Il valore deve essere il valore di 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 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 deve includere CATEGORY_DEFAULT nel filtro per intent. I metodi startActivity() e startActivityForResult() tratta tutti gli intent come se avesse dichiarato la categoria CATEGORY_DEFAULT. Se non dichiari questa categoria nel filtro per intent, nessun intent implicito verrà risolto in la 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 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ù istanze di <action>, <data> o <category>. In questo caso occorre assicurarsi che il componente sia in grado di gestire combinazioni di questi elementi di filtro.

Quando vuoi gestire più tipi di intent, ma solo in determinate combinazioni di l'azione, i dati e il tipo di categoria, devi creare più filtri per 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 trovare una corrispondenza per neanche uno di questi, il sistema Android non invia 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 per intent limitino un componente in modo che risponda solo 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 tuoi componenti, non dichiarare i filtri intent nel file manifest. Imposta invece Attributo exported a "false" per quel componente.

Analogamente, per evitare di eseguire inavvertitamente Service, usa sempre un intento esplicito 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(). Dopodiché puoi annullare la registrazione del ricevitore 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 nel 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 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 app del sistema. Se l'elemento <activity> non specifica 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 contenuti. Anche se gli utenti potrebbero accedere a questa attività da MainActivity, può anche inserire ShareActivity direttamente da un'altra app che emette una stringa 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 foto panoramiche, che puoi gestire con Google le API panoramiche.

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 le tue solo se quest'ultimo 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 dall'interno della 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 su android.uid.system.
  • Intent provenienti dalla radice.

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 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 (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 della 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 una tipo di componente dell'app (Activity, Service o BroadcastReceiver), anche un PendingIntent deve essere creati 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 l'oggetto PendingIntent chiamando il rispettivo metodo del creator:

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 prende l'app Context corrente, il Intent che vuoi avvolgere e uno o più flag che specificano come deve essere utilizzato l'intent (ad esempio se può essere utilizzato più volte).

Per saperne di più sull'utilizzo degli intent in attesa, consulta la documentazione di ogni dei rispettivi casi d'uso, ad esempio nella scheda Notifiche e le guide dell'API 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 nessuno dei flag di mutabilità, 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.

Crea intent in attesa immutabili quando 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, le altre app non possono modificare l'intent per regolare il risultato della chiamata al l'intento.

KotlinJava
val pendingIntent = PendingIntent.getActivity(applicationContext,
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE)
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE);

Tuttavia, alcuni casi d'uso richiedono invece oggetti PendingIntent modificabili:

  • Supporto delle azioni di risposta diretta nelle notifiche. La La risposta diretta richiede una modifica ai dati del clip nell'oggetto PendingIntent associato alla risposta. In genere, per richiedere questa modifica, FILL_IN_CLIP_DATA come flag per fillIn() .
  • Associare le notifiche al framework Android Auto utilizzando istanze di CarAppExtender.
  • Inserire conversazioni in bolli utilizzando le istanze di PendingIntent. Un oggetto PendingIntent mutabile consente al sistema di applicare i flag corretti, ad esempio FLAG_ACTIVITY_MULTIPLE_TASK e FLAG_ACTIVITY_NEW_DOCUMENT.
  • Richiesta di informazioni sulla posizione del dispositivo chiamando requestLocationUpdates() o API simili. L'oggetto mutabile PendingIntent consente al sistema di aggiungere parametri aggiuntivi dell'intent che rappresentano gli eventi del ciclo di vita della posizione. Questi eventi includono un cambiamento nella località e la disponibilità di un fornitore.
  • Programmazione delle sveglie utilizzando AlarmManager. L'oggetto PendingIntent modificabile consente al sistema di aggiungere EXTRA_ALARM_COUNT intent extra. Questo extra rappresenta il numero di volte in cui una sveglia ricorrente è stata attivata. Contenendo questo elemento aggiuntivo, l'intent può informare con precisione un'app se una sveglia ripetuta è stata attivata più volte, ad esempio quando il dispositivo era in sospensione.

Ti consigliamo vivamente di creare un oggetto PendingIntent modificabile nella tua app di utilizzare un intent esplicito e di compilare ComponentName. In questo modo, ogni volta che un'altra app richiama l'PendingIntent e ritrasmette il controllo alla tua app, sempre lo stesso componente nella tua app.

Utilizza intent espliciti all'interno di intent in attesa

Per definire meglio il modo in cui altre app possono usare gli intent in attesa della tua app, aggregare un intent in sospeso a un intent esplicito. Per seguire questa best practice, svolgi i seguenti passaggi:

  1. Verifica che i campi relativi all'azione, al pacchetto e al componente dell'intent di base sono già impostati.
  2. Usa FLAG_IMMUTABLE, aggiunta in Android 6.0 (livello API 23) per creare intent in attesa. Questo flag impedisce alle app che ricevono un PendingIntent di compilare proprietà non compilate. Se il valore minSdkVersion della tua app è 22 o inferiore, puoi garantire sicurezza e compatibilità con il seguente codice:

    if (Build.VERSION.SDK_INT >= 23) {
      // Create a PendingIntent using FLAG_IMMUTABLE.
    } else {
      // Existing code that creates a PendingIntent.
    }

Risoluzione dell'intent

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 associati ai componenti appropriati in base alla dichiarazione del filtro per intent nel file manifest di un'app.

Test azione

Per specificare le azioni per intent accettate, un filtro per intent può dichiararne zero o più <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'è niente per intent a trovare una corrispondenza, in modo che tutti gli intent non superino 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>

Per avere l'intenzione di superare il test, ogni categoria in Intent deve corrispondere a una categoria del filtro. Non è necessario l'inverso: il filtro per intent potrebbe dichiarare più categorie di quelle specificate in Intent e Intent risulta ancora 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 <data> può specificare una struttura URI e un tipo di dati (tipo multimediale MIME). Ogni parte dell'URI è un separato attributo: 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'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à passano il filtro, indipendentemente dai relativi percorsi.
  • Se un filtro specifica uno schema, un'autorità e un percorso, solo gli URI con lo stesso schema, l'autorità di controllo e il percorso superano il filtro.

Nota: una specifica del percorso può contenere un asterisco (*) 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 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 passa il carattere 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 dal l'URI) supera il test solo se il suo URI corrisponde al formato URI del filtro e il filtro non specifica 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 dal URI) passa la parte del test di tipo MIME solo se corrisponde a uno dei tipi elencati nel filtro. Passa la parte URI del test se il suo URI corrisponde a un URI nel filtro o se ha un valore content: o file: e il filtro non specifica alcun URI. In altre parole, si presume che un componente supporti i dati content: e file: se il 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>.

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. 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ò ricevere dati immagine da un contenuto e lo visualizziamo:

<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. Per 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 il programma di avvio app trovandovi tutte le attività con filtri di intent che specificano l'azione ACTION_MAIN e la categoria CATEGORY_LAUNCHER. Una corrispondenza è valida solo se le azioni e le categorie nell'intent corrispondono al filtro, come descritto nella documentazione della classe IntentFilter.

La tua applicazione può utilizzare la corrispondenza degli intent in modo simile all'app Home. PackageManager ha un set di query...() che restituiscono tutti i componenti che possono accettare un particolare intento una serie simile di metodi resolve...() che determinano componente per rispondere a un intento. Ad esempio: queryIntentActivities() restituisce un elenco di tutte le attività che possono essere eseguite 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.