You are on page 1of 43

CAPITIJLO 7.

Almacenamiento de datos

Las aplicaciones descritas hasta este captulo representaban la informacin a procesar en forma de variables. El problema de estas variables es que dejan de existir en el momento en que la aplicacin es destruida. En muchas ocasiones una

aplicacin necesita almacenar informacin

de manera

permanente. Las

alternativas ms habituales para conservar esta informacin son los ficheros, las bases de datos o servicios a travs de la red. Estas tcnicas no solo permiten mantener a buen recaudo los datos de la aplicacin, sino que tambin vamos a poder compartir estos datos con otras aplicaciones y usuarios.

A lo largo de este captulo estudiaremos cmo utilizar estas tcnicas en Android. Comenzaremos describiendo las caractersticas del sistema de ficheros que incorpora Android. Estos ficheros pueden ser accedidos a travs de las clases estndar incluidas en Java, de forma adicional se incluyen nuevas clases para
cubrir las peculiaridades de Android.

Como segunda alternativa se estudiar el uso de XML para almacenar la


informacin de manera estructurada. Se describirn dos herramientas alternativas, las libreras SAX y DOM.

Como tercera alternativa al almacenamiento de datos se estudiarn las bases de datos. Android incorpora la librera SQLite, que nos permitir crear y manipular nuestras propias bases de datos de forma muy sencilla. Como cuarta alternativa se describir la clase conte:ltpro.;i.ler. Consiste en un mecanismo introducido en Android para poder compartir datos entre aplicaciones

En el captulo siguiente se describe otra alternativa, el uso de lnternet como recurso para almacenar y compartir informacin. Concretamente se describir el uso de sockefs TCP y los servicios web.
Trataremos de almacenar la lista con las mejores puntuaciones obtenidas en Asteroides. Este ejemplo es descrito al principio de este captulo. En este ejemplo se introduce adems la vista vie"vLlsi:- y el paso de informacin entre actividades.

Todas estas alternativas sern ilustradas

travs del mismo ejemplo.

177

Elgran libro de Android

7.1. Aadiendo lista de puntuacones en Android


Muchos videojuegos permiten recordar
anteriores, de esta forma, un jugador puede tratar de superar su propio rcord o mejorar de otros jugadores. Esta informacin ha de almacenarse permanentemente en el sistema, por lo que tenemos que utilizar alguno de los mtodos de almacenamiento disponibles en Android. En este captulo almacenaremos las puntuaciones en un fichero, en XML, en una base de datos y terminaremos utilizando la clase L-on t errr p:!:.:;v i. <1er..

las

puntuaciones

de

partidas

el

Vamos a intentar que el mecanismo de almacenamiento seleccionado sea lo ms independiente posible del cdigo de Asteroides. Con este propsito, vamos a definir la interfaz Almacer?puntuaciones. Para ello abre la aplicacin Asteroides y pulsa botn derecho sobre carpeta cdigo (siri:/',:onr.exa:rpie.asi:e:oi<1es) y selecciona New > lnterface. En la entrada Name: introduce Alrna{:errpr,ni:uicj.cres y pulsa Finish. El cdigo a introducir se

con el

la

de

muestra a continuacin:

publtc fnterface

ALmacenPuni:uat:j.'.:.nes

i
nombre,

public vol.d quarCarPuntuacioniint puntos, Strinc long f ecira) ;


pub1.

Ver: b<r'.:

lil r i. rrg." l i.st aPr.ri t-r:ac

:i

<llres

lnt

carit. : da<i ),'

i
Nota: La intedaz es una clase abslractq pura, es decir una clqse donde se indican los mlodos pero no se implemenla ninguno (en este ccrso se dice que los mtodos son abstractos). Permite al programador de la clase establecer la estruchtra de esta (nombres de mtodos, sus parmelros y tipos que retorna, pero no el cdigo de cada mtodo). Una intetfaz tambin puede contener constantes, es decir campos de tipo static y final.

jugador

Las diferentes clases que definamos para almacenar puntuaciones han de implementar esta interfaz. Como ves tiene dos mtodos. El primero para guardar la puntuacin de una partida, con los parmetros puntuacin obtenida, nombre del

fecha de

la

partida. La segunda es para obtener una lista de

puntuaciones previamente almacenadas. El parmetro ca'Lidari indica el nmero mximo de puntuaciones que ha de devolver.

Veamos a continuacin una clase que utiliza esta interlaz, aunque siempre devuelve las mismas puntuaciones. Cralo para poder testear la siguiente parte delcodigo:

178

Almacenamiento de datos

public class Alnacen?urr.Eaclcnesccnslanie


,hl riic.ipt-
Ir L i.:i..: i. orl<.t

impLementa

i
orrbr-e,

public void Er:a:-darPtuir-racj.on(int punF. Sir:nq long fecha) i


!

public 1cior{gir:"rrq> i:slapunir:aciorres iint caniidad) i VecL.rr'.1SLr j-:c.> resitlL = new Vecrt:or.iSLr'1.rig> { ) ;
re6i11t ..1Gdi"123000 Pepi-to Domingez', i re;iilt- .adc! ( !'11-10C0 Periro Mart.inez" ) ;
;

returrx
t i

rei:;il.l.L.;.:i<i "51.1C00

Dan Ferrit-ot')

l'e:.rl.t:;

Para que los jugadores puedan ver las ltimas puntuaciones obtenidas, vamos a modificarel cuarto botn del layout n.ir:.xni1 para que en lugardel texto "salir" se visualice "Puntuaciones". Para ello modifica los ficheros resirvatues,/srrir:gs. Tambin sera interesante que cambiaras el fichero res.i,/..ii1li4s-er:,/s:t:rings;. En la actividdd As:t:eroicies-r tendrs que declarar una variable para almacenar las puntuaciones:

publ

ic gtatic

Al

riia:eri

i-'r..

rr i::r c :i.ore

s:i

almacen,,
eii Pii lit,
i.u r:: i.l)i! e $ Cc, n,:

new

A.'1.

nt;lr::

rt

it, e i

Tambin hay que modificar el escuchador asociado al cuarto botn para que
llame al siguiente mtodo:

public void i* zil?uli:.ri;l<::i.<>nes:; ( ) :nLsnL :. = nrr f tten: (t,his,


.L

prinar-raci.ones . class )

iii i:.:i:l'i::11i:: t:.

:!.

:i.

i:..,, (

:i.

j!

El
>r: n i:-u:* c

tayout
:i.

que

utilizaremos para

la
-

nueva actividad
:

<>rle :r . :.:::ii.i.

. Este sg muestra a continuacin


{-.1,,

se

llamar

.:

xri:..]-

rerg ion= " I . i.),' errccdi r1g=,, t f

?>

<L

11?ar l-'y.clii i:

xlnf :s: ancl:'cirl= "i: :): / t'st:heias. anci.:oiri. r-:ot!'aitkt're:;.'anclr.>i11"

android : ori ent.ai. ion=', vert i c a l,' andi'oid: layour_\e'idth= ', 111_-are-n

,,

179

Elgran libro de Android

android: 1ayout.._height= " f: J13ar:ent " andro id : background= " G dr awab f e / de g r ada do " >
<'I'ex{:View

andrcid : l.ayout_width= " fi L l_3 arent " android : layout_height = "urrap_ c ont ent android: text= "Pii?tuaciones " androi.d : gravity= " cent er " android: layout_margin= " 11px" android: t.extSize= " 7io " tr i
:>

"

.: f'l:';t.tne.lJir.y

Dii tl
"

android: layout_widEh= " t i 1i*arent android: layoub_height= " 0Cip" andr:oid: layorrl_weight= "1 ">
,:,.i.stV.i.itw

android: id= "Gandroid: idt Iist"


an.dro i.cl

i*c)ar en. l " anCroid: layor.rt_heigltt= " f i11*parent ' android:drardselectorOnTop= " faise" i >
:1a

yorrt_w

th= " f

:i'.1

<':i:e.1.i:V.i. *i14

android : id= " Q androi d : i dl enpty " android : layout_widL}:.= " f i I 7-par ent " and:c j.rl : ayout_he i gh t= " fi i J-.pa rent " android:text= "Ic hay puntuacicnes" /,
1.

.c

/Fratnel.a-vo,.rl.
le:a:r'La yo
L.ltr >

>

< 7 L i.

Necesitamos ahora crear la actividad punruaci-ones. Crea una nueva clase en proyecto tu e introduce elsiguiente cdigo:

public clasg Pnntuaciones extendg ListActir,'ity i


,Ai:iv$:i.'r i.ilc

publtc void onCr:eate(Bundi.e savedlnstoncestet.e )


super . oncreat,e ( savedlnsanceSlae ) ; s e L C'on I ent.Vi. ew ( R . layou,ts- . punt ua c i ones i
se

tL,

j. s

rAclapir: ( ne$ Arra.yAdapte r < S t. ;:i.ng.:, ( tht s, a:'cirr: i- ri . ii . l a yc irt . sirnpl e_-i i s t _i t em_J- t

aet.l,i.
)

Asteroj.dtrs .almacen. l-islaFurtuar:i.ones (r.A) i ) ; s iVi rlw ( i . s,e iTe:,;t F i. i. t i?rBn tr.l. erd ( true ) ;

Recuerda

que toda nueva actividad ha de ser

registrada en

r.rr:<ircicl'{anif es,r . y,m1. Prueba Si funciona el codigo introducido.

180

Almacenamiento de datos

7.2. Comunicacin entre actividades


Cuando una actividad ha de lanzar a otra actividad en muchos casos necesita

enviarle cierta informacin. Android nos permite este intercambio de datos


utilizando el mecanismo que se muestra en el siguiente ejemplo: Si lanzas una actividad usando el siguiente cdigo:

Ii:t_erit i:iteiiL = new lirLerit (this, UU!4E.g.c1assi iiitent . llilt.E-\tra. i"usuario" , t'pepito Perez" ) ir1:eirt.$utElil.-ra ( "edad" , 21) ;
s-rt-ar'1-Ar-'L

ii,'i-t:i; i i.lit:ei'rt. ) ;

En la actividad lanzada podemos recoger los datos de la siguiente forma:


Bun<j-le est ras - <;eL f jil e:tL O . <;et.E-rl ras i ) ;t.r i.fie it "" ext.,ras.geLS-llr':j.:.ic,i i "usuaro" ) ;

int . ,: ext.l'ii

..jet:..1.:.!l i ',edad"

Cuando la actividad lanzada termina tambin podr devolver datos que podrn ser recogidos por la actividad lanzadora de la siguiente manera.
.Irtre:'i1:. :i.rit-,*:irt. :i: new .lirtreri.t:. { this, lvll.._,(:I.,Afilii: . class ) sjt:art:Acrtrivi.t.-vI:'<:iriies-rl-1.t: (:il,airi:a.-f i.reii), '1.)34i ;

,iai!'err iaie

protected void cr:lrctivitiiies:-rlt. if String


i

(int

reqi'testcr:ie, int

re:illlCce,
.{

.l.rjt:erti:. d;:ii:-;i )

(r:'ecu*L;i::(,'*ii**,.1:1.,j4 & r:e:ii.il.til<>d.e=.= U.MSUI,T_OK.) t


..'i?f--

- data"rJn:i-rt.;:.;.::().qrr-lS-,:i.':i.nr]("::esult.ado");

En la actividad llamada has de escribir:

Intent i-nt.enL = nt{ tntenL O ; int.ent.putExtra ( "resultado", "valor" setResult (RES\ILT_OK, intent) ; finish ( ) ;

,.

181

El gran libro de Android

Ejercico

1. 2.

Crea una variable globalen la clase visra,ruego con nombre pr:rri:r:aci.on e inicialzala a cero. Gada vez que se destruya un asteroide incrementa esta variable. Utilizando elcdigo descrito, prepara la actividad Alri:.e!:oj.de$ para que cuando lance la actividad ,rireso esta pueda re@ger la puntuacin obtenida.
a

Una posible solucin del segundo apartado del ejercicio se muestra


continuacin:

pubJ.ic vold l.anz,.h.egoi) { I:ltent i = new lntent (thts, Lrueltc.claee) gta::tAct i. yi. L_vForRerui. L ( j., :.2 3.i ) ; i
'eiOv e r-::r':i.<1e

prot'eeted vof d

iv i Ll'Isssi. L ( int :'eque*it:Coi1e, inb resiJlt:Coce, I$LenL <li:L;e) super . cnAct j. v j. tyRtlsul.. | ( r:eques f-Code, resul lCcde, da t a ) ; if (sr'uest.Code*-it:l,l & iesu.l-iCcde=-RESULT_OK & datal=nutt ) fnt pilnLrlacio: * dala.geLllxlras () .geLInL ( "prrntuacion't;i i
onAr::t:

if

i i i,*e.:r1.* d.r.rsclri: Lin Dj.e:l.oll c AlerrDialoq.Builder i ) ;

ipuntr.r;,lci.t':n > C) t $lr:.irlg norbre = "Yotr;

u.r]:

r!licjvi

*r;1,vci..rd.

almacen. guarrlarPitntracicr ipr.ini:uacion, nombre,


Sys tem . r-'Lr i.'.r'{:.ri i li'.i :le i:i.i .!..i :..s ( ) ) ;

IatlzaFunrLlaeiones
I
t

l
)

Para realizar la respuesta de la actividad puede ser ms sencillo hacerlo desde \,'i.ritaJ.rego. El problema es que esta clase es un vista no una actividad. Para solucionar el problema puedes usar el siguiente truco. lntroduce en vistaJueqo l siguiente cdigo:

prLvate Act.ivity padre;

public voLd setPadre(Activity padre) this.padre = padre; l


182

Almacenamiento de datos

Cuando detectes una condicin de victoria o derrota

if

(padre != nutl) { Bundle bundle = nev eundle O ; bundLe.putTnt (,,puntuacionr', punt.rracion) ; Intent intent = nerd Intent ( ) ; intent.putExtras (bundle) ; padre. setResult (Actlvity. RESULT*OK, intent)

7.3. Accediendo a ficheros

- Existen tres tipos de ficheros donde podemos almacenar informacin en Android: ficheros almacenados en la memoria interna del telfono, ficheros almacenados en la tarieta SD y ficheros almacenados en los recursos. Estos ltimos solo pueden ser ledos por lo que no son tiles para almacenar informacin
desde la aplicacin.

7.3.1. Sistema interno de ficheros

. _ Android permite almacenar ficheros en la memoria interna del telfono. por defecto los ficheros almacenados solo son accesibles por la aplicacin que los cre, no pueden ser ledos por otras aplicaciones, ni siquiera por el usuario del telfono. cuando se desinstala la aplicacin los ficheros que ha creado se eliminarn. cuando trabajes con ficheros en Android, ten siempie en cuenta que la memoria disponible de los telfonos mviles es limitada.

- .R99uerda que el sistema de ficheros se sustenta en la capa Linux, por lo que Android hereda su estructura. Cuando se instala una nueua aplicaciOn Android crea un nuevo usuario Linux asociado a la aplicacin y es est,e usuario el que podr o no acceder a los ficheros.
Puedes utilizar cualquier rutina del paquete i a.a . io para trabajar con ficheros. Adicionalmente se han creado mtodos adicionales asociados a la clase conrex para facilitarte el trabajo con ficheros almacenados en la memoria interna. En particular los mtodOs oi*irF,iieI:r:rrr: (i y openf,ij.)oLii:pi1L ( i te permiten abrir un fichero para lectura o escritura respeciivamente. Si ,liliras estos mtodos el nombre del archivo no puede contener subdirectorios. De hecho elfichero siempre

es

almacenado

(.rc1ata,;,lata,,/nom.nre--de1....oaqriete,/f

con el mtodo

escribir en l un texto:

en ra carpeta reservada para tu apticacion ile,s). Recuerda siempre cerar los ficherc clcse l. El siguiente ejemplo muestra cmo crear un fichero y

13

Elgran libro de Android

St-ring f j.chero - I'f ichero.txtt,; St.riarq texto = 'ttexto almacenado";


li'i ieoul:put:fiL:'eam os: ;

rry i

fos = openFileOutput tficherc, Co:lt.ext .MODE_pRfvATEi ; fcs.write (terto. geLAytes ( ) i ;


f <*. cl.ose O ;

.}

ei i ('Mi Aplicacin", e.getMessage O,e), l catch (IOExcepticrrr e) { Log.e ("Mi Aplicacin",e.getMessage O,e) ;
Log.e

catch

(Fj.leliotF'orrniil!-ucepi.i.r-.n

Es muy importante hacer un manejo cuidadoso de los erores. De hecho, el acceso a ficheros ha de realizarse de forma obligatoria dentro de una seccin trlz/c:4i.L. Adems de los dos mtodos indicados pueden serte tiles algunos de los siguientes: gelri'i.teslrir'() devuelve la ruta absoluta donde se estn guardando los ficheros. set-Dlr'(i crea un directorio en tu almacenamiento intemo (o lo abre si existe). clei.er-eFi..r.e o borra un fichero. f it.er,:isL O devuelve un anay con los ficheros almacenados por tu aplicacin.

7.3.2. Almacenando puntuacones en el sistema de ficheros interno El siguiente codigo muestra una clase que implementa la interfaz
Almacenp'rrntuac i one s utilizando los mtodos antes descritos.

public clasg AimacenFuntuacicnesFichero i.nplenents ;'\knacenPruti-uacicnes private static Si:r'ing FICHERO = 'tpuncuaciones.txt"; prLvate {:oni.exlr. context; public AlniacenPuni:racionest'ichero iConl-ext: conr:ezt ) this. context = corltexL,'
t

eOYerride

public

rry

voJ.d i-:r-ra:'c1lrPuntuar:o:{

j.nt purttos, Str.ing ncsib::e, long fecira)


(FICHERO,

i Fileoutput.Stream fos = contexE.openFileouLput


f o* . wr:' Le (text.o . get-syt-es i ) ! ;

St.ring Lextl<r = p-i'!Lcs + r'

Context . MODE_APPEND't + ncmirre .+ !'\n'

fos.closeii; 1U

Almacenamiento de datos

i catct (Excepi:ion e)
t
1

i
;

Lq. (t'Ast.eroj-des", e.<]eli'lessaq(), e)


t

,irtverr ide publlc vect.cr.:String>

Veci::or':<i:lt:r..i..fl.c1> r:e::ir:.1.t:

rry

listaPli::t.uacj.cres iint cantiriadi - rew Veci:-()r<;t:r.i:rlgl" { } ;

t Iii.i.e.!in:ui::$!:::r'+::ir

ga.rj.rrq cerf.3lla = ""; int i.; int rr = 0;

ili.s = context^i:i:*nIti.i.e.:.n:i.ri:: ,FICIIERO):

doi

i = fis.readi); if (i !='\n') { cail*na ' :r^clena .u (char) i; .|


eLse
{

res:ii.:ll. i: . ;ir1.<1 i cilci:r* {:id.r.lfl..i

* "";

fl-i + ;

i while (.i :" 0 & n .: ci:ni::-i:1ai.1) ; fis.ciose(); i catch (Excepilcr: e) i I.,*gtr. {.'("Asteroideg'r, 1 .eet.llrgsq{:
)

r;)

return resi:lt.;
\
)

7.3.3. La tarjeta SD
Los telfonos Android suelen disponer de memoria adicional en forma de tarjeta SD extrable. Este almacenamiento suele ser de mayor capacidad por lo que resulta ideal para almacenar ficheros de msica o vdeo.
Para acceder a la tarjeta SD utiliza la ruta

partir

de la

i sciarcti... versin 1.6 resulta necesario declarar

el

permiso

i';R.i'l'ji_ilY:iiiF-lilA1.*fi::'O[I\--:ii fl "ir.rdrr::itil'anrf: *si:-.y"rrJ. plt accedef a este feCUfSO.

185

El gran libro de Android

aplicacin. Has de tener en cuenta que estos ficheros no podrn ser modificados. Por ejemplo, si anastra cualquier el fichero caLcs a la carpeta res.,,ra,r, podrs acceder a l usando Iiescurcej . .rpentsawesoi.rce (R. raw . <!ialos; ) . Recuerda que todo fichero en la carpela t'aw nunca ser compilado. El siguiente codigo ilustra como utilizarlo:

7.3.4. Acceder a un fchero de los recursos Tambin tienes la posibilidad de almacenar ficheros en el paquete de la

InputStr:eam i.:,'

t"y

bytei.i reaiter = new byteiis.a'.railabIeO j; while i.e,r'e.edir:radcr') l= -1.) { ) eatch iTOffxcepti.orr c) { I.,og.e ("Asteroids", .geblvtessagei), e)
)

i :{ =

geLR.e$<}.ir:r,::er:.i { )

oie:lRawFescur.ce (R. r.aw. f i.cher.o)

7.4. Trabajando con XML


Como sabrs XML es uno de los estndares ms utilizados en la actualidad para codificar informacin. Es amplamente utilizado en lnternet, adems como hemos mostrado a lo largo de este libro se utiliza para mltiples usos en el SDK de Android. Entre otras cosas es utilizado para definir Layouts, animaciones, A.rid rc'i dMr ri j. f e;t . xm1.,, ..
. . Una de las mayores fortalezas de la plataforma Android es que se aprovecha el lenguaje de programacin Java y sus libreras. El sDK de Android no caba de ofrecer todo lo disponible para su estndar del entomo de ejecucin Java (JRE), pero es compatible con una fraccin muy significativa de la misma. lo rism ocurre en lo referente a trabajar con XML, iav dispone de gran cantidad de Apls con este propsito pero no todas estn disponibles desde Android. Libreras disponibles:

o
o

Java's Simple API for XML (SAX) (paquetes ors.xmi . sax.*)


Document Object Model (DOM) (paquetes o:.s. w3c
.

dcm.

*)

Libreras no disponibles: streaming API for xML (stAX). Aunque se dispone de otra librera con funcionalidad equivalente (paquete org.:anlp.rrJ-r.vi- .xmlFuJ-lFarser).

186

Almacenamiento de datos

o Java Architecture for XML Binding (JAXB). Resultara


pesada para Android.

demasiado

Como podrs ver al estudiar los ejemplos, leer y escribir ficheros XML es muy laborioso y necesitars algo de esfuerzo para comprender el cdigo empleado.

Vamos

explicar las dos alternativas ms importantes, SAX

planteamiento es bastante diferente. Tras ver los ejemplos podrs decidir que herramienta se adapta mejor a tus gustos personales o al problema en concreto
que tengas que resolver.

DOM.

El

El ejemplo utilizado para ilustrar el trabajo con XML va a ser el mismo que el utilizado en el resto del captulo: almacenar las mejores puntuaciones obtenidas. Elformato XML que se utilizar para este propsito se muestra a continuacin:
.r?-xln.l,
.:
.i. ,;.

vereion= "-l , t) " encoding= "UlLq'-8 "?:'

i:
.:

t u c: i. oil.: s > )il ril.. i.r.i:r,:: :i.,:>i: f ecia =' .i 2 B -c i 2 2 0 2 3 4 :L i " > (: llf)riirr te )'Mi nombre< r/ni:ilibL r :'
;1.....1.rr-lir ,:

]:ii.t.rl.i: i.;ls >4 5 0 0 0 < ,,''t:;i.:ri i: (::f.i >

.: /"i:ti.l.i i t:'r."i].(:::i. <:'ii --

.r.i:ii:'rt;ii.;lc'icli f eclia= " i2EBi2242Ii.'i2


,:.il

":

oriil:
i::

L' r,:

>Ot
o.i] ),

ro nombre
t
i
,1

< i f: <:i:ill

:.'

<:

>

.:;>u r:
..
,,,'

r:>s:i:..3 1 0 0 0

;i; n i:-c>s:l :..

$i;:t

t-

i.rii c
i:

i
1

.: r; -'i. i. r:: t.. 1r.... l;t l

t.;t.::

i,i)i

l,r:

.>

7.4.1. Procesando XML con SAX


El uso de la API SAX (Smpfe API for XML) se recomienda cuando se desea un programa de anlisis rpido y se quiere reducir al mnimo el consumo de memoria de la aplicacin. Eso hace que sea muy apropiado para un dispositivo mvil con Android. Tambin resulta ventajoso para procesar ficheros de gran
tamao.

SAX nos facilita realizar un parser (analizador) sobre un documento XML para as poder analizar su contenido. Ha de quedar claro que SAX no almacena los datos. Por lo tanto necesitaremos una estructura de datos donde guardar la informacin contenida en el XML. Paa realizar este parser se van a ir generando

una serie de eventos

secuencialmente. Por ejemplo, generar los siguientes eventos:


a Conieiiza a:rj:n:i eiiila Te:.:t ':, Ce F .!.la.i. j. za Ccnie;:za
C<;i.!- e;: e e.i.

medida que

se vaya leyendo el documento al analizar el documento XML anterior, SAX


t-t: a r: -i- o:,' e

e:iie::

i-:

i. i. t:

I a_:t":ii

eiene::io:
3-!a'd.::i

pi-ii:t.uacioi-i, coii at::ibr i.o ieclia,,, "i;:88i;:U01:-14i0"

ef e:ileriirj ; .l:.;nb:i:e

: i{i r!:ib* : lloiirl:re eiene:-.o : pL:::"-os


e.:.oei t:o

187

El gran libro de Android

'l.iilt-c de ncdc:

45QCC

Fi.neLiza elenielto : lliltos Finalize elerento: puntuecion L'cni.en.rr* elenlenlo: punt:uei:i.on, con etri.bitro f*i:ha= "1.2881-22428132"
Ccrnen=a elementc: rcnbre 'I'eetc d.e nr-:d.c: Otrc; nc:mbre

Final.iza eLewiento: rr:uib:e tlcnienze elemerho : plnho.g Te:(t de ri$di!: f 1.00Ct FinIiza elementcr Funtcs j.r)r't F j. na.l. i za e.l. ernentr : iir.1l1t'.r.1a Finaliza elelienr-o: lista Duntuaciones
c.:

Para analizar un documento mediante SAX, vamos

escribir mtodos

asociados a cada tipo de eventos. Este proceso se realiza extendiendo la clase r'e1:air:t.r.riarr<i1.er QU nos permite reescrbir 5 mtodos. Los mtodos listados a continuacin sern llamados a medida que ocuran los eventos de el proceso de
lectura secuencial del documento.

.
o .

sLa::tDccunent
en,lDr:cuneirt
(

Comienza el Documento XML.

i:

Finaliza documento XML.

irit.:r.:r't-i:li.ernent- {SL:':i.i:rg t.t.:r':i., SL:'i.ngi r}onfiref,o{:a.!., St:t':i.;tg no:nhrecual.if , Att"':ihutes aL::j.hutcsi : Comienza una nueva etiqueta; se indican los parmetros:

uri.:

La uri del espacio de nombres o vaco, si no se ha definido.

ncnb:'tLcca:: nombre local de la etiqueta sin prefijo.


nomir::ecual

if : nombre

cualifcado de la etiqueta con prefo.

arib.rr.,cs, lista de atributos de la etiqueta.

e:rci8ienrent

(String uri, SLring norrel,ocai, 9tring


):

ncnrbrecuaiif

Termina una etiqueta.

caracteres dentro de una etiqueta. Es decir

coniienzo, inL long:i Lrrd.) : DevUglVe IOS en ,:et.j.suerr.r.> caracteres </'etiquta" Devolvera car-acleres. Para obtener un String con estos CafaGtgfes: String s = nevr Strinq ich, comien:o, longituc) . MS
chlrlct,er*: (r:hi.:r' ch i.i

:.::rL

adelante veremos un ejemplo de cmo utilizar este mtodo.

Una vez descritos los principios de trabajo con SAX, pasemos a implementar

la interfaz

Alrriacer!;:i:iriLr.iici.ore. mediante

esta APl. Para ello crea la clase

AlnaceriP*ntirac:ioneiext'{1:,_sAx en la aplicacin Aster:'Eri.clei.; cdigo:

escribe el siguiente

public class A.lm"rcenPuritlrarji.rle-q:{lull,,-SL:{ Laplenenls Al.mace,*rPun.tr:acj.oires private statl.c SirinS FICHERO = 'rpuntuaciones.xml";
188

Almacenamiento de datos

private Cc+Lezt: contextoi private I.,i.s1:.i.|airt:ir.c:j.oies 1ista,' private boolean cargadalista ; public Al.rnacen.[?i.u':titacorie;;XI,1I.,_SA> ((::oi:t.ext, this.contexto * corlt.*xio; lista - new LisLapliniuacicnesi); cargadalista = false;
I
t

(:]oi!trex{:.()) I

,QCverri,ie

public void

,<;:.ulr'<ilr.-Pur:t-lraci.cn

ilnt

l:!i-liil:c'r$, Strj.;.::g ;ionl-:r'e.

rry

Iong fi,:ctia)

if (:cargadalistali
1ist.a. leerxlvll. icontexto. oper:Filel::put (FfCHERO} ) ;
i
(

i catch icatch

i.;'.i.l'eoi:i:'*u'il6xcept

ioir e )

(Exc:r.r':t.j.r:rl.

e)

Lo.l . r.(,rAsteroi.des'r, *..Jei:!!i$Sr:ri.Je ()

, e) ;

lista.rluevc ipunlos, rrcmi-.re, f echa) i

rry

1ista.

e:cr::.}:.i.::Xl,I,

(contexto.

'i

i catch (Excepi.iorr e.) { ILarJ . i:, ( "As t.eroids rr . . . -qe i:v:iLq sirJei ( ),

+pnFi.i.rrtihrl.r1. \FICHERO, Ccrlt'exi .MODE_PRIVATE) )


r.:

public r'/ector(gtrinq> lista!'rr:rtuclciones iint canticacii

i'Ca'err.^i.ce

rry

if (:cargadalista)i
1i-sta. leerxl'{L icontexto. ore:'rl:'ilelnpirt i}'ICH&?O) i
i
;

i catch
j

(.tixi::er:i.on

*)

{
;

Log.r.,i"AEiteroides!r, e.geli"i+ssrge O, *)

returr lista.
)

;iYei::tr:rijtr:..i.rlcr i )

La nueva clase comienza definiendo una serie de variables y constantes. En primer lugar el nombre del fichero donde se guardarn los datos. Con el valor indicado, el fichero se almacenar en,r'aata./ciata,/'orq. ezainpie. ast.ei-cidesl
189

Elgran libro de Android


f i.1esr'punr.'aci.oi:es.xnt. Pero, puedes almacenarlos en otro lugar, como por ejemplo en la memoria SD indicando 7'sdcard,lpuntuacicires.xml-. La variable ms importante es lista de la clase Listapunr.uaciones. En ella guardaremos la informacin contenida en el fichero XML. Esta clase es definida a continuacin. La variable cargarsaList,a flos indica si l isra ya ha sido leda desde el fichero.

El cdigo contina sobrescribiendo los dos mtodos de la interfaz. En i:.r;.rr<iar:Puritlacicnii comenzamos vefificando si ti.sta ya ha sido cargada,

para hacerlo en caso necesario. Es posible que el programa se est ejecutando por primera vez, en tal caso el fichero no existir. En este caso se producir una excepcin de tipo FileNotFoundExcepiion al tratar de abrir el fichero. Esta excepcin es capturada por nuestro cdigo, pero no realizamos ninguna accin dado que no se trata de un verdadero enor. A continuacin se aade un nuevo elemento t i.sra y se escribe de nuevo el fichero XML. El siguiente mtodo, :]. i.taPill:tuacon ( ) , resulta sencillo de entender al limitarse a mtodos definidos en la clase ListaEunt.uaci-ones. Pasemos

mostrar el comienzo de la clase r,isr.apunruaciones. No es

necesario almacenarla en un fichero aparte, puedes definirla dentro de la clase anterior. Para ello copia el siguiente cdigo justo antes del ltimo ) de la clase
A,l.rraceiPlrl tuiar::.
<n

e*XML,

SAX

private claeE ListaPuntuaciones prlvate clase Puntuacion


J"nt puntos;
{

String nombre; long fecha;


)

prlvat,e List<Puntuacion> listaPuntuaciones

publtc ListaPuntuaciones O { listaPuntuaciones = new ArrayListcPunEuacion>O ;


)

public vold nuevo(lnt puntos, String nombre, long fecha) Puntuacion puntuacion = neer PuntuacionO; puntuacion.puntos = puntos; puntuacion.nombre = nombre; puntuacion.fecha = fecha;
1

istaPuntuac iones . add (puntuacion),.

pubLic Vector<String> avectorStringO { Vector<String> result = aer Vector<String>


190

O;

Almacenamiento de datos

for (Puntuacion puntuacion : listaPuntuaciones) { result . add (puntuacion. nombre+ " " +puntuacion. puntos ) ; returrt result;
)
)

El objetivo de esta clase es mantener una lista de objetos plnL'riaciclr.


Dispone de mtodos para insertar un nuevo elemento (nr:urv,:' ( I ) y devolver un listado con todas las puntuaciones almacenadas (aveci::or:si::r.:i.ng i )).

Lo verdaderamente interesante de esta clase es que permite la lectura y escritura de los datos desde un documento XML (lee1'ri.{L (i } escribi,rx}.rl ( ) ). Veamos primero como leer un documento XML usando SAX.
pubJ.ic void leerXl"llifnputStream ei:tracta) throws Except.ion
S.A>;parser:Foc-'!:ory fblr:.c".
{

SAi4">arser parse'r

li.Al(fiir:s*rFi.flt.cir'.r'..r;,:1r,..rl:;,si;i:i'rO;

= fabrica.nev-SAXFarserO ; Xl'4:.1rleader leci:or : !irser'.get:j(F!rReii.<1er: ( ) ; &'iane-i adr:rrX[u'!J, tianej aclcrXl'4L = ae$ Niane:i adc::r'X-trii,
I ec or . se t. Ccnt. ent.Hand I
i*

m.:.ne

1.cor.parse inew in;-ut.,Surc (onirada) ) ;

i adorlo,il ) ;

cargadalista - true;
)

Para leer un documento XML comenzamos creando una instancia de la clase

lo que nos permite crear un nuevo parser XML de tipo Luego creamos un lector, de la clase xMr,F,clacier.-, asociado a este parser. Creamos rn;r;ierji.rtior')tMl de la clase xMi,Ha<ii.crr y asociamos este manejador al xqlneacer. Para finalizar le indicamos al xr'rr,Reac-er QU entrada tiene para que realice el proceso de parser. Una vez finalizado el proceso, marcamos que elfichero est cargado.
SAXI?;r:r,;*:l'ti;ii::i:i):r'./,
SAXFaT:r-r+:'.

Como ves el proceso es algo largo, pero siempre se realiza igual. Donde s que tendremos que trabajar algo ms es en la creacin de la clase xtri:.,r:r.rd:1.e1.., dado que va a depender del formato del fichero que queramos leer. Esta clase es listada a continuacin. Puedes escribir este cdigo a continuacin, dentro de la
clase anterior:

elass ManejadorXMl extends DefaultHandler private StringBuilder cadena; private Puntuacion puntuacion;
ir,'Ovr: r:r'
:i.

<1*:

public void startDocument O ttrows SAXException

191

Elgran libro de Android

listaPuntuaciones = ner Arraylist<Puntuacion> O ; cadena = ner StringBuilderO;


]

riCe public void startElement(string uri, String nonibrel,ocal, string nombreCualif, Attributes atr) throws SAxException { if (nombrelocal , eguals ('tpunLuacion" ) ) { puntuacion = new PuntuacionO; punLuacion. fecha = Iong.parseLong(atr -getvalue ( "fecha ) ) ; l
,d(li't
) @i:)vs:r'::'.i.ile

pubttc vol.d characters(ctar ch[], lnt comienzo, tnt long)


cadena.append(ch, comienzo, long)
) ;

(l0verii.le

publte vold endElement(String uri, String nombreLocal, String nombreCualif) throwe SAXException { Lf (nombrelocal . equals (',puntosu { puntuacion.puntos = Integer .parselnt(cadena.toString elee tf (nombrelocal.eguals("nombre")) { ) puntuacion.nombre = cadena.toStringO ; ) else lf (nombrelocal.equals(,'puntuacion") ) {
I istaPuntuaciones . add (puntuacion)
.

));

cadena
) )

setlength

(0

);

liliv i' r
)

l: i. cl.r:r

public woid endDocument O throws SAXException

el proceso de parsing en SAX. En srarrnocument. ( ) nos limitamos a inicializar variables. Elt siarreieneni O verifcamos que hemos llegado a una etiquta <p'.lni-uacin'. El tal caso, creamos un nuevo objeto de la clase punruar:icn y
inicializamos el campo f echa con el valor indcado en uno de los atributos.

Esta clase define un manejador que captura los cinco eventos generados en

El mtodo ci:.aract"ers O es llamado cuando aparece texto dentro de una (<etiqreta> caracierrs <'etio-rreta>). No limitamos a almacenar este texto en la variable cadena para utlzarlo en el siguiente mtodo. SAX no nos
etiqueta 192

Almacenamiento de datos garantiza que nos pasar todo el texto en un solo evento, si el texto es muy enenso se realizaran varias llamadas a este mtodo. Por esta razn, el texto se va acumulando en ce,.1err..

El mtodo encirrlleLient:i) resulta ms complejo, dado que en funcin de que etiqueta est acabando realizaremos una tarea diferente. Si se trata de .:,/ii:-rirt.c-,s:" o dB <'r:crrii:re,-- utilizaremos el valorde la variable <:*<i*ni; para actualizarel valor
conespondiente. Si se trata d aadimos el objeto "tl;'.rr.;r.ai:;i.iirl->
la lista.
pl.;rr-uir.-'j..1r. a

Veamos el ltimo mtodo de la clase Lisrapunr.iracici:es, que nos permite


escribir el documento XML:

pubLic vol-d escribirXMl(Outputstream salida) { XmlSerializer serializador = XmI .newSerial.izerO

rry

serializador.setOutput (salida, "IJTF-8") ; serializador.startDocument ( utlTp'-I", true) ; serializador. startTag ( " ", "11sta_puntuaciones" ),. for (Punt.uacion punLuacion : listaPuntuaciones) { serializador.st.arLTag(" ", "punt-uacion") ; serializador.attribuL (" ", "fechat', String. valuelJf(puntuacion. fecha) ) ; serializador. startTag(" ", "nombre'),. serializador. t.ext (punLuacion.nombre) ; serializador. endTag ( " ", "nomlrre" ) ; serialj-zador. startTag( "',, "puntos" ) ; seriali-zador. text. (String .vaLueOt (puntuacion.puntos) ) ; serial j-zador. endTag (', " , ,'punLos" ) ; serializador.endTag( " ", "Funtuacricln") ;
)

serializador. endTag ( "', , "1ist-a_punluaciones" serializador. endDocumenb ( ) ; i catch ( tlxi:rilit ri + ) i Lo.J. ( "Asrtreroidesi", +. <ei:i"ies.l.:age { ), +) ;
:i. <>

)i

t:

)
) ) )

x:riseriaii:er l que se le asigna como salida el oupi;t-st-rean: QUB hemos


pasado como parmetros.

Como puedes ver todo el trabajo se realiza por medio de un objeto de la clase

193

El gran libro de Android

7.4.2. Procesando XML con DOM


DOM (Modelo de Objetos del Documento) es un API creado por W3C (World Wide Web Consortium) que nos permite manipular dinmicamente documentos XML y HTML. Android soporta el nivel de especificacin 3, por lo que permite trabajar con definicin de tipo de documento (DTD) y validacin de documentos. Para no extender en exceso los ejemplos no vamos a entrar en la definicin y validacin de documentos. Como ya hemos comentado, el planteamiento de DOM es muy diferente al de SAX. DOM permite cargar cualquier tipo de documento XML y manipularlo directamente en memoria (RAM). Podremos crear nuevos nodos, o modificar los existentes. Una vez dispongamos de la nueva versin podremos almacenarlo en
un fichero o mandarlo por lnternet.

Trabajar con DOM tiene sus ventajas frente a SAX, por ejemplo nos evitamos definir a mano el proceso de parser (clase Ma:rr:ja<ic;:xur,) y crear una estructura para almacenar los datos (clase ,j,sr.apunruaeiones). Pero tambin tiene sus inconvenientes: Recorrer un documento DOM puede ser algo complejo, adems, al tener que cargarse todo el documento en memoria puede consumir excesivos recursos para un dispositivo como un telfono mvil. Este inconveniente cobra

especial relevancia al trabajar con documentos grandes. Para terminar, DOM procesa la informacin de forma ms lenta.
Veamos cmo se implementa nuestro ejemplo mediante elAPl DOM. Para ello crea la clase ainacenplrnluacioneExltl_DoM en la aplicacin Asleroides y escribe el siguiente cdigo:

publ.tc class AlmacenPuntuacionesXML_DOM Lnplenente AlmacenPuntuaciones{ rrivate static String FICHEI?O = 'tpuntuaciones.xml'r; private Context contexto; prlvate Document documento; private boolean cargadoDocumento;

public AlmacenPuntuacionesXML_DOM (Context contexto) this.contexto = contexto; cargadoDocumento = fal.se,.


)
ilL1ver.

ri-Lle
{

public void guardarPuntuacion(int puntos, String nombre, long fecha) t"y { if (lcargadoDocumento) { leerXML (contexto. openFilelnput (FTCHERO) ) ; ) catch (FileNotFoundException e)
crearXMlO;
194
) {

Almacenamiento de datos

) catch (Exception e) { Log. e("Asteroidesr, e.getMessage O, e) ;


)

nuevo(puntos, nombre, fecha)

rry

;
1

esc

ribi

rXML ( context.o . openFi

eOutput

F ICHERO,

) catch (ExcepLion e) { Log.e("Asteroides,, e.getMessage O, e) ;


) )

Context. .1,ODE_PRI\ATE) ) ;

rice publtc Vector<String> listapuntuaciones(int cantidad)


fi,".;er

rry

if (lcargadoDocumento)
)

leerXML (contexto. openFilelnput.

(I7.IC'IIERO)

) catch (FileNotFoundException e)
crearXMl ( ) ; (Exception e) catch )
{

Irog.e("Asteroidestt, e.getMessage O, e) ;
(

return avectorstring
)

interfaz. Su funcionamiento es similar al ejemplo anterior, aunque ahora en lugar de definir una clase nueva, definiremos los mtodos: cr.ear:xr'{r,i), .l.eerx-Mi(), rlrre\.'o {) , irv*ctc,r'.(-:t}:'j.rig i) I es<:r: it;.i r:xMI., ( ) . ESTOS mtodos tienen por objeto
interactuar con el objeto clr;ern-rr:ntc. Veamos los dos primeros.

que en el ejemplo anterior con la excepcin de ik-:cr.;nr.rritc; d la clase atq.\i:Jx.l.on.rJouinent.Dcllinnl. En este objeto mantendremos en memoria nuestro documento XML. Tras el constructor, se definen los dos mtodos de la

La clase comienza definiendo una serie de variables y constantes. Son iguales

publ"ic void crearXMlO { try { DocumentBuilderFactory fabrica

DocumentBuilderFactory . tlewlnstance O ; DocumentBuilder constructor = fabrica.newDocumentBuildero ; document.o = constructor.newDocument O ; Element taz = documento. createElement ('r:I..sta puntuacicnes,' ) ; documento. appendChi-Id (raiz ) ; cargadoDocumento = true,. ) eatch (Exception e) {
195

Elgran libro de Android

Log.e("Ast.eroidesrr, e.getMessage O, e)
)

public void leerXML(fnputstream entrada) throwe Exception DocumentBuilderFactory fabrica =

Document.BuilderFactory . newlnstance ( ) ; DocumentBuilder constructor = fabrica.newDocumentBuilder ( ) ; documento = constructor.parse (entrada) ; cargadoDocumento = truet

y I I i I

Efl crear]lMl, ( ) COmenzamOs COnstruyendO ObjetOS DocumenbBuilcerFactory DocumentBuild.er pafa podef cfear una nueva instancia de documento. Creamos un nuevo elemento que es aadido enlaraizde doc,..rmerrc. Finalizamos marcando que eldocumento est creado.

mtodo nu,rse ) Qu se encargar de procesar la entrada. Como puedes comprobar el Otoceso de lectura del documento es mucho ms sencillo en DOM que en SAX. Veamos los siguientes dos mtodos.

En leerKul.,i) el proceso es similar, aunque ahora llamamos al

publtc vold nuevo(lnt puntos, String nombre, long fecha) { Element puntuacion = documento.createElemene ("puntuacion") puntuacj-on.seEAttribute ( "fecha', String. vaTueOf(fecha) ) ; Element e_nombre = documento.createElement ('tnombre") ; Text texto = documento.createTextNode(nombre) ; e_nombre . appendChi Id ( texto) ;
puntuacion. appendChild ( e_nombre ) ; Element e_puntos = documento.createElement ("puntos") ; texto = document,o.createTexENode (String. vaTueOf(puntos) ) ; ejuntos . appendChild (texto) ; puntuacion. appendChild (e_puntos ) ; Element raz -- documento.getDocumentElement ( ) ; raiz . appendChild (puntuacion)
,.

public Vector<String> aVectorStringO { Vector<String> result = oerrr Vector<Stringr O ; String nombre = n rt, puntog = rr tr Element raz = documento.getDocumentElement O ; Nodelist puntuaciones = raiz. getElementsByTagName (,'puntuacion,' for (int i = 0; i < puntuaciones.getlength0; i++) { Node puntuacion = puntuaciones.item(i) ; Nodelist propiedades = puntuacion.getChildNodes O ;
196

);

Almacenamiento de datos

for (int, j = 0; j . propiedades.getlengthO; j++) { Node propiedad = propiedades.item(j) ; String etiqueta = propiedad.getNodeNameO ; if (eLiqueta. eguals (',nombre" ) ) t nombre = propiedad.getFirstchildO .getNodevalue ) else if (eLiqueta.equals(',puntos")) { puntos = propiedad.getFrstchildO .getNodevalue
)

O O

result.add(nombre + u " + puntos);

return result,.
)

El mtodo rlu-e\ci) tiene por objeto insertar dentro de aicrculre:rtc una nueva etiqueta <ounti-rci,)n> con los atributos y etiquetas interiores necesarios. Comienza creando el elemento i,.:nl::rar-:irr al que le aade el atributo fei:ba. Luego, crea el elemento :n<>inl>r:.e y le aade el texto correspondiente. Este
elemento es aadido como hijo d;>iirir: r.r;r,::i.,r;'r. El mismo proceso se repite para el elemento puntci$. Para finalizar el elemento :r.r;ir-uec-.i.or s odido a la raiz del
documento.

puntuaciones almacenadas. Para ello iremos recorriendo todo el documento comenzando por el elemento raiz. Obtenemos la lista de nodos glnruar:ioles., para recorTerla en un bucle ::r:r.. Par cada uno de estos nodos, obtenemos en ;:r'i:;p:i.ei:1;:i:1es los nodos hijos. Recorremos esta segunda lista en un nuevo bucle, donde analizamos si el nombre del nodo es n.o:nbro o pur;rt:i-r, y guardamos el valor asociado al nodo en la variable correspondiente. Antes de pasar al siguiente odo pr"iniuaciorr, aadimos lo obtenido al resultado. Veamos el ltimo mtodo.

El mtodo a.-rrecrcrsr::in,c ( )

devuelve

un vector de sfnngs con

las

public void escribirxMl(Outputstream salida) lhrows Except.ion { TransformerFact.ory fabrica = TransformerFactory.nerl/rnstanceo Transformer t.ransformador = fabrica.newTransformerO ;
DOMSource

transf ormador . se toutput Property ( output Keys . olq I T_KM|,_DECLAR AT I oN, " ye s r' ) ; transformador. setoutputProperty (OutputKeys . JNDEIV?, "yes " ) ;

fuente = nee DOMSource (documento) ; StreamResult resultado = new StreamResult (salida) transformador . transform ( fuente, resul tado ) ;

) )

Este mtodo permite escribir el documento XML utlizando un objeto de la clase jav:ix.xinl .i*..in,.;fc)rri.T;:ansir-rrii.:::.. Tras Configurarlo se le indica cOmo fuente cicc:.rnr-r:-:t('r y como resultado de la trasformacin l t--rii]:;r.ir-jt.r,r1nl pasado

197

El gran libro de Android

como parmetro. Por desgracia la clase rr:ansformer solo est disponible a partir del nivel de API 8. Si quieres implementar una aplicacin que sea compatible con la totalidad de telfonos te proponemos que reemplaces el mtodo anterior por el
siguiente:

publtc voLd escribirxMl (Outputstream salida) throwe Exception String s = seriaiiza(documento.geCDocumentElement O ) ; salida.write (s.getBytes ('tUrI'F-8tr) ) )

public static String serj-aliza(Node raiz) thrors lOException { StringBuilder resultado = ner. Stringeuilder O ; if (raiz.getNodeTtpe O == Node. "EXT_NOD.E) () ) ; resultado. append ( raiz . getNodeValue elee { if (raiz.getNodeTypeO != Node. DOCUMEN'I' NODE) { StringBuffer atributos = new StringBuffer O ; for (tnt i = 0 < ralz.getAtEributes O .getlengEh0 ; ++i) atributos . append ( ,' ,' ) . append (raiz.getAttributes ( ) . item(i) . getNodeName ( ) )
.append(n*\iltr)
.

append

(raiz. getAtLributes ( ) . item () . getNodeValue


il).
( ;

.append(il\n

resultado.append ( "< " ) .append (raiz, getNodeName .append(u u) .append(atributos) .append("r")

) else

.append ("<?xml versi.on=\n1. 0\" encoding=\',UTF-8\"?>" l Nodelist listaNodos = raiz.getChildNodesO ; for (int i = 0; i < listaNodos.getLength0 ,. i++) { Node nodo = listaNodos.item(i);

Resultado

);

resultado . append ( seriaT iza (nodo) ) ;

tf (raiz.getNodeTypeO != Node.DOCUMENI:_NODE) { resultado.append ( u<,/ u ) .append (raiz. getNodeName


.

append

( ,, >tt

)i

return resultado.toString () ;
)

El mtodo se::i.liza(i convierte un documento DOM a un sirinq. No es especfica de nuestro ejemplo, puedes usarla para convertir cualquier documento
198

Almacenamiento de datos XML. Se trata de una funcin recursiva. A partir de un nodo que se pasa como parmetro, se encarga de realizar una nueva llamada por cada uno de los nodos que contiene. Si queremos convertir todo el documento, en la primera llamada hay que indicar en el parmetro el nodo raz del documento.

7.5. Bases de datos


Las bases de datos son una herramienta de gran potencia en la creacin de aplicaciones informticas. Hasta hace muy poco resultaba muy costoso y complejo incorporar bases de datos a nuestras aplicaciones. Afortunadamente, Android incorpora la librera SQLite que nos permitir utilizar bases de datos mediante el lenguaje SQL, de una forma sencilla y utilizando muy pocos recursos del sistema. Como vers en este apartado, almacenar tu informacin en una base de datos local no es mucho ms complejo que almacenarlos en un fichero, y adems
resulta mucho ms potente.

SQL es el lenguaje de programacin ms utilizado para bases de datos. No resulta complejo entender los ejemplos que se mostrarn en este libro. No obstante, si deseas hacer cosas ms complicadas te recomiendo que consultes alguno de los textos recomendados en la bibliografa.

Para crear nuestra base de datos nos basaremos en


que implementar los mtodos
crri-'r.r)sti.j

la

clase

sr)Li.:::ei.ip*:nr.rei.p*:r'que nos facilita tanto la creacin de la base de datos, como el

trabajar con futuras versiones de esta base de datos. Para crear una subclase hay

O,

onopen () . La gran ventaja de utilizar esta clase es que ella se preocupar de abrir

orru;c1r:;..ide

Oy

opcionalmente

la base de datos si existe o de crearla si no existe. lncluso de actualizar la versin si decidimos crear una nueva estructura de la base de datos. Adicionalmente esta Clase tiene dOS mtOdOS gel:!.eied:e;:ller;at:abaiie ( ) y get:'tr':ltablel;ar:-ai::a..,':e i) QUe abren la base de datos en modo solo lectura o lectura y escritura. En caso de todava no existir estos mtodos se encargarn de crearla.

7.5.1. Utilizando una base de datos para guardar puntuacones


Pasemos a describir cmo guardar las puntuaciones obtenidas en Asteroides en una base de datos. Si comparas la solucin propuesta con las anteriores vers como el cdigo necesario es menor. Adems, una base de datos te da mucha ms potencia; puedes por ejemplo ordenar la salida por puntuacin, eliminr filas antiguas, etc. Todo esto sin aumentar apenas el uso de recursos.
Aln:,r:errPrnirrscj.c)rler,sltQI.,i.t'*
::ij-i.ceria.Fr-rnt.L1.c:olle

y escfibe el cdigo siguiente. La nueva clase adems de extender sqlit.eupenHetper va a implementar la interfaz
para permitir un acceso homogneo a los datos desde

En primer lugar crea una nueva clase con

nombre

Asteroides.

199

Elgran libro de Android

public elasg AlmacenPuntuacionesSQlite extends SeLiteOpenHelper


implenente AlmacenPuntuaciones

public AlmacenPuntuacionesSQLite(Context context) super(context, "puntuaciones", nulI, 1-) ;


)

t!. i+

t:

.1

:;

rJ

+; $l l)i; i. I r: i.'tp ri nH e l. p r'


c-.i

(|f')\,ter! icle

publtc void onCreate (SQliteDat.abase db) { db.execSQL(trCREATE TABLE puntuaciones (',+ il_id INTEGER PRIMARY KEY AUTOINCREMENT, '+ puntos INTEGER, nombre TEXT, fecha LONG) ");
)
.1i(:lt"i::r'I: .i.de

publ.ic void onUpgrade (SQliteDatabase db, Lot, oldVersion, J.at newVersion) { il tr, asc rfe Lilta n1;eva versin habra cue act'rializar las tablas
)

publtc void guardarPuntuacion(1nt puntos, String

,//H'i.orlos de Alr:a-cenPurr!uaciones

nombre,

tong fecha)
"+

SQliteDatabase db = getwritableDatabase O ; db.execSQl(IINSERT INTO puntuaciones VALUES ( null, puntos+t', I rr+nofre+! I, "+fecha+") "),.
)

public Vector<String> listaPuntuaciones(lnt cantidad) { String tl CAMPOS = { "puntos", "nombre" } Vector<String> resulL = nw Vector<Stringt ( ) ; SQLiteDatabase db = getReadableDatabase O ; cursor cursor=db.query( "puntuaciones", CAMPOS, nuJ.J., aull, null, nuL1", "punLos", Integer. toString(cantidad) ) ; whlle (cursor.moveToNextO ) { result.add(cursor.getlnt (0)au u +cursor.getstring(1) ) ;
,.

returo result;
) )

El constructor de la clase se limita a llamar al constructor heredado con el


perfil:

200

Almacenamiento de datos
SQi.,:i.i.+O!:)eril{ei.fie}:(ilorrttexi:: a:orit-exi::(),

SQLiteatal-rage. CuisarFacto:'ir .:Lli-Bor, int

i.:1tr.i:ri.5.1 ncirirh:r'*.

.ersici'r.)

Los parmetros se describen a continuacin:

. . .

corrlexio: contexto usado para abrir o crear la base de datos.


n,:r:ibre: nombre
"puntuaciones".
i::1.i':$(:.'r':

de la base de datos que se crear. En nuestro

caso

se utiliza para crear un objeto de tipo cursor. En nuestro caso no lo

necesitamos.

o versicnr nmero

de versin de la base de datos empezando desde 1. En el caso de que la base de datos actual tenga una versin ms antigua se llamar a orltit)!r):'ac{e i) para que actualice la base de datos.

El mtodo orii:r'ei1r.e i) es invocado cuando sea necesario crear la base de datos. En nuestro caso tendr solo la tabla :'irit.r::ii:jcn.::j$ que es creada por medio del comando SQL ri. tAT TABI"F: tint;uac:.oirns;...

El
el

mtodo or:.upcra.*ei)

no ha sido

implementado.

Si ms adelante

decidiramos crear una nueva estructura para la base de datos, tendramos que indicar un nmero de versin superior, por ejemplo la 2. Cuando se ejecutara el cdigo sobre un sistema donde se dispone de una base de datos con la versin 1,

mtodo

<>:rt.lrgr:';;r<ie

comandos necesarios para transformar la antigua base de datos en tratando de conservar la informacin de la versin anterior.

ser invocado. En l tendramos que escribir los la nueva,

Pasemos a describir los dos mtodos de la interfaz r-J.n.,c:eneFunt'*acic:e,g. El mtodo r7uarrlar.r'rinluacioni) comienza obteniendo una referencia a nuestra base de datos utilizarldo get!.irit:ablelial,;ei::a..::ei). Mediante la cual ejecuta el comando SQL para almacenar un nueva fila en la tabla ir.rsliR? rl,;f i:) pun!:.ui*<-':i.<>nere ... El mtodO .i.i.;:rt:.;;Pr.rnt:ri:l<-'.i.<ire:i(i comenza Obteniendo Una referencia a nuestra base de datos utiliza[do i.:lri:tpei.rc:.rb].eiat.r.l-.ase i) . Realiza una consulta utilizando el mtodo querr'(), con la que obtiene un cursor que utiliza para leer todas las filas devueltas en la consulta.

7.6. Utlizando la clase ContentProvider


Los proveedores de contenidos (content providers) son uno de los bloques constructivos ms importantes de Android. Nos van a permitir acceder a informacin proporcionada por otras aplicaciones, o a la inversa, compartir nuestra
informacin con otras aplicaciones.

Tras describir los principios en los que se basan los ConfentProviders, pasaremos a demostrar como acceder a ContentProviders creados por otras aplicaciones. Esta operacin resulta muy til dado que te permitir tener acceso a informacin interesante, como la lista de contactos, el registro de llamadas o los
201

Elgran libro de Android


ficheros con informacin multimedia del dispositivo. Terminaremos este apartado mostrando cmo crear tu propio ContentProvider, de forma que otras aplicaciones puedan acceder a tu informacin.

7.6.1. Conceptos bsicos


7.6.1.1. El modelo de datos de diseo interno, de forma que podramos utilizar cualquiera de los mtodos descritos en este captulo. No obstante, cuando hagamos una consulta al ContentProvider se devolver un objeto de tipo clirsor. Esto hace que la forma
ms prctica para almacenar la informacin sea en una base de datos. La forma en la que un ContentProvider almacena la informacin es un aspecto

Utilizando trminos del modelo de bases de datos, un ContentProvider proporciona sus datos a travs de una tabla simple, donde cada fila es un registro y cada columna es un tipo de datos con un significado particular. Por ejemplo, el ContentProvider que utilizaremos en el siguiente ejemplo se llama caliloq, y permite acceder al registro de llamadas del telfono. La informacin que nos proporciona tiene la estructura que se muestra a continuacin:
date
1

number
2O:42

Duration
65

I2ILO|lO t6:to

t2lLtlIO

4 5

L3/LUto t2:L5 t4lLUtO 22:LO

555123123 555453455 555123123 555783678

INCOMING WPE
OUTGOING TYPE MISSED WPE OUTGOING WPE

355 90

542

Tabla 1: Ejemplo de estructura de datos de un ContentProvider.

Cada registro incluye el campo numrico ....id que lo identifica de forma nica. Como veremos a continuacin podremos utilizar este campo para identificar una llamada en concreto.
La forma de acceder a la informacin de un ContentProvider es muy similar al proceso descrito para las bases de datos. Es decir, vamos a poder realizar una consulta (incluso utilizando el lenguaje SQL), tras la cual se nos proporcionar un objeto de tipo c'Lr:rsor. Este objeto, contiene una informacin con una estructura similar a la mostrada en la tabla anterior.

7.6.1.2. Las URI Para acceder

uRl (ver estndar RFc 23go) es una cadena de texto que permite identificar un recurso de informacin. Suele utilizarse
identificarlo con una uRl. una frecuentemente en lnternet, por ejemplo para acceder a una pgina web. una uRl est formada por cuatro partes, tal y como se muestra a continuacin:

a un contentProvider en particular va a ser necesario

202

Almacenamiento de datos
'ist.and.ii.rd...pr:ef .:*>:
i

/.:ar:l.hcri.ty>,r,:d.a-r;1....!rilt.ll>,/,:i.rl>

La parte .:etantar"d_pi'*f ix:" de todos los ContentProviderha de ser siempre crlni:eni:. En el primer ejemplo de este apartado, utilizaremos un ContentProvider donde Android almacena el registro de llamadas. Para acceder a este ContentProvid er util izaremos la sigu iente U Rl : ccnlerrt. : r,/ca11....log I ee,LIs En la Tabla 5 encontrars otros ejemplos de URI que te permitirn acceder a
otras informaciones almacenadas en Android.

Para acceder a un elemento concreto has de indicar un .:lc> en la URl. Por ejemplo, si te interesa solo acceder a la llamada con identificador 4 (normalmente corresponder ala cuarta llamada de la lista), has de indicar:
r::oni:eni:: : I i c:a1.

L lcg/calls.r4

Un mismo ContentProvider puede contener mltiples conjuntos de datos identificados por diferentes URl. Por ejemplo para acceder a los ficheros multimedia almacenados en el mvil utilizaremos el ContentProvider Mecliastcre, utilizando algunos de los siguientes ejemplos de URI:
i:: e:n | : / I t,t:<:l :i. a I .i n t e:r'ri ;i 1.,/ :!. rilil ie s ccnt' er1t. : I i r:.e dL a i est. e rrra I i v i de // f-, conieni: i ie,edia.i* ia.t-dic>

<terl

Cada ContentProvidersuele definir constantes con sus correspondientes URl.

Pof ejemplO, anrlroiil . pro.",i<ler . :lal1Lcg


COll co:tenl', : I i t:a'LI _Ig/ca1 L$.

:a.l1s.'j

::cl'I'Ef{?_{.Inf COneSpOnde

Y la constante:
aricirc''c . Fr..'rvi.cielr'.Mc.j.li..1Store. A:-rcio.
Flr-.:c

j.;-. 1NTERI.jA.i.,....I-'OITF:l;T....rJRI.

COffeSpOnde Cofl coni en i :,/ 7' meeli a I tt'tz e.rnal- iartrf i o.

7.6.2. Acceder a la informacn de un ContentProvder Android utiliza los ContentProvider pata almacenar diferentes tipos
en Android:

de

informacin. En la tabla siguiente los ConfentProvider ms importantes disponibles

203

Elgran libro de Android

Clase
Browser

lnformacin almacenada Enlaces favoritos, historial de navegacin, historial de


bsquedas Llamadas entrantes, salientes y prdidas.
Lista de contactos del usuario.

Eiemolos de URls

content://browser/bookma rks content://call_log/calls content://contacts/people content://med ia/interna l/images content://med ia/externa l/video content://med ial*/a ud io content://setti ngs/system/ringtonr content://settin gs/system/notifical

CallLog

Contacts

MediaStore

Ficheros de audio, vdeo e imgenes, almacenados en dispositivos de almacenamiento internos V externos. Preferencias del sistema.
Palabras defindas por el usua-

Setting UserDictionary
(APl level3)

ion
rio, utilizadas en los mtodos de entrada predictivos.

sound

content://user_d ictiona ry/words

abla 5: ContentProviders disponibles

an Android.

7.6.2.1. Leer informacn de un ContentProvider


Veamos un ejemplo que permite leer el registro de llamadas deltelfono. Crea una nueva aplicacin con los siguientes datos:

Prcject na$e : Conl-eniCai.l-Log Bniid Target: Android i. S Appj.ication na*le : C(:,ntent,Caiil,og Package ilaine : ot'rJ, exil ln:1.e . prn.uac.i.onesir.ov Creale Activit'y: ContentCal"if,og ivlin SDK Vereion: 3

:!.

der.

Aade en el fichero resr7.'.i.;rycx,it-,/m.ri.ri.xr*l. la lnea subrayada:


<?x:ni. version=, L. 0,, errcoding= "ut f -B ineerl-Lavorit
"? >

<L

xmlns : androicl=

" http: I / *qchemas . android . cont,apk/.r.es /anciroid android : orientation= " vert i ca7 "

"

204

Almacenamiento de datos
andro i d : I ayout.._iv j- clt'h= " f j j andro id : Iayoui_he iqht.=,, f i
".'I't:x
i:-

l__: a r en
J j_:r a

r eri

t,, t"

:i.

er

android : id=,,@+id,/sai, Jda,' androi<l: layoui:_widt.h= " fj i i_p aren t " .l.ndro i d : 1 aycut_he i ght. =', h'r-ap*con t en t,' android : t.ext=,' t? st r ing 1. hei I o,'

t,

.: /.!.,.i.r.<l,r:r'.!-,.'i) (X.i i:

.>

De esta forma podremos identificar el Tezrview desde el programa y utilizarlo para mostrar la salida.
Aade al final del fichero A:rl-ci.:tr4ari f es;L. xmr las lneas subrayadas:

5u

s.-8.:9.:::.R.9.::II..l::

Pi.Sin

e*.f*:9.+_flj..ng.me.=:rncJro.id.perng.g::,,jjl*!:_!pA,:ig!_.::
i...$::i.f:f .:i;if'tl:ti:!.1i.::.i.,iliti:i

</rnr!i f erf,.>

Al solicitar el permiso RriA,l) (::,:)lr,Ats podremos acceder al registro de llamadas. Aade al final del mtod i:rn,::'+:ii::e de la actividad principal elliguiente cdigo:
gtll.iligii
TextVe,;w
1'Its_LL.'.\}{
;:;..].

i.t"i;.1

- f uu, r'entrant.eir,'rsa1iente", "perdida',}; iTcxtV j.ew j f i.rid\,'j.e.iBi/f c (R. i.cl . sa].idaj ;


!:;

llri 1 1.anua<i.iesi - I.ir i . )1.i: e i " cont ent : / / cal-l _]-ag/ca11 s,' ) ; urgor c = rnanagedQueryllarnaCas, null, null, nu1l, nuLli; while (c.m,)ve'j'()li*xt.O ) {
s:alicla. e:pencl
("

\n'r
(u.

.i. Iiri::eFol:iirit . fc,.j.-i;i (" dd/tlt'ri/yy k :mm


.

.r c.ge::t.::tying {c.Eei:co]-umnIrde.xiceii*: ^DURAl,toNi ) .u u} n .r (: . g+t:$t:.1::.rrc] (c. qei(:lo::.uitlrrf nde::: ({l:1.;.:l -NUMBER\ } .. " , ri + TI P'..LL 4ADA I ini eger . ;].i.-,.ie jn t ( c . geigirino ( c . gelColunnindex iCalIs. )iI);
t

c (Teil,ong ( c . ger.Coirr:nnindex ( Cal I s .

DATE.\'t

"],pEi

,/' ,.*., r,.'.i

1.Eil ;.-,r -.'.i;t';,:.:::.:::'.


'\==r/
C.A lf ,jr,.!-/i<L; ffi?,JiA .iriirr.,j.1L I q /

205
r. f:.--)

Elgran libro de Android


En primer lugar se define un affay de sfnhgs, de forma que ?:r-o...i.,i,Aup;;A
COneSpOnda

"e:itr.1nte.', TIpC.....LL.qttqAt2l CoffespOnda a '.s.11iente,,, etc. Luego se crea el objeto salida que es asignado dl ?exivj-er correspondiente del
Layout.

ilj

interesante, creamos la URl, 11ar::a,Jas asociada a Paa realizar la consulta creamos el c:ur.r:or:, r::. Se trata de la misma clase que hemos utilizado para hacer una consulta en una base de datos, pero ahora la informacin ser suministrada por un ContentProvider por medio del mtodo manaseeuerr-i ) . Este mtodo permite varios parmetros para indicar exactamente los elementos que nos interesan, de forma similar a como se hace en una base de datos. Estos parmetros sern estudiados ms adelante. Al no indicar ninguno se devolvern todas las llamadas registradas.

Ahora comienza

lo

coiil:.eirtr:./'./,::a.l. l._l.,rG,/cal..l..-:.

(c:.:iiovc?otir:xr.-.())

No tenemos ms que desplazarnos por todos los elementos del cursor e ir aadiendo la salida (s;,r1i.d;.r.afrpen,t(i) la informacin

correspondiente a cada registro. En concreto fecha, duracin, nmero de telfono

y tipo de

llamada. Una vez que el cursor se encuentra situado en una fila determinada, podemos obtener la informacin de una columna utilizando los mtOdos ger:$!:ring(), get:rnl: O, get::,ongi) Y gell"1,:raL(i dependiendo del tipo de dato almacenado. Estos mtodos necesitan como parmetros el indice de columna. Para averiguar el ndice de cada columna utilizaremos el mtodo setcolum::rr:ciex( indicando el nombre de la columna. En nuestro caso estos nombres sofl "cate", "d1irai1cn,', "rlumber,, y'.t)ape,,. En el ejemplo, en lugarde utilizar estos nombres se han utilizado cuatro constantes definidas con estos
valores.

Especial mencin tiene la columna ..da{:.e,, que nos devuelve un entero largo que representa un instante concreto de una fecha. Para mostrarla en el formato deseado hemos utilizado el mtodo esttico f orm..t ( i de la clase narer'$:i:inaL.
El resultado de ejecutar este programa se muestra a continuacin:

206

Almacenamiento de datos

Veamos con ms detalle el mtodo ma:ircecouerr...i i l

'Jurso: inan+qedgrle::" (r"j"


Si:^'inq

il

aygsSe

uii, ;aiiiug [] p:^'i,eccion, Sirtng seieccj,on. j-ecc. Srring crden)

Donde los parmetros corresponden a:

iir:.i
prr:y.,.ri:c

URI correspondiente al ContentProvider a consultar.

ion

Lista de columnas que queremos que nos devuelva. Clausula SQL correspondiente a !'iiER. Lista de argumentos utilizados en el parmtro sel.eccic:r. Clausula SQL correspondiente a .rr':DER By.

selec,n
arss,selcr

crden
lnea,
(::i.n'ilcrr'

Para ilustrar el uso de estos parmetros reemplaza en el ejemplo anterior la

,l = rrra!'irr:ier:lQlrerir ( l.l.;trud;i3, nul3., nulL, nul1, nul1)

por,
Si::r':i.n3

[1

grrcryecr;:icn

= oew Si:.r'lng [.]

{
T

AlTS .DATE, Ca]ls . DI,]RATION, alls .NUMBER, aIls .TYPE St t:in(:l [1 ar."islieiec-'c = ne! Sirirrq [] t tt 1tt ! t
i::urf--ri:i"'.' c-.
1

fi!ne !ledQr.113'.,' i

1amacas.

:r'cryeccio*, I't)rye = ?rr,


ar<sSelecc

, ;

"d;lte

DIISC" )

,le.L CoiLc:1t F rori..ier l::Ol.i-riltia$ iue fic'*r i:::Ler:e;al I i /./ <:oi:;'.i.L t.-1. Vliiniil i i pa::metrog .;1e lr r,'or;siri.ta arl.e::j.o:r: trl r,):,:ienado ;ioI' f ec:iia, r:r',:ien ir.:i.rr:eIic' j.e!'il: e i

i r.Jri

De esta forma nos devolvern solo las columnas indicadas n proieccior:.


Esto supone un ahorro de memoria en caso de que existan muchas columnas. En el siguiente parmetro se indica las filas que nos interesan por medio de una consulta de tipo 'it.rulil. En caso de encontrar algn carcter ? este es sustitudo por el primer si:r'I-ng del parmetro argselecc. En caso de haber ms caracteres ? se iran sustituyendo siguiendo el mismos orden. Cuando se sustituyen los

interrogantes cada elemento d ;..rri:$el.er:c es introducido entre comillas. Por lo tanto, en el ejemplo la consulta i'iiERE resultante es *i^HEp.E r./pe = ,:'". Esta
207

Elgran libro de Android


consulta implica que solo se mostrarn las llamadas entrantes. El ltimo parmetro sera equivalente a indicar en SQL "son.r:D riy dat-e irr,:is-:c" es decir el resultado estar ordenado por fecha en orden ascendiente.

7.6.2.2. Escribir informacin en un Contentprovider


Aadir un nuevo elemento en un ContentProvider resulta muy sencillo. Para ilustrar como se hace escribe el siguiente cdigo al principio del ejemplo anterior:
Cio:rLent:Vai.ues vaio:'r+s = nerr Cr)LenLVaiiles O ; va.l"c"':e.-.lut (Cal,lr-{.DATfl , few Dete (i .gelTime (} } ; vaLcres . puL ( Cai ie . ttiili-ltfR, " 55 55 555 55', ) ;
.a.!. or:es . pu l( l-1;:1.1. 1.

;: . DL:F-AT

IOtl,

{55ni .

vaiores.pr-t (cal-i-s.TYPE, Caj-is. ilcoi{INc sypei

tlri.

i1u.evc1gl.,,rfrttni.cr

-. l]ilt.fjosniResolver: ( ) . insel:t

Ca11s . CCI\]TENT'..iJRI

, valores)

Como puedes ver comenzamos creando un objeto corterrvat...res donde vamos almacenado una serie de pares de valores, nombre de columna y valor

asociado a

getconter:tResolver i ) . i:rserr. i) pasndole la URI del ContentProvider y los valores a insertar. Este mtodo nos devuelve una uRl que apunta de forma especfica al elemento que acabamos de insertar. Podras utilizar esta URI para
hacer una consulta y obtener un cursor al nuevo elemento y as poder modificarlo, borrarlo u obtener el _r:D.
primer lugar.

la columna. A

continuacin,

se llama

si ejecutas ahora el programa, la nueva llamada insertada ha de aparecer en

g&

Film,r,9s8

208

Almacenamiento de datos
Estamos modificando el registro de llamadas del sistema, por lo tanto, tambin puedes verificar esta informacin desde las aplicaciones del sistema.

B ffiCg

trer?*

7.6.2.3. Borrar y modificar elementos de un GontentProvider Puedes utilizar


ContentProvider

el

mtodo clelet:e i) para eliminar elementos

de

un

int.

-lont:eni::tsrov!rler'. rlelei::e

iilri uri., iiit:r:inq s*lecc:-icn, Sc.rinq il arqsSelecc)

Este mtodo devuelve

el

nmero

parmetros del mtodo se detallan a continuacin:

de elementos eliminados. Los tres

URI correspondiente al ContentProvider a consultar.

siirL.icrclcn

Clausula SQL correspondiente a

[triiERE.

;.ir:-csfi*:leci: Lista de argumentos utilizados en el parmetro set.,lc:1::i.i::r.


Si quisiramos eliminar un solo elemento podramos obtener su URI e indicarlo en el primer parmetro, dejando los otros dos a nul]. Si por el contrario quieres eliminar varios elementos puedes utilizar el parmetro seieccicri. Por ejemplo, si

quisiramos eliminar todos los registros de llamada del nmero sssssssss,


escribiramos:
ir-jtl-lc)ritr.j:1t.Rr-.:i-cl.vt:r.O.,;irl.et-e(C.l.l..l.s.CONTENT URI ,

number='555555555, u. null)

Tambin puedes utilizar el mtodo urd;r.ie


ContentProvider.

i)

para modificar elementos de un

209

Elgran libro de Android

iat CcntenFrilvider.updat.e{Uri uri, ContentVal-ues valcres, *clrinE selecccn, SLr:!g ! argsSelecci


Por ejemplo, si quisiramos modifr los registros con nmero 555555555, por el nmero 444444444, escribramos:

Contentvai-ues va.lores2 = nerrr Coiltent.Valuee(i ; valc::es2.put (Ca11s .NUMBER, tt444444444tt\ ; getconLent ltesoh'er { ), upciat e ( Cai i s . CONTENT_URf , r.'aiores:, ',number= ' 555555555,", nul1)

7.6.3. Creacin de un ContentProvder


En este apartado vamos a describir los pasos necesarios para crear tu propio ContentProvider. En concreto vamos a seguir tres pasos:

1)
2l
3)

Definir una estructura de almacenamiento para los datos. En nuestro


caso usaremos una base de datos SQLite. Crear nuestra clase extendiendo content provide r.

Decfarar

el

ContetPrcvider en

el nndroidl,rani-fesl.xmr de

nuestra

aplicacin.

Si no deseas compartir tu informacin con otras aplicaciones, no es necesario crear un ContentProvider. En este caso resulta mucho ms sencillo usar una base de datos directamente, taly como se ha explicado en el apartado anterior. En este apartado vamos a seguir el mismo ejemplo descrito en los anteriores apartados del captulo, es decir, vamos a crear un ContentProvider que nos permita compartir la lista de puntuaciones con otras aplicaciones. Posiblemente, no se trate de una situacin muy realista. Es de suponer, que ninguna aplicacin est interesada en esta informacin. No obstante, hemos pensado que va a resultar ms sencillo continuar con el mismo ejemplo.
Crea una nueva aplicacin con los siguientes datos:

ecL r:i?.me : pi.u:t:iriec i cnesPrcvider **i.1.<i llargetr: Aridroid 1.. 5 Appl i ca t io' l:.rme : Punt uac ionesroviCer Fach:rce name : org. example . pirnluacioiresprovi,<ier C:reate Acf'.r:iLy: ActividadPr'ncipal Min SDK Version: 3
Prc''j

7.6.3.1. Definir la estructura de almacenamiento del ContentProvider


El objetivo ltimo de un co:rt.eirbF:::c.icer es almacenar de forma permanente informacin. Por lo tanto, resulta imprescindible utilizar alguno de los mecanismos

210

Almacenamiento de datos descritos en este tema,

estudiado

en el

ContentProvider de forma similar a una base de datos (podemos hacer consultas SQL y nos devuelve un objeto de tipo -:lir'$or). Por lo tanto, la forma ms sencil6 de almacenar los datos de un ConfentProvider es en una base de datos. De esta forma, si nos solicitan una consulta sQL, no tendremos ms que trasladarla a nuestra base de datos, y el objeto C'ursor: que nos devuelva ser el resultado que
nosotros devolveremos.

o en el siguiente, para almacenar datos. como se apartado anterior, podemos realizar consultas a

ha un

Para crear la base de datos de nuestro ContentProvider aade una nueva clase con nombre punt'dacioessor,i-teHetper g introduce el siguiente cdigo:
pr;l:i..i.i:: i::1.*f,irit

PuntuacionessQLiteHelper e"':lsni:k; SeLiteOpenHelper


{

public PuntuacionessQliteHelper(Context conLext) $uper'(context, "puntuaciones,,, rii.i1.J., l-) ;


)
(4r:).J

:3.'r'i.de

rr.rb.l.

i.r: vc'rd oncreate (SQLiteDatabase db)

db.execSQL(CREATE TABLE

puntuaciones (r
KEY AUTOINCREMENT,

+ + + +
]
':iOver:r: :i.de

"_id INTEGER PRIMARY "puntos INTEGER,


"nombre TEXT,
tr

I'fecha

LONG)

")

pub1.:ic'.ro:i.i:i onUpgrade (SeLiteDatabase Fl:i


cra.sa.

db,

i.rit:.

,:ie

oldVersion, ;lt newVersion)

ulia

i':ilov.r vei.-si..ir

1-:ai:,r

r. l:llrt lctur.1.

j.:al-

l.a.s

L.t1i:j.l.a$

l
)

La clase que acabamos de aadir al proyecto se encarga de crear la base de d?tos prntnacii:r:i*s extendiendo la clase i:ict,iLei:)iienr.telper'. Este proceso fue

descrito en el apartado dedicado


apartado.

trabajando sobre el mismo ejemplo, la tabla creada es idntica y lo mismo ocune con el cdigo. Para una explicacin ms detallada recomendamos consultar este

a las bases de datos. Dado que stamos

7.6.3.2. Extendiendo la clase GontentProvider

Ahora abordamos la parte ms laboriosa,


descend
ie

la

creacin

de una

clase

nte

Ci; n

:;

r:n:

F.:'ilrr

i. d i3.-...

211

Elgran libro de Android


Los mtodos principales que tenemos que implementiar son:

. qetr?*pe (; - devuelve el tipo MIME de los elementos del ContentProvider. . '.{ue:'i/ i i - permite realizar consultas al ContentProvider. o i.nse:'b (i - inserta nuevos datos. . ceiere (i - bona elementos del ContentPovider. . upriate t ) - permite modificar los datos existentes.
La clase cc.rnLe:rt:prcyirler toma las precauciones necesarias para evitar problemas con las llamadas simultneas de varios procesos, por lo tanto en la creacin de una subclase no nos tenemos que preocupar de este aspecto.
Aade una nueva clase con nombre pun*'uee.cn(isprcvi.rlclir e introduce el
siguiente cdigo:

public class PuntuacionesProvider extenda ConEentProvider


pubLtc etatlc flnal String
trorg. example . puntuacionesprovider " ;
AUTORIDAD =

public gtatlc final Uri CON?EN"_IJRI = Uri.parse("content://" + AUTORIDAD + "/puntuacioneg'r) i publte Etatlc ffnal LrrE TODOS_LOS_ELEMENTOS = L public etatic finaL l:rt W_ELEMENTO = 2; private Etatic UriMatcher URI_MATCHER = null; etattc { UR I _MAI'CH:ER = nee UriMatcher (UriMatche r . NO_IIAI'CH) ;
URI_MATCHER. addIIRI (AUTORIDAD,
UR

rrpuntuaciones

rr,

TODO S_LO S_ELEMENTO

S) ;

I _MAI,CHER. addURT (AU,I,OR I DAD, .I punLuaciones / #'',

UN_ELEMENI,O) ;

public statLc fLnal String TABLA = "puntuaciones"; private SQliteDatabase baseDeDatos ;


,d,Cver ride public booleaa onCreate O

{
(

PuntuacionesSQliteHelper dbHelper = new PuntuacionessQLiteHelper ( getContext baseDeDatos = dbHelper.getWritableDatabaseO ; return baseDeDatos != null && baseDeDatos.isOpenO;
)

));

212

Almacenamiento de datos

continuacin creamos constantes, AUTLIRIDAD ContentProvider mediante la URI:


ccrrt
en.L :

Como no podra ser de otra forma la clase extiende ,i:ririii.rripr:ovi.cjer:. A I uri, que identifcaran nuestro
i i at i:. ezanpl e . purt nac j-oneeDrovider,/ p-ru:t.uac iones

Las siguientes lneas permiten crear el objeto esttico ri:_Mr'r',ll.trp de la clase I-;:':i"':ei:,:rh*:. Es habitual en Java utilizar variables estticas finales para albergar

permite diferenciar entre diferentes tipos de URI que vamos a manipular. En nuestro caso permitimos dos tipos de URI: acabada en ,/pi_r::Luacicres, que identifica todas las puntuaciones almacenadas y acabada en /pr.ri!:'riaci,:nes::,/#, donde # hay que reemplazarlo por un cdigo numrico que coincida con el campo _iri de nuestra tabla. Cada tipo de URI ha de tener un cdigo numrico asociado, en nuestro caso rt r'lArrx (-1), r,;nr:s r,rrs.....F:r,:t1::;T{is (1) y un-.elrir',:nilio (2).

objetos que no van a cambiar en toda la vida de la aplicacin, es decir que son constantes. Conviene recordar que solo se crear un objeto F-r i,rA,rr-:H,1R aunque se instancie varias veces la clase ?.ir-ituacoricr$t-,rovi.{ler. La clase trri.tvt;.rtc:ier'

La declaracin de variables termina con la constante rABr.,A, que identifica la tabla que gastaremos para almacenar la informacin y el objeto bas,,Denar.rs
donde se almacenar la informacin.

A continuacin est el mtodo oncreat:e {) que es llamado cuando se crea una instancia de esta clase. Bsicamente se crea un iiel,ire!.ietre]. a partir de la claSe descrita en el apartado anteriOr (i?i.ir:tti.:a(:::i(:rnersll)i:,:i t:.eilel.)er') y Se aSigna la base de datos resultante a la variable barreDri.iDeriir-.. Devolvemos rr:rre solo en el caso de que no haya habido problemas en su creacin.
Veamos la implementacin del mtodo errr;r:,e devuelve eltipo MIME que le corresponde:
ri'Crwer:.r: i.dr:

i) que a partir de una URI nos

pubLic String getTl4l(finaI Uri uri)

switch
caae cage

{ {

(URI_I{ArICHER.match(uri) "vnd . android . cursor.

TODOS_L,OS_EI.,EMENTOS :

relurn return default:


)

dir/vnd. org. example . punbuacion,'


_

,.

UN-EI.EM.ENTO: rnd.. andro "

id. cursor . i tem/vnd. o::g

examp

le . pun.tuac on',

throw new IllegalArgumentException("URf incorrecta: " + uri);


)

Alofa; Los tipo MIME fueron creados para identificar un tipo de datos concreto que aadamos camo anexo a un coneo electrnico, aunque en la actualidad son utilizados por muchos sisfemas y protocolos. Un tipo M\ME. tiene dos partes r:pi::_3eni':.;:o,/i:1pi::_+s:erii !r,:o, pof ejemplO irrage./ci f , iniace;,-iire1,
ircxi::/''ht.in;., ...

213

Elgran libro de Android

Existe un convenio para identificar los tipos MIME que proporciona un


ContentProvider. Si se trata de un recurso nico, utilizamos:

vnd. ancroic. cursor , it.em/vnc.

i:il!.lf!lr.r,i-r

y si se trata de una coleccin de recursos utilizamos:


v':r-i . a:r'o

id

. c r

so

r . <ii r r/.r n<i . li i., i:V.t!:.N'.f i.)

Donde !;:i.,r.;:!dii:r{'!'.) ha de ser reemplazado por un identificador que describa el tipo de datos. En nuestro caso hemos elegido org"exi:inrpi.{l .pu:nruecicn. Utilizar prefijos para definir el elemento minimiza el riesgo de confusin con otro ya
existente.

A continuacin hemos de sobrescribir los mtodos qlrery, inserr, cielere

updat.e que permitan consultar, insertar, borrar y actualizar elementos de nuestro ContentProvider. Comencemos por el primer mtodo:
O.li:jv):r'r:.i.(le

publtc Cursor query(Uri uri, String[] projection, St.ring seleccion, Stringll argseleccion, String orden) { SQliteQueryBuilder queryBuilder = r SQliteQueryBuilder()
queryBuilder . setTables ( r) ; swl.tch (URI_MATCHER.match(uri) ) eaae TODOS_LOS_ELEMENTOS :
break,.
{

case

UN_EEMENTO:

break;

String id = uri.getpathsegmentsO .get(1) ; queryBuilder.appendwhere(,'_id = " + id) ;


;

default: throw new lllegalArgumentException(!'URf incorrecta,'+uri)


)

return queryBuilder.query(baseDeDatos, proyeccion, seleccion, argSeleccion, nulI, nulL, orden);


)

si nos pasan una uRl que


continuacin:

El mtodo eru,e,-:y ( ) que hemos de implementar tiene parmetros similares que s,lr,i.tr-.rQ:-rc:ry3u1.cier.sur.iryi). Por lo tanto, no tenemos ms que trasladar la consulta a nuestra base de datos. No obstante existe un pequeo inconveniente,.

identifque un solo elemento, como

la mostrada

cc,ni:erlt : i I arg . ey.;arriple . p.,.nLuac ior:espc' icierr/p*r:tuaci

csnes I .324

214

Almacenamiento de datos Hemos de asegurarnos que solo se devuelve el elemento con j.<l=324. paa conseguirlo se introduce una clusula swiich, gue en caso de tratarse de una URI correspondiente mediante el mtodo aparrclrriherei). La clusula tener ms condiciones si se ha utilizado el parmetro Eete<.:i:ioi.
,*.;ei::'L:i- i-:

de tipo ur_El,nr'{E}irr:, aada a la clusula

wusR-

de la consulta la

condicin

i.HEp.E puede

publlc Uri inEert(Uri uri, Contentvalues valores) { Long ldFila = baseDeDatos.insert (TABLA, null, val-ores) ; if (IdFiIa > 0) { return Contenturis . wi t:hAppendedld (CONTENT_ URr, IdFila) ; ) else { lhrow nerr SQLException("Error aI insert.ar reqist.ro en "+uri)
)
)

pubJ"ic

'iil./f:

i-,'l: i.

fle
{

int del.ete(Uri uri, String seleccion, StringIJ argseleccion) ewitch \IIRI_MATCHER.match(uri) ) {


caae
TODOS_.I,OS_EI.,EI4ENTOS :

break,.

caEe

I.IN_EEMENTO:

String id = ur.getpathSegmentsO .get(1) ; if (TextUtils. jsEmpt_v(seleccion) ) { seleccion=u_id="+id; ) elee { seleccion = . id = " + id + " AND (" + seleccion + ") ";
)

break,.

default,: thrort nw IllegalArgumentException("URI i.ncorrecta: ', + uri);


)

reEurn baseDeDatos. delete (TABL,A, seleccion, argSeleccion)


)

El mtodo i.rs*:r'i::

(l no requiere

explicaciones adicionales.

elemento (..".r'pu:rt':agionesr'3:e ). En el mtodo qLl"ery(), solucionamos este prOblema llamandO SQLite,,iueriliilil.ier . alpe::ci.Jhere ( i . Sin. embargo, ahora no disponemos de un objeto de esta clase, por lo que nos vemos obligados a rcaliz este trabajo a mano. En caso de no haberse indicado nada en seteccior:, este parmetro valdr '._:l = :.t',24,,i y en caso de haberse introducido una condiGin, pof ejemplo "Trr.rinr.i::o * 'b!s,", se1rli::c,'i.on valdf " ci = -:;:t4 Al,;D (nr1!rlE': = '555'l".

El mtodo d*Lr:tc':i), igual como ocurri con el mtodo rJr-rer]::\'O, presenta el inconveniente de que pueden habernos indicado una URI que identifique un solo

215

El gran libro de Android

,dCverri,le

public int update(Uri uri, Contentvalues valores, String seleccion, Stringll ArgumentosSeleccion) { switch (URT_MATCHER.match(uri) ) {
ca6e ?ODOS-LOS_ELEMENTOS : break; case {/AI_.8EMENTO: String id = uri.getpathSegmentsO .get (1) ; lf (TextUtiIs.isEmpty(seleccion) ) {

seleccion=trid=r+id; ) else { seleccion - u id = rr * id + ', AND (" + geleccion + ")";


)

break;

default: throrr new IllegalArgumentException("URI incorrecLa: " + uri); retura baseDeDatos.update (TABLA, valores, seleccion,
ArgmmentosSeleccion)
)
) ; )

Finalizamos con el mtodo Llpdare { ) que es muy simlar d detece ( ).

7.6.3.3. Declarar el ContentProvider en AndroidManifest.xml


Si queremos que nuestro flonrentl?ro.;.de:: sea visible a otras aplicaciones al sstema declarndolo en el A:':droid"ranifesr.zr1. Para conseguirlo no tienes ms que aadir el siguiente cdigo dentro de la etiquetia <applicar j-on>:

resulta imprescindible hacrselo saber


<prcrvicter

ardroid : authori L i es= " or g . exampJ e. pun uaci onesprtrrzi <fer',' an{iroirl: natne= " orq. exampJ. e. puntuacione-sp rovider . .I\tntuacicnesPrcider'

La declaracin de un conrenrprcvider requiere que se especifquen dos


atributos:

nane: nombre cualifcado

de la clase donde hemos implementado nuestro

ContentProvider.

o aurho:-ties:

parte de conespondiente a ta autoridad de las URI que vamos a publicar. Puede indicarse ms de una autoridad.

216

Almacenamiento de datos

Tambin se pueden indicar otros atributos en la etiqueta <;,rovi,:er>. Veamos los ms importantes:

talretr etiqueta describiendo el contentProvider . . . .


::.ccin:

que ser mostrada al


que

usuario. Esta etiqueta como una referencia a un recurso de tipo string.

una referencia

represente nuestro ContentProvider.

a un recurso de tipo drawable con un icono

enabte: indica si est habilitado. El valor por defecto es Lrue.


exuoL-t:ed: indica si puede ser accedido desde otras aplicaciones. Valor por defecto rr''.ie. Si est a f ;.r l -* solo podr ser utilizado por aplicaciones con el mismo lD de usuario que la aplicacin donde se crea.

rear-lr,errrlsronl indica si los clientes podrn consultar la informacin.

o vtrize?erniisicrrt indica si los clientes podrn modificar la informacin. o perrii...,rsion: l nombre de un permiso que el cliente ha de solicitar para
acceder al ContentProvider. Para ms informacin consultar el captulo sobre seguridad.

ml.:lt.iprocessi el valor por defeCto es fa:se, en este Caso se Crear un nico proceso para contener el ContentProvider. Si se indica cr-re, s crear

un

nuevo proceso por cada cliente que quiera hacer uso

del

ContentProvider.

]:ircc:ijj$s: nombre del proceso en el que el ContentProviderha de ejecutarse.

Habitualmente, todos los componentes de una aplicacin se ejecutan en el mismo proceso creado para la aplicacin. Si no se indica lo contrario, el nombre del proceso coincide con el nombre del paquete de la aplicacin (si etiqueta -rr!)1.:ici:rt.:i.crn'.). Si prefieres que un componente de la aplicacin se ejecute en su propio proceso, has de utilizar este atributo.

lo deseas puedes cambiar este nombre con el atributo ircce*rs de

la

. initrder: .
elservidor.

orden en que el ContentProviderha de ser instalado en relacin

a otros ContentProviders del mismo proceso. synd:al:ile: indica

sila informacin del ContentProviderest sincronizada con

1. 2.

Trata de introducir nuevos atributos a la etiqueta Observa los resultados obtenidos.

]:rc-!/i.,-te)r.>.

217

Elgran libro de Android

7.6.4. Acceso a PuntuacionesProvider desde Asteroides


Una vez hemos creado y declarado nuestro ContentProvider vamos a probarlo desde la aplicacin Asteroides. Como hemos hecho en ejemplos anteriores vamos a crear una nueva clase que implemente la interfaz ?.rimacenpuniuaciones.
Almacenp

Crea una nueva clase en la aplicacin AsLeroi<ies con rr:': Luacj i cne *! P rc, v i rf e r. lntfOduce el SigUiente Cd igO :

el

nombre

public class AlmacenPuntuacionesProvider


impl.eurents ALmacenPuntuaciones

private Activity activity; public AlmacenPuntuacionesProvider(Activity activity) thie.activity = activity;


)

public void guardarPuntuacion(1nt puntos, String nombre, long fecha)


{

Uri uri = Uri.Parse(


'

content : / / or g. exampl e . puntuac ionesprovider/puntuac iones " ) ; Contentvalues valores = new Contentvalues O ; valores,put ( "nombre", nombre) ; valores.put ( "puntos", puntos) ; valores.put ("fecha", fecha) i

rry

activity. getcontentResolver
) catch (Exception e)
Toast .makeText
(

insert (uri

valores ) ;

activity, "Verificar que est instalado "+


Toast
.

"org. example . puntuacionesprovider ",

Log. e
)

I'Asteroides" , I'Error: t' + e. tostring O , e) ;

LENGTH_LONG) . show ( ) ;

public Vector<String> listaPuntuaciones (lnt cantidad) Vector<String> result = D.w Vector<String> O ; Uri uri = Uri.parse(
"

cursor cursor = activity.managedQuery(uri, null, null., nuLl, "fecha DESC"); if (cursor t= uIl) { whlle (cursor.moveToNextO ) {
218

content : / / org. example . puntuacionesprovider/punt.uac iones " ) i

Almacenamiento de datos

String nombre = cursor.gtetstring( iat puntos = cursor.getlnt(


)

cursor. getColumnlndex ( "nombret') ) ;


")

cursor. getColumnlndex (,tpuntos result.ad{(puntos + tr " + nombre);

cursor.closeO;

returr resulE;

Para utilizar esta nueva clase, modifica en el prncipio de la clase Arreroj.desl y de la clase puntuacorre,s la declaracin de la variable at.macen por:
A.i.:necerrPrrrt-uaci.ore$ almacen

s oer Al.NracenFrr:rrtu{:.i.onenflrovi.der(thts}

219

You might also like