Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
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
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 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.
177
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,
Ver: b<r'.:
:i
<llres
lnt
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
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
impLementa
i
orrbr-e,
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:
prinar-raci.ones . class )
:!.
:i.
i:..,, (
:i.
j!
El
>r: n i:-u:* c
tayout
:i.
que
utilizaremos para
la
-
nueva actividad
:
<>rle :r . :.:::ii.i.
se
llamar
.:
xri:..]-
?>
<L
11?ar l-'y.clii i:
android : ori ent.ai. ion=', vert i c a l,' andi'oid: layour_\e'idth= ', 111_-are-n
,,
179
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
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:
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
registrada en
180
Almacenamiento de datos
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. ) ;
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
(int
reqi'testcr:ie, int
re:illlCce,
.{
.l.rjt:erti:. d;:ii:-;i )
- data"rJn:i-rt.;:.;.::().qrr-lS-,:i.':i.nr]("::esult.ado");
Intent i-nt.enL = nt{ tntenL O ; int.ent.putExtra ( "resultado", "valor" setResult (RES\ILT_OK, intent) ; finish ( ) ;
,.
181
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
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
u.r]:
r!licjvi
*r;1,vci..rd.
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:
Almacenamiento de datos
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)
- 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.
. _ 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
rry i
.}
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:{
fos.closeii; 1U
Almacenamiento de datos
i catct (Excepi:ion e)
t
1
i
;
Veci::or':<i:lt:r..i..fl.c1> r:e::ir:.1.t:
rry
t Iii.i.e.!in:ui::$!:::r'+::ir
doi
* "";
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
el
permiso
185
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 { )
o
o
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
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
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. ,;.
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 ,:
":
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;:t
t-
i.rii c
i:
i
1
t.;t.::
i,i)i
l,r:
.>
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
medida que
e:iie::
i-:
i. i. t:
I a_:t":ii
eiene::io:
3-!a'd.::i
ef e:ileriirj ; .l:.;nb:i:e
187
'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.:
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
(
i:
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.:
if : nombre
e:rci8ienrent
ncnrbrecuaiif
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
Una vez descritos los principios de trabajo con SAX, pasemos a implementar
la interfaz
Alrriacer!;:i:iriLr.iici.ore. mediante
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
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)
, e) ;
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 ( ),
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
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
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
public vold nuevo(lnt puntos, String nombre, long fecha) Puntuacion puntuacion = neer PuntuacionO; puntuacion.puntos = puntos; puntuacion.nombre = nombre; puntuacion.fecha = fecha;
1
O;
Almacenamiento de datos
for (Puntuacion puntuacion : listaPuntuaciones) { result . add (puntuacion. nombre+ " " +puntuacion. puntos ) ; returrt result;
)
)
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
i adorlo,il ) ;
cargadalista - true;
)
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*:
191
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
(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
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
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:
)
) ) )
Como puedes ver todo el trabajo se realiza por medio de un objeto de la clase
193
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;
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
rry
;
1
esc
ribi
eOutput
F ICHERO,
Context. .1,ODE_PRI\ATE) ) ;
rry
if (lcargadoDocumento)
)
(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
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
Log.e("Ast.eroidesrr, e.getMessage O, e)
)
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.
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
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
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
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
.append(il\n
) 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
);
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.
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.
la
clase
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.
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
nombre
Asteroides.
199
t!. i+
t:
.1
:;
rJ
(|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
)
,//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;
) )
200
Almacenamiento de datos
SQi.,:i.i.+O!:)eril{ei.fie}:(ilorrttexi:: a:orit-exi::(),
i.:1tr.i:ri.5.1 ncirirh:r'*.
.ersici'r.)
. . .
caso
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.
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.
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
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
INCOMING WPE
OUTGOING TYPE MISSED WPE OUTGOING WPE
355 90
542
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.
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:
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
: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.
7.6.2. Acceder a la informacn de un ContentProvder Android utiliza los ContentProvider pata almacenar diferentes tipos
en Android:
de
203
Clase
Browser
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
an Android.
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.
<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
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.
.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
DATE.\'t
"],pEi
205
r. f:.--)
"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
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
il
aygsSe
iir:.i
prr:y.,.ri:c
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'
por,
Si::r':i.n3
[1
grrcryecr;:icn
{
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.
, ;
"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
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
;: . DL:F-AT
IOtl,
{55ni .
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
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?*
el
de
un
int.
-lont:eni::tsrov!rler'. rlelei::e
el
nmero
siirL.icrclcn
[triiERE.
number='555555555, u. null)
i)
209
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)
1)
2l
3)
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
210
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
:3.'r'i.de
rr.rb.l.
db.execSQL(CREATE TABLE
puntuaciones (r
KEY AUTOINCREMENT,
+ + + +
]
':iOver:r: :i.de
I'fecha
LONG)
")
db,
i.rit:.
,:ie
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
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
la
creacin
de una
clase
nte
Ci; n
:;
r:n:
F.:'ilrr
i. d i3.-...
211
. 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 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,
S) ;
UN_ELEMENI,O) ;
{
(
PuntuacionesSQliteHelper dbHelper = new PuntuacionessQLiteHelper ( getContext baseDeDatos = dbHelper.getWritableDatabaseO ; return baseDeDatos != null && baseDeDatos.isOpenO;
)
));
212
Almacenamiento de datos
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:
switch
caae cage
{ {
TODOS_L,OS_EI.,EMENTOS :
,.
examp
le . pun.tuac on',
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
i:il!.lf!lr.r,i-r
id
. c r
so
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.
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;
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,.
la mostrada
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-:
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
{
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,.
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
,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) ) {
break;
default: throrr new IllegalArgumentException("URI incorrecLa: " + uri); retura baseDeDatos.update (TABLA, valores, seleccion,
ArgmmentosSeleccion)
)
) ; )
ardroid : authori L i es= " or g . exampJ e. pun uaci onesprtrrzi <fer',' an{iroirl: natne= " orq. exampJ. e. puntuacione-sp rovider . .I\tntuacicnesPrcider'
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:
una referencia
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
del
ContentProvider.
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.
la
. initrder: .
elservidor.
1. 2.
]:rc-!/i.,-te)r.>.
217
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
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 ) ;
Log. 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
Almacenamiento de datos
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