Il concetto di intent è, a mio avviso, il concetto più interessante introdotto da Google per la programmazione di applicazioni su Android. Un intent è un oggetto (generato dall'omonima classe
Intent
, nel package android.content
) che descrive un'azione da far effettuare a un componente su un insieme di dati. Si tratta, in altre parole, di un meccanismo che consente, attraverso lo scambio di oggetti Intent
, di avviare altri componenti della stessa applicazione oppure, cosa assai più interessante, di avviare componenti di altre applicazioni!Come già detto in precedenza, le activity di un'applicazione realizzano una oppure tutte le funzionalità previste per l'interazione con l'utente. Molto dipende dalla complessità dell'applicazione e da come essa viene scritta dal programmatore. Un'activity di un'applicazione può realizzare, con la dovuta efficienza, una precisa funzionalità al punto che la stessa sia poi richiesta all'interno di altre applicazioni.
Se sul dispositivo esiste già un componente capace di portare a termine l'azione necessaria all'applicazione, il programmatore, anziché scrivere altro codice, può riciclare quest'ultimo attraverso l'invocazione di un intent. La risoluzione di un intent può inoltre stabilire la presenza di più componenti capaci di portare a termine la stessa azione. In tal caso verrà offerto all'utente la possibilità di decidere quale componente attivare per far proseguire l'applicazione.
Un intent può avviare, con metodi diversi, activity, service o broadcast receiver. In questo articolo mi occuperò dei metodi e dei meccanismi che Android mette a disposizione del programmatore per l'avvio di activity.
E' possibile ricondurre un'activity in una delle seguenti classi:
- intent espliciti: sono intent che avviano un componente già noto, rintracciandolo attraverso il nome a lui assegnato;
- intent impliciti: sono intent che tentano di avviare un componente le cui caratteristiche coincidono con quelle descritte nell'intent stesso;
Il meccanismo risolutivo per gli intent impliciti va sotto il nome di intent resolution. Per stabilire i migliori componenti presenti sul dispositivo e capaci di aiutare l'activity chiamante, il meccanismo di intent resolution cerca di accoppiare le caratteristiche date all'oggetto Intent con quelle dichiarate dalle activity nel proprio manifesto (il file
AndroidManifest.xml
), con elementi intent-filter
.Gli intent-filter descrivono, allora, le competenze e le capacità delle activity installate sul dispositivo. Un activity senza intent-filter riceverà, pertanto, solo intent espliciti.
L'istanza di un oggetto
Intent
è caratterizzata dai seguenti campi:ComponentName
: è il nome del componente che dovrebbe ricevere l'intent e gestirne, eventualmente, i contenuti extra (spiegherò in avanti come). Si tratta di un parametro usato, quindi, per l'invocazione di intent espliciti. E' possibile passarlo a un costruttore oppure impostarlo in un secondo momento, attraverso i metodisetComponent()
,setClass()
oppuresetClassName()
. Un intent esplicito verrà sempre preferito a un intent implicito, ignorando, se presenti nell'oggetto, tutte le altre proprietà in esso contenute. La classeComponentName
offre diversi costruttori (come ad esempioComponentName(String package_name,String class_name)
,ComponentName(Context package,String class_name)
eComponentName(Context package, Class class)
) ognuno di questi permette di identificare uno specifico componente di un'applicazione. Il metodogetComponent()
ritorna ilComponentName
impostato per l'intent;Action
: rappresenta, con una stringa, l'azione da eseguire. Nella classeIntent
sono definite molte azioni di default e altre possono essere aggiunte dal programmatore. I metodisetAction()
egetAction()
permettono, rispettivamente, di impostare e leggere l'azione espressa nell'intent. Per un elenco completo delle costanti di default vi invito a consultare la documentazione in linea della classe Intent. E' preferibile rispettare, per la descrizione dell'action, la seguente sintassi:package_name.intent.action.ACTION
. Alcune action più usate sonoACTION_MAIN
(per avviare un'activity segnata come activity principale) eACTION_EDIT
(a cui corrisponde l'azione per la modifica dei dati passati). Un'activity dichiara nel proprio manifesto le azioni che è in grado di gestire attraverso elementiaction
, a cui va passato il parametroaction:name
con la stringa dell'azione;Data
: indica l'indirizzo (con un URI) e il tipo di dati su cui agire. Può essere impostato consetData()
,setType()
osetDataAndType()
. Le activity dichiarano nel loro manifesto (con elementidata
e attributoandroid:mimeType
) le tipologie di dati che sono in grado di manipolare. Il tipo di dati permette di inquadrare meglio l'azione da effettuare che altrimenti rimarrebbe generica. Ad esempio, con l'azioneACTION_EDIT
si possono editare immagini e/o testo. Se però indichiamo nel manifesto la proprietàandroid:mimeType=image/*
per l'elemento data del filter-intent ne aumentiamo sicuramente la descrizione (in questo caso l'azioneACTION_EDIT
è dunque orientata alla modifica di immagini). Il riferimento ai dati è specificato per mezzo della sintassischeme://host:port/path
. Ogni singolo elemento della sintassi appena vista prevede il proprio attributo da sistemare all'interno del manifesto (se necessario). Esistono infatti, per l'elementodata
, gli attributiandroid:host
,android:port
,android:path
eandroid:scheme
. L'attributohost
eport
formano assieme l'authority
e permette di individuare il content provider;Category
: permette di suggerire nell'intent le activity in base al compito che queste hanno all'interno di altre applicazioni. Come per le azioni, la classeIntent
definisce molte categorie. Vi invito pertanto a vedere un elenco completo delle categorie disponibili sulla guida in linea per Android. Ad esempio, le activity che figurano all'interno del launcher di Android (quelle che avviano l'applicazione, con l'azioneACTION_MAIN
) hanno tutte la categoriaCATEGORY_LAUNCHER
. I metodiaddCategory()
egetCategory()
permettono, rispettivamente, di aggiungere o leggere il nome di una categoria. La categoria di un un'activity viene dichiarata all'interno del manifesto attraverso l'elementocategory
e l'attributoandroid:category
(che contiene la stringa che identifica la categoria);Extras
: sono coppie chiave-valore associate all'intent attraverso il metodoputExtras(Bundle extras)
. L'activity ricevente può estrarre i valori aggiunti in fase di richiesta con il metodogetExtras()
. La classeIntent
dispone di molti metodi set, il valore inserito viene legato a una chiave che ne permette in seguito la restituzione, con uno dei metodi get (dipende dal tipo aggiunto all'intent). Possiamo assegnare all'intent anche un oggetto a patto di dichiarare la classe dell'oggetto come parte dell'interfacciaSerializable
(e usare il metodoputExtras(String key,Serializable obj)
). Trovate un elenco completo dei metodi per gli extras nella documentazione in linea di Android;Flags
: sono impostazioni di vario tipo usate dal sistema operativo per caratterizzare l'avvio di un'activity. Ogni impostazione viene passata con il metodosetFlags()
, a cui va passata una costante (definita nella classeIntent
) che la rappresenta. Come al solito, trovate nella guida in linea un elenco completo dei flag. Quelli più usati sonoFLAG_ACTIVITY_SINGLE_TOP
(che permette di non avviare una nuova istanza per l'activity se già presente, l'activity verrà riciclata) eFLAG_ACTIVITY_NEW_TASK
(che avvia l'activity all'interno di un nuovo task che ha la stessa affinità);
Dopo aver caratterizzato un intent con le proprietà volute, possiamo avviare la sua risoluzione (che avverrà a run-time) attraverso il metodo
startActivity()
. Ecco, ad esempio, un pezzo di codice che crea un intent esplicito è lo avvia:Intent helpIntent=new Intent(requestActivity.this,helpActivity.class);
startActivity(intent);
Trattandosi di un intent esplicito occorre precisare nella richiesta il componente che la riceverà (il secondo parametro passato al costruttore). In questo esempio, invece:
Intent helpIntent=new Intent();viene tentato di avviare un intent implicito, il componente dovrà avere la capacità di editare un testo.
helpIntent.setAction(android.intent.action.ACTION_EDIT);
helpIntent.putExtras(message,userText);
helpIntent.setType(text/plain);
startActivity(helpIntent);
Il meccanismo di intent-resolution analizza esclusivamente i campi
Action
, Data
e Category
dell'oggetto Intent
. Gli attributi Extras
e Flag
non influenzano la ricerca. L'intent-resulotion entra gioco solo se l'intent invocato è di tipo implicito. Per consegnare la richiesta descritta nell'intent a un componente vengono controllati i tre campi dati detti sopra, il test coinvolgerà inevitabilmente tutti campi dati presenti nell'intent-filter. Poiché un elemento intent-filter può contenere più elementi action
, category
e data
, il test sui campi dati sarà superato con successo solo se le proprietà dell'oggetto Intent
coincidono con almeno un elemento dell'intent-filter. Ad esempio, se l'intent-filter di un componente descrive più action
, il test sul campo dati Action
verrà superato solo se l'azione nell'oggetto Intent
è fra gli intent-filter del componente (ricordate, infatti, che un oggetto Intent
può contenere una sola Action
mentre un intent-filter ne può contenere più di una). L'esempio appena visto vale anche per i campi dati Data
e Category
, che pure possono essere numerosi nell'intent-filter (dipende dalle capacità del componente). Ogni componente, anche se non indicato nell'intent-filter, viene assegnato alla categoria android.intent.category.DEFAULT
. Il meccanismo di intent-resolution potrebbe, infine, non trovare nessun componente capace di gestire l'intent. In tal caso verrà lanciata un eccezzione e l'activity richiedente verrà dunque chiusa.
Ottima guida. Grazie!
RispondiElimina