Android/Intents: el paper dels diferents arguments
Definició
[modifica]Una Intent és un objecte de missatgeria que pots utilitzar per a sol·licitar una acció d'un altre component d'una app. Si bé les Intents faciliten la comunicació entre components de diverses formes, hi ha tres casos d'ús principals:
Iniciar una activitat
[modifica]Una Activity representa una única pantalla en una aplicació. Pots iniciar una nova instància d'una Activity passant un Intent a startActivity(). La Intent descriu l'activitat que s'ha d'iniciar i conté les dades necessàries per a això.
Si vols rebre un resultat de l'activitat quan finalitzi, crida a startActivityForResult(). L'activitat rep el resultat com un objecte Intent separat en la devolució de trucada de onActivityResult() de l'activitat.
Iniciar un servei
[modifica]Un Service és un component que realitza operacions en segon pla, sense una interfície d'usuari. Amb Android 5.0 (nivell d'API 21) i versions posteriors, pots iniciar un servei amb JobScheduler. Per obtenir més informació sobre JobScheduler, consulta la seva API-reference documentation.
En les versions anteriors a Android 5.0 (nivell d'API 21), pots iniciar un servei utilitzant mètodes de la classe Service. Pots iniciar un servei per realitzar una operació única (com descarregar un arxiu) passant un Intent a startService(). La Intent descriu el servei que s'ha d'iniciar i conté les dades necessàries per a això.
Si el servei està dissenyat amb una interfície client-servidor, pots establir un enllaç amb el servei d'un altre component passant un Intent a bindService().
Transmetre una emissió
[modifica]Una emissió és un avís que qualsevol aplicació pot rebre. El sistema transmet diverses emissions d'esdeveniments, com quan s'inicia el sistema o comença a carregar-se el dispositiu. Pots transmetre una emissió a altres apps passant un Intent a sendBroadcast() o sendOrderedBroadcast().
Tipus de intents
[modifica]Existeixen dos tipus d'intents:
- Les Intents explícites especifiquen quina aplicació les administrarà, ja sigui incloent el nom del paquet de l'app de destinació o el nom de classe del component completament qualificat. Normalment, l'usuari fa servir una intent explícita per iniciar un component en la seva pròpia aplicació perquè coneix el nom de classe de l'activitat o el servei que vol iniciar. Per exemple, pots utilitzar-la per iniciar una activitat nova en resposta a una acció de l'usuari o iniciar un servei per descarregar un arxiu en segon pla.
- Les Intents implícites no nomenen el component específic, però, en canvi, declaren una acció general per a realitzar, la qual cosa permet que un component d'una altra aplicació la manegi. Per exemple, si desitges mostrar a l'usuari una ubicació en un mapa, pots fer servir una intent implícita per demanar que una altra aplicació apta mostri una ubicació específica en un mapa.
Quan utilitzes una intent implícita, el sistema Android busca el component apropiat per iniciar comparant el contingut de la intent amb els filtres d'Intents declarats en el fitxer de manifest d'altres aplicacions en el dispositiu. Si la intent coincideix amb un filtre de Intents, el sistema inicia aquest component i li lliura l'objecte Intent. Si diversos filtres d'Intents són compatibles, el sistema mostra un quadre de diàleg perquè l'usuari pugui triar l'aplicació que s'ha d'usar.
Un filtre d'Intents és una expressió a l'arxiu de manifest d'una aplicació que especifica el tipus d'intent que el component podria rebre. Per exemple, declarar un filtre de Intents per a una activitat permet que altres aplicacions la iniciïn directament amb un tipus d'intent específic. Així mateix, si no declares cap filtre d'intent per a una activitat, aquesta només es pot iniciar amb una intent explícita.
Intents explícites
[modifica]Una intent explícita és una intent que es fa servir per iniciar un component específic de l'aplicació, com una activitat o un servei particular en l'aplicació. Per crear una intent explícita, defineix el nom de component de l'objecte Intent (totes les altres propietats de la va intentar són opcionals).
Per exemple, si vas crear un servei en el teu app denominat DownloadService, dissenyat per descarregar un arxiu de la web, pots iniciar-lo amb el següent codi:
- Link al repositori: Pàgina principal d'Android, Intents explícites, Intents
- Anotacions: No
- Vegeu també: No
// Executat en una Activity, per tant 'this' es el Context.
// El fileUrl es una string URL, com "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
El constructor Intent(Context, Class) proporciona Context a l'aplicació; i el component, un objecte Class. D'aquesta manera, la intent inicia explícitament la classe DownloadService a l'app.
Intents implícites
[modifica]Una intent implícita especifica una acció que pot invocar qualsevol aplicació en el dispositiu que pot realitzar l'acció. L'ús d'una intent implícita és útil quan l'aplicació no pot realitzar l'acció, però altres aplicacions probablement sí, i vols que l'usuari triï quina aplicació utilitzar.
Per exemple, si tens contingut que vols que l'usuari comparteixi amb altres persones, crea un intent amb l'acció ACTION_SEND i afegeix extres que especifiquin el contingut per compartir. Quan crides a startActivity() amb aquesta intent, l'usuari pot triar una aplicació mitjançant la qual compartir el contingut.
- Link al repositori: Pàgina principal d'Android, Intents implícites, Intents
- Anotacions: No
- Vegeu també: No
// Creació d'un missatge de text amb string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verificar que l'Intent por resoldre la Activity.
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
Quan es crida a startActivity(), el sistema examina totes les aplicacions instal·lades per determinar quins poden manejar aquest tipus de intent (una intent amb l'acció ACTION_SEND i que té dades de text / sense format). Si només hi ha una aplicació que pot manejar-la, aquesta s'obre immediatament i se li lliura la intent. Si diverses activitats accepten l'intent, el sistema mostra un quadre de diàleg, com el que es mostra a la figura 2, perquè l'usuari pugui triar l'aplicació que s'ha d'usar.
Com crear una intent
[modifica]Un objecte Intent té informació que el sistema Android usa per determinar quin component s'ha d'iniciar, com el nom del component o la categoria que rep la Intent, a més de la informació que el component receptor usa per realitzar la acció correctament.
La principal informació que conté un Intent és la següent:
Nom del component
[modifica]El nom del component que s'ha d'iniciar. Cal tenir en compte que inicialitzar aquesta part de la Intent és totalment opcional, encara que no fer-ho pot comportar diferents problemes explicats després. En cas d'especificar el nom del component immediatament la Intent creada passa a ser un Intent explícit, que això vol dir que solament es podrà enviar la Intent cap el nom del component que té aquest nom. En cas de no tenir nom del component immediatament el Intent passar a ser un Intent implícit. Donada aquesta situació el propi sistema decideix quin component interactuarà amb la Intent, gracies a la informació restant que té una Intent, que està detallada més endavant, com l'acció, les dades i la categoria. Per finalitzar solament s'ha de recordar que si necessites que una Intent tingui un component específic simplement se li ha de donar un nom del component, altrament no ho serà.
- Nota: En cas d'inicialitzar un Service sempre s'ha d'especificar el nom del component ja que en cas de no fer-ho no podràs assegurar-te que la Intent creada sigui resposta pel Service, on això desencadena que l'usuari no pugui inicialitzar el Intent per tant no podrà fer ús d'aquest Service.
En aquest camp de la Intent és un objecte del tipus ComponentName que aquest nom el pot especificar amb el nom d'una classe completament especificada, fins i tot pot posar el nom d'un paquet. Una de les manares de posar
- el nom del component es fent servir setComponent(), setClass(), setClassName() o fent servir el constructor Intent.
Acció
[modifica]És un string que especifica l'acció genèrica que s'ha de realitzar.
En cas d'estar fent servir un intent del que far un broadcast, aquesta acció quan tingui lloc i aquesta serà enregistrada. L'acció es la part que determina quina és la estructura de la resta del Intent, especialment la informació que inclou dades i extres.
Un desenvolupador pot especificar quines accions usa un Intent incluent una acció pròpia, o fins i tot que altres aplicacions puguin fer servir per invocar components d'un aplicació feta per tu o una altra persona; usualment has d'especificar accion constant les quals han sigut definides prèviament a la classe Intent o altres classes les quals són incloses en el teu marc de treball.
Aquí tens un exemple de quines accions usades per inicialitzar una activitat ( startActivity() ) :
- ACTION_VIEW
Pot usar aquesta acció en un Intent amb startActivity() quan tinguis informació on la activitat pugui mostrar al usuari, com una foto a una app de galeria entre altres.
- ACTION_SEND
Aquesta acció també es coneix com el Intent per compartir i l'has d'usar en un Intent amb startActivity() quan tinguis la informació que el usuari pugui necessita per poder compartir fent servir una altra aplicació, per exemple una de correo o xarxes socials.
Pots especificar la acció d'un Intent amb el setAction() o fent servir el mateix constructor.
En cas de definir les teves pròpies accions, t'has d'assegurar d'incloure el nom del paquet de la aplicació com a prefix, tal com es mostra al següent codi:
- Link al repositori: [1]
- Anotacions: No
- Vegeu també: No
// Executat en una Activity, per tant 'this' es el Context.
// El fileUrl es una string URL, com "http://www.example.com/image.png"
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Dades
[modifica]Un URI (objecte) que fa referència a les dades on s'han de realitzar les accions o el tipus de MIME de les d'aquestes dades. El tipus de dades subministrades eé generalment determinat per l'acció del Intent. Un exemple seria que a la acció fos ACTION_EDIT, on a les dades hauria d'haver el URI del document que s'ha d'editar.
Quan crees un intent, és important especificar el tipus de dades (tipus de MIME) a més del URI. Un exemple d'això seria una Activity la qual pot ensenyar una imatge el més segur és que no pugui reproduir un àudio encara que els 2 tinguin URI similars. Especificar el tipus de MIME de les teves dades ajuda al sistema Android a trobar el millor component per rebre un Intent. Altrament, hi ha cops que el tipus de MIME es pot arribar a deduir del URI, especialment quan les dades son una URI:content. Un URI:content indica que les dades es trobe en el dispositiu i son controlades per un ContentProvider.
Si solament vols establir un URI de dades es pot fer servir setData(). Altrament si solament vols posar el tipus de MIME fas servir setType(). I per finalitzar en cas que fos necessari que els 2 estiguin explícits pots fer sercvir la funció de setDataAndType().
Categoria
[modifica]És un string que conté informació addicional sobre el tipus de component que la Intent ha de dirigir. En una Intent, es pot incloure la quantitat desitjada de descripcions de categories, però la majoria dels Intents no els hi fa falta una categoria. Aquí es mostren unes de les categories més comunes:
- CATEGORY_BROWSABLE
La Activity de destí permet que la iniciï un navegador web per mostrar unes dades a les quals fa referència un enllaç, com una imatge, un correo entre d'altres.
- CATEGORY_LAUNCHER
La Activity es la inicial d'una tasca i està enumerada en el selector d'aplicacions.
Una de les maneres d'especificar una categoria es fent servir la funció addCategory().
Extres
[modifica]Els parells de clau-valor que contenen informació addicional necessària per completar la acció sol·licitada. Al igual que algunes accions usant tipus particulars de URI de dades. Pots agregar dades addicionals amb varis metodes com putExtra(), on aquest accepta dos paràmetres: el nom de la clau i valor. També es pot fer servir un objecte Bundle per poder afegir informació addicional.
Per posar un exemple, tu pots crear un intent per a enviar un correu electrònic fent servir ACTION_SEND, on pots especificar el mail amb la clau EXTRA_MAIL, entre d'altres opcions.
La classe Intent te moltes constants EXTRA_* per a cada tipus de dades estandarditzades. En cas que es vulgui declarar un extra propi per a un Intent s'haurà d'assegurar d'incloure el nom del paquet de l'aplicació.
Indicadors
[modifica]Els indicadors es defineixen a la classe Intent que funciona com metadades del Intent. Els indicadors poden indicar al sistema Android com inicar una Activity y com tractar-la després que s'iniciï.
Com rebre una intent implícita
[modifica]Per informar les Intents implícites que pot rebre la teva app, declara un o més filtres per a cada component d'aquesta amb un element <intent-filter> a l'arxiu de manifest. Cada filtre de Intents especifica el tipus de Intents que accepta segons l'acció, les dades i la categoria de la intent. El sistema lliurarà la intent implícita al component de l'aplicació només si la intent pot passar per un dels filtres.
Nota: Una intent explícita sempre es lliura a la destinació, independentment dels filtres de Intents que declari el component.
Un component d'aplicació ha de declarar filtres independents per a cada tasca única que pot fer. Per exemple, una activitat d'una aplicació de galeria d'imatges pot tenir dos filtres: un per veure una imatge i un altre per editar-la. Quan comença l'activitat, inspecciona la Intent i decideix com comportar-se conforme la informació de la Intent (com mostrar els controls de l'editor o no).
Cada filtre d'Intents està definit per un element <intent-filter> a l'arxiu de manifest de l'app, niat en el component corresponent (com un element <activity>). A l'<intent-filter> pots especificar el tipus d'Intents que s'acceptarà amb un o més d'aquests tres elements:
<Action>
- Declara l'acció de la intent acceptada, en l'atribut name. El valor ha de ser el de la string literal d'una acció, no la constant de classe.
<Data>
- Declara el tipus de dades que s'accepta, mitjançant l'ús d'un o més atributs que especifiquen diversos aspectes de la URI de dades (scheme, host, port, path, etc.) i el tipus de MIME.
<Category>
- Declara la categoria de la intent acceptada, en l'atribut name. El valor ha de ser el de la string literal d'una acció, no la constant de classe.
Nota: Per rebre Intents implícites, has d'incloure la categoria CATEGORY_DEFAULT en el filtre de Intents. Els mètodes startActivity() i startActivityForResult() tracten totes les Intents com si pertanyessin a la categoria CATEGORY_DEFAULT. Si no declares aquesta categoria en el filtre de Intents, no s'aplicarà cap intent implícita a l'activitat.
Per exemple, a continuació, es mostra una declaració d'activitat amb un filtre d'Intents per rebre una intent ACTION_SEND quan el tipus de dades és un text:
- Link al repositori: Pàgina principal d'Android, Rebre Intents implícites, Intents
- Anotacions: No
- Vegeu també: No
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
Pots crear un filtre que inclogui més d'una instància de <action>, <data> o <category>. Si ho fas, t'has d'assegurar que el component pugui administrar qualsevol combinació d'aquests elements de filtres.
Quan vulguis administrar diversos tipus d'Intents, però només en combinacions específiques de tipus d'accions, dades i categories, hauràs de crear diversos filtres de Intents.
Una intent implícita es prova amb un filtre comparant la intent amb cada un dels tres elements. Perquè es lliuri al component, la intent ha de passar les tres proves. Si no passa tan sols una d'elles, el sistema Android no la lliurarà al component. No obstant això, com un component pot tenir diversos filtres d'Intents, una intent que no passi pel filtre d'un component potser passi per un altre filtre.
Com utilitzar una intent pendent
[modifica]Un objecte PendingIntent és un contenidor d'un objecte Intent. El propòsit principal d'un objecte PendingIntent és atorgar permís a una aplicació externa per a usar la Intent continguda com si s'executés des d'un procés de la seva pròpia aplicació.
Aquests són alguns dels casos d'ús principals d'una intent pendent:
- Declarar una intent que s'executarà quan l'usuari realitzi una acció amb la seva notificació (l'objecte NotificationManager del sistema Android executa l'Intent).
- Declarar una intent que s'executarà quan l'usuari realitzi una acció amb el giny de la teva aplicació (l'app de la pantalla principal executa l'Intent).
- Declarar una intent que s'executarà en un moment especificat en el futur (el AlarmManager del sistema Android executa l'Intent).
Com cada objecte Intent està dissenyat per a ser manejat per un tipus específic de component de l'aplicació (Activity, Service o BroadcastReceiver), s'ha de tenir la mateixa consideració en crear una PendingIntent. Quan utilitzeu una intent pendent, l'aplicació no l'executarà amb una trucada com startActivity(). En canvi, has de declarar el tipus de component desitjat quan creïs la PendingIntent cridant al mètode creador respectiu:
- PendingIntent.getActivity() per a una Intent que inicia una Activity.
- PendingIntent.getService() per a una Intent que inicia un Service.
- PendingIntent.getBroadcast() per a una Intent que inicia un BroadcastReceiver.
Llevat que l'aplicació estigui rebent Intents pendents d'altres aplicacions, els mètodes indicats a dalt per a crear una PendingIntent probablement siguin els únics mètodes PendingIntent que necessitaràs.
Cada mètode pren el Context actual de l'aplicació, la Intent que desitja encapsular i un o més indicadors que especifiquen com s'ha d'usar la intent (per exemple, si es pot fer servir més d'una vegada).
Exemples
[modifica]Com forçar un selector d'apps
[modifica]Quan més d'una aplicació respon a la intent implícita, l'usuari pot seleccionar quina aplicació s'ha d'usar i establir-la com l'opció per defecte per a l'acció. Això és útil quan es realitza una acció per la qual l'usuari probablement vulgui utilitzar la mateixa aplicació en endavant, per exemple, en obrir una pàgina web (els usuaris solen preferir un sol navegador web).
No obstant això, si diverses aplicacions responen a la intent i és possible que l'usuari vulgui fer servir una diferent cada vegada, has de mostrar explícitament un quadre de diàleg de selecció. El quadre de diàleg de selecció li pregunta a l'usuari quina aplicació vol utilitzar per a l'acció (no pot seleccionar una aplicació per defecte per a l'acció). Per exemple, quan la teva app realitza l'acció de "compartir" amb l'acció ACTION_SEND, és possible que els usuaris vulguin concretar la compartició usant una app diferent segons la situació en què estiguin. Per això, has de fer servir sempre el quadre de diàleg de selecció, com es mostra en la següent figura.
Per mostrar el diàleg de selecció, crea una Intent usant createChooser() i transfereix-la a startActivity(), tal com es mostra en el següent exemple. Aquí es mostra un diàleg amb una llista d'apps que responen a la intent transferida al mètode createChooser(), amb el text proporcionat com a títol del diàleg.
- Link al repositori: Pàgina principal d'Android, Intents, Forçar selector
- Anotacions: No
- Vegeu també: No
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);
}
Exemples de filtres
[modifica]Per demostrar alguns comportaments de filtres de Intents, s'inclou un exemple de l'arxiu de manifest d'una app d'intercanvi social:
- Link al repositori: Pàgina principal d'Android, Intents, Exemple filtres
- Anotacions: No
- Vegeu també: No
<activity android:name="MainActivity">
<!-- 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">
<!-- 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 primera activitat, MainActivity, és el punt d'entrada principal (l'activitat que s'obre quan l'usuari llança inicialment l'app amb la icona de selector):
- L'acció ACTION_MAIN indica que aquest és el punt d'entrada principal i que no s'esperen dades de Intents.
- La categoria CATEGORY_LAUNCHER indica que la icona d'aquesta activitat s'ha de posar al gestor de l'aplicació del sistema. Si l'element <activity> no especifica una icona amb icon, el sistema fa servir el de l'element <application>.
Aquests dos han de sincronitzar-se perquè l'activitat aparegui al gestor de l'aplicació.
La segona activitat, ShareActivity, té com a objectiu facilitar l'intercanvi de text i contingut multimèdia. Tot i que els usuaris poden ingressar a aquesta activitat navegant fins a ella des MainActivity, també poden ingressar a ShareActivity directament des d'una altra app que emeti una intent implícita que coincideixi amb un dels dos filtres de Intents.
Proves
[modifica]Resolució de Intents
[modifica]Quan el sistema rep una intent implícita per iniciar una activitat, busca la millor activitat per a la va intentar mitjançant la comparació de la intent amb els filtres de Intents basant-se en tres aspectes:
- acció
- Dades (tant URI com a tipus de dades)
- categoria
En les següents seccions, es descriu la manera de fer coincidir les Intents amb els components apropiats segons la forma de declarar el filtre de Intents en un arxiu de manifest d'una app.
Prova d'acció
[modifica]Per especificar les accions de Intents acceptades, un filtre de Intents pot declarar un nombre d'elements <action> de zero en endavant, tal com es mostra en el següent exemple.
- Link al repositori: Pàgina principal d'Android, Intents, Exemple filtres
- Anotacions: No
- Vegeu també: No
<source lang="java">
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.VIEW" />
...
</intent-filter>
Per passar el filtre, l'operació especificada per l'Intent ha de coincidir amb una de les accions indicades en el filtre.
Si el filtre no enumera cap acció, no hi ha res amb què establir la coincidència, de manera que cap de les Intents passa la prova. No obstant això, si una Intent no especifica una acció, passarà la prova (si el filtre conté, almenys, una acció).
Prova de categoria
[modifica]Per especificar les categories de Intents acceptades, un filtre de Intents pot declarar un nombre d'elements <category> de zero en endavant, tal com es mostra en el següent exemple.
- Link al repositori: Pàgina principal d'Android, Intents, Exemple filtres
- Anotacions: No
- Vegeu també: No
<source lang="java">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
...
</intent-filter>
Perquè una intent passi una prova de categoria, cada categoria en la Intent ha de coincidir amb la categoria que figura en el filtre. No cal que passi la situació inversa (el filtre de Intents pot declarar més categories de les que s'especifiquen a la Intent, i la Intent passarà la prova). Per tant, una va intentar sense categories sempre hauria de passar aquesta prova, independentment de quina categories estiguin declarades en el filtre.