You are on page 1of 453

INTRODUCCIN

NDICE
1.1

INTRODUCCIN .............................................................. 3
1.1.1 Qu es Android .................................................................3
1.1.2 Proyecto libre (Open Source) ..........................................3
1.1.3 Su historia ..........................................................................3
1.1.4 Inconvenientes de Android..............................................4

1.2

QU ES ECLIPSE ............................................................. 5
1.2.1 El Consorcio Eclipse ........................................................5
1.2.2 Instalacin de Java Developmente Kit (JDK)................6
1.2.3 Instalacin de Eclipse ......................................................7
1.2.4 Instalacin de las libreras de Android...........................9
1.2.5 Aadir versiones y componentes de Android ............19
1.2.6 Definicin del dispositivo virtual de Android ..............25

Introduccin

1.1

INTRODUCCIN

1.1.1 Qu es Android
Android es un sistema operativo, inicialmente diseado para telfonos mviles como
los sistemas operativos iOS (Apple), Symbian (Nokia) y Blackberry OS.
En la actualidad, este sistema operativo se instala no slo en mviles, sino tambin en
mltiples dispositivos, como tabletas, GPS, televisores, discos duros multimedia, mini
ordenadores, etctera. Incluso se ha instalado en microondas y lavadoras.
Est basado en Linux, que es un ncleo de sistema operativo libre, gratuito y
multiplataforma.
Este sistema operativo permite programar aplicaciones empleando una variacin de
Java llamada Dalvik, y proporciona todas las interfaces necesarias para desarrollar fcilmente
aplicaciones que acceden a las funciones del telfono (como el GPS, las llamadas, la agenda,
etctera) utilizando el lenguaje de programacin Java.
Su sencillez principalmente, junto a la existencia de herramientas de programacin
gratuitas, es la causa de que existan cientos de miles de aplicaciones disponibles, que
extienden la funcionalidad de los dispositivos y mejoran la experiencia del usuario.

1.1.2 Proyecto libre (Open Source)


Una de las caractersticas ms importantes de este sistema operativo reside en que es
completamente libre. Es decir, ni para programar en este sistema ni para incluirlo en un
telfono hay que pagar nada. Por esta razn, es muy popular entre los fabricantes de telfonos
y desarrolladores, ya que los costes para lanzar un telfono o una aplicacin son muy bajos.
Cualquier programador puede descargarse el cdigo fuente, inspeccionarlo,
compilarlo e incluso modificarlo.

1.1.3 Su historia
Android era un sistema operativo para mviles prcticamente desconocido hasta que
en 2005 lo compr Google.
En noviembre de 2007 se cre la Open Handset Alliance, que agrup a muchos
fabricantes de telfonos mviles, procesadores y Google. Este ao se lanz la primera versin
de Android, junto con el SDK (del ingls, Software Development Kit, que significa Kit del
desarrollo de software) para que los programadores empezaran a crear sus aplicaciones
para este sistema operativo.

El despegue del sistema operativo fue lento porque se lanz antes el sistema operativo
que el primer terminal mvil, aunque rpidamente se ha colocado como el sistema operativo
de mviles ms vendido del mundo.
En febrero de 2011 se anunci la versin 3.0 de Android, cuyo nombre en clave es
Honeycomb, que est optimizada para tabletas en lugar de para telfonos mviles.

Versiones disponibles:
Las versiones de Android reciben nombre de postres en ingls. En cada versin el
postre elegido empieza por una letra distinta siguiendo un orden alfabtico:

C:
D:
E:
F:
G:
H:
I:
J:

Cupcake (v1.5), magdalena glaseada.


Donut (v1.6), rosquilla.
clair (v2.0/v2.1), pastel francs conocido en Espaa como pepito.
Froyo (v2.2), (abreviatura de frozen yogurt) yogur helado.
Gingerbread (v2.3), pan de jengibre.
Honeycomb (v3.0/v3.1), panal.
IceCream Sandwich (4.0), sandwich de helado.
Jelly Bean (??), gomitas de gelatina)

En el siguiente enlace puedes encontrar una descripcin de la funcionalidad que


incluye cada versin de Android.

1.1.4 Inconvenientes de Android


Android ha sido criticado muchas veces por la fragmentacin que sufren sus
terminales (con versiones distintas de Android), ya que las actualizaciones no se despliegan
automticamente en estos terminales una vez que Google lanza una nueva versin. Cada
fabricante debe crear su propia versin. Sin embargo, esa situacin cambiar ya que los
fabricantes se han comprometido a aplicar actualizaciones al menos durante los 18 meses
siguientes desde que empiezan a vender un terminal en el mercado.
Adems, actualmente Google tiene la intencin de unificar la funcionalidad entre las
versiones del sistema operativo para tabletas y mviles en la versin 4.0.
Disponer del cdigo fuente del sistema operativo no significa que se pueda tener
siempre la ltima versin de Android en un determinado mvil, ya que el cdigo para soportar
el hardware de cada fabricante normalmente no es pblico; as que faltara un trozo bsico del
firmware (controladores) para poder hacerlo funcionar en dicho terminal.
Hay que tener en cuenta que las nuevas versiones de Android suelen requerir ms
recursos, por lo que, en los modelos ms antiguos, no se puede instalar la ltima versin por
insuficiente memoria RAM, velocidad de procesador, etctera.
4

Introduccin

1.2

QU ES ECLIPSE

Eclipse es un entorno de software multi-lenguaje de


programacin que incluye un entorno de desarrollo integrado (IDE).
Inicialmente, se dise pensando principalmente en el lenguaje de
programacin Java y se puede utilizar para desarrollar aplicaciones en
este lenguaje.
En la web oficial de Eclipse (www.eclipse.org), se define como An IDE for everything
and nothing in particular (un IDE para todo y para nada en particular). Eclipse es, en realidad,
un armazn (workbench) sobre el que se pueden instalar herramientas de desarrollo para
cualquier lenguaje, mediante la implementacin de los plugins adecuados. El trmino plugin
procede del ingls to plug, que significa enchufar. Es un software que permite cambiar,
mejorar o agregar funcionalidades.
La arquitectura de plugins de Eclipse permite, adems de integrar diversos lenguajes
sobre un mismo IDE, introducir otras aplicaciones accesorias que pueden resultar tiles
durante el proceso de desarrollo, tales como herramientas UML (modelado de objetos),
editores visuales de interfaces, ayuda en lnea para libreras, etctera.
Usando distintas libreras es posible servirse de este entorno de desarrollo para otros
lenguajes de programacin, como Ada, C, C + +, COBOL, Perl, Delphi, PHP, Python, R. Ruby,
Scala, Clojure y Scheme.
A menudo el IDE Eclipse aade un apellido a su nombre cuando se usa para
programar otro lenguaje. Por ejemplo, se llama Eclipse ADT (Ada Development Toolkit) para
Ada, Eclipse CDT para C / C + +, Eclipse JDT para Java y Eclipse PDT para PHP.
Esta lista de lenguajes aumenta con los aos, ya que este IDE se est convirtiendo en
el entorno de desarrollo de muchos programadores por su simplicidad y facilidad de uso.

1.2.1 El Consorcio Eclipse


En su origen, el Proyecto Eclipse era un proyecto de desarrollo OpenSource,
desarrollado y mantenido en su totalidad por IBM. Bajo la direccin de IBM, se fund el
Consorcio Eclipse, al cual se unieron algunas empresas importantes como Rational, HP o
Borland.
Desde el ao 2004, el Consorcio Eclipse es independiente de IBM y entre otras
empresas, est integrado por HP, QNX, IBM, Intel, SAP, Fujitsu, Hitachi, Novell, Oracle, Palm,
Ericsson y RedHat, adems de por algunas universidades e institutos tecnolgicos.

1.2.2 Instalacin de Java Developmente Kit (JDK)


Es muy importante tener en cuenta que, para poder ejecutar el entorno de desarrollo
Eclipse y las libreras de Android, es necesario tener instaladas en el ordenador las libreras de
desarrollo de Java. Aunque ya est disponible la versin 1.7, para poder compilar
aplicaciones Android, es necesario instalar la versin 6 de Java (tambin conocida como
Java 1.6).
Podemos descargar la versin correcta del JDK de Java en:
http://java.sun.com/javase/downloads

Introduccin

Nota: en el caso de Linux o Mac, es posible tambin instalar Java usando los
programas habituales del sistema operativo que permiten la actualizacin de paquetes.
Nota: si vas a instalar Eclipse y las libreras de Android en Linux, lee las notas que se
encuentran en Preguntas y Respuestas de esta Introduccin en la mesa del curso.

1.2.3 Instalacin de Eclipse


La instalacin es muy sencilla. Simplemente accedemos a la pgina web:
http://www.eclipse.org/downloads/

En esta pgina seleccionamos el tipo de Sistema Operativo donde vamos a instalar


Eclipse y descargamos el archivo "Eclipse Classic 3.7".

Hay que tener en cuenta que debemos descargar la versin 32 bits o 64 bits en funcin
del sistema operativo de que dispongamos.
En el caso de Windows podemos ver el tipo de sistema operativo haciendo clic con el
botn derecho del ratn en el icono "Equipo" o Mi PC del Escritorio y haciendo clic de nuevo
en "Propiedades":

En el caso de Linux, desde la lnea de comandos podemos ejecutar el siguiente


comando para saber si el sistema operativo es de 64bits:

$ uname -m
x86_64

En el caso de Apple Mac, utiliza el Perfil de Sistema para determinar si ests utilizando
un kernel de 64 bits.

1. En el men Apple (
"Ms informacin":

), selecciona Acerca de este Mac y a continuacin, haz clic en

Introduccin

2. En el panel "Contenido", selecciona "Software".


3. Si Extensiones y kernel de 64 bits est configurada como S, ests utilizando un kernel
de 64 bits.

Cuando hayamos descargado el fichero correspondiente, lo copiamos a un directorio


o carpeta del ordenador y descomprimimos este fichero.
Es recomendable usar un directorio sencillo que podamos recordar fcilmente, por
ejemplo C:\cursos_Mentor\eclipse. Adems, es muy importante que los nombres de los
directorios no contengan espacios, pues Eclipse puede mostrar errores y no funcionar
correctamente.
Una vez descomprimido el fichero, Eclipse est listo para ser utilizado; no es necesario
hacer ninguna operacin adicional.
Recomendamos hacer un acceso directo en el Escritorio del ordenador para arrancar
rpidamente el entorno de programacin Eclipse.

1.2.4 Instalacin de las libreras de Android


A continuacin, debemos instalar el Paquete de Desarrollo de iniciacin (en ingls,
SDK Starter Package) de Android. Este paquete no incluye las libreras de desarrollo
completas, sino que nicamente es el ncleo del SDK que se utiliza para descargar el resto de
los componentes SDK, como la ltima plataforma Android.
9

Para descargar el fichero necesario, accedemos a la pgina de descarga del SDK de


Android y nos bajamos la versin que corresponda en funcin del sistema operativo donde
vayamos a instalarlo. Recomendamos que hay que descargar el archivo .zip, ya que con ste
la instalacin es ms sencilla y rpida:

Cuando hayas descargado la versin .zip o .tgz, descomprmelo en el disco duro.


Recomendamos que conviene usar el directorio C:\cursos_Mentor\Android\android-sdkwindows.
Si usas otro directorio, toma nota del mismo, ya que, ms adelante, tendrs que usar
el nombre de este directorio para acabar de configurar el plugin de Android en Eclipse.
Ahora vamos a instalar las libreras necesarias en Eclipse. Estas libreras se denominan
Android Development Tools (ADT). Para ello, arrancamos Eclipse haciendo doble clic sobre
el acceso directo que hemos creado anteriormente. A continuacin, Eclipse pedir que
seleccionemos el "workspace", es decir, el directorio donde queremos guardar los proyectos.

10

Introduccin

Seleccionaremos un directorio sencillo y fcil de recordar.


Importante: Recomendamos usar el directorio C:\cursos_Mentor\Android\proyectos como
carpeta personal

Finalmente hacemos clic en OK para abrir Eclipse:

Ahora vamos a configurar las preferencias de la versin de Java en Eclipse para


compilar los proyectos de Android. Para ello, hacemos clic en la opcin del men Window->
Preferences..., hacemos clic en el panel izquierdo sobre Java->Installed JREs y
seleccionamos jre6 en el campo Installed JREs:

Si no podemos seleccionar "jre6" debemos usar el botn "Add" para aadir las
libreras del JRE 6. Por ejemplo, en Windows, se pueden encontrar en el directorio:
"C:\Program Files\Java\jre6".
11

Para finalizar, en esta ventana hay que seleccionar la versin de Java utilizada para
compilar los proyectos de Android. Para ello hacemos clic en Java->Compiler y elegimos
1.6 en el campo Compiler compliance settings:

Si no hemos seleccionado la versin 6 de JRE en el paso anterior aparecer el


siguiente mensaje de error en Eclipse:

12

Introduccin

Importante: Es necesario disponer de conexin a Internet para poder continuar con los
siguientes pasos y poder descargar las libreras necesarias.

A continuacin, seleccionamos en el men Help -> Install New Software...


En el cuadro de dilogo que aparecer, introducimos la direccin del sitio de descarga
de las libreras ADT:
https://dl-ssl.google.com/android/eclipse/
Y pulsamos la tecla Intro. A continuacin, marcamos todas las opciones tal y como se
muestra en la siguiente captura de pantalla:

Es muy Importante comprobar la versin de Java. Si no, no se instalar bien el software de


Android.

13

Hacemos clic en el botn "Next".


Nota: este proceso puede llevar un rato en funcin de la conexin a Internet y de la
potencia del ordenador que tengamos.
Despus, aparecer la siguiente ventana:

Hacemos clic de nuevo en "Next", seleccionamos "I accept..." en el acuerdo de


licencia y hacemos clic en "Finish":

14

Introduccin

A continuacin, se instalar el software necesario de Android:

Se mostrar este aviso de seguridad y pulsaremos en "OK" para continuar la


instalacin:

Al acabar la instalacin, es necesario reiniciar Eclipse. Para ello haremos clic en


"Restart":

15

Al arrancar de nuevo Eclipse ya dispondremos de las libreras necesarias para


empezar a trabajar con Android:

Podemos hacer clic en la X de la pestaa "Welcome" para acceder al entorno de


desarrollo:

La primera vez que accedemos al entorno Eclipse, aparece la siguiente ventana en la


que debemos indicar dnde hemos instalado el SDK de Android:

16

Introduccin

Hacemos clic en el botn Browse... e indicamos el directorio donde hemos instalado


el SDK de Android. Si has seguido las instrucciones de Windows, el directorio por
recomendado

para

descomprimir

el

archivo

del

SDK

es

C:\cursos_Mentor\Android\android-sdk-windows. Pulsamos el botn OK para finalizar


la configuracin.

A continuacin, aparece otra ventana solicitando nuestra conformidad para enviar


estadsticas de uso del SDK de Android. No es necesario hacerlo si no lo deseamos:
17

Pulsamos el botn Finish para finalizar la configuracin. A continuacin, aparece el


siguiente mensaje indicando que no hemos instalado ninguna versin del sistema operativo
Android:

Pulsamos el botn OK, en el siguiente paso, instalamos la versin de Android sobre


la que vamos a trabajar en este curso.
En el caso de que no aparezca la ventana que permite indicar a Eclipse dnde se
encuentra el SDK de Android, podemos hacerlo manualmente. Para ello, hacemos clic en la
opcin del men Window-> Preferences...
izquierdo:

18

y seleccionamos Android en el panel

Introduccin

Para acabar, pulsamos el botn OK.

1.2.5 Aadir versiones y componentes de Android


El ltimo paso en la configuracin de las libreras de Android es descargar e instalar
los componentes esenciales del SDK para el entorno de desarrollo.
El SDK utiliza una estructura modular que separa las distintas versiones de Android,
complementos, herramientas, ejemplos y la documentacin en un nico paquete que se puede
instalar por separado. Para desarrollar una aplicacin en Android, es necesario descargar, al
menos, una versin. En este curso vamos a usar la versin 2.3, por ser la ms extendida en
el momento de redaccin de la documentacin. No obstante, vamos a emplear sentencias
compatibles y recompilables en otras versiones.
Para aadir esta versin hay que hacer clic en la opcin Android SDK Manager del
men principal Window de Eclipse:

19

Se abrir la ventana siguiente:

Para instalar la versin 2.3.3, seleccionamos los paquetes que se muestran en la


siguiente ventana:

20

Introduccin

Nota: la revisin de las versiones de Android puede ser superior cuando al alumno o
alumna instale el SDK.
Una vez hemos pulsado el botn Install 6 packages, aparece esta ventana y
seleccionamos la opcin Accept All y, despus, hacemos clic en Install:

El instalador tarda un rato (10-20 minutos) en descargar e instalar los paquetes:


21

Para acabar, reiniciamos el ADB (Android Debug Bridge):

La instalacin ha finalizado correctamente:

Ahora vamos a ver la estructura que tiene el SDK de Android. Para ello, abrimos el
explorador en el directorio C:\cursos_Mentor\Android\android-sdk-windows o en el
directorio donde lo hayamos descomprimido. La siguiente tabla describe los subdiretorios que
contiene esta carpeta:

22

Introduccin

NOMBRE
CARPETA

DESCRIPCIN

add-ons/

Contiene los paquetes add-on del SDK de Android que permiten desarrollar
aplicaciones usando libreras externas disponibles para algunos dispositivos
o terminales.

docs/

Documentacin completa en formato HTML, incluyendo la Gua del


desarrollador y la gua de la API. Para leer la documentacin, puedes abrir el
fichero offline.html en un navegador Web.

platformtools/

Contiene las herramientas de desarrollo comunes del SDK que se actualizan


con cada nueva versin de Android, tales como el ADB (Android Debug
Bridge), as como otras herramientas que no se suelen utilizar directamente.

platforms/

Contiene las versiones de Android con las que se puede desarrollar


aplicaciones en Eclipse. Cada versin se encuentra en un directorio
independiente.

Directorio de la plataforma de la versin correspondiente, por ejemplo,


"Android-10". Todos los directorios de la versin de Android contienen un
<platform>/ conjunto similar de archivos y la misma estructura de subdirectorios.
Adems, tambin incluye la librera de Android (android.jar) que se utiliza
para compilar aplicaciones con esta versin de Android.

samples/

Contiene los ejemplos de cdigo para esa versin especfica de Android.

tools/

Contiene el conjunto de herramientas de desarrollo y creacin de perfiles que


son independientes de la versin de Android, como el emulador, el SDK de
Android y AVD Manager, DDMS (Dalvik Debug Monitor Server), etctera.

SDK
Readme.txt

Archivo que explica cmo realizar la configuracin inicial del SDK de Android.

SDK
Manager.exe

Aplicacin que inicia el SDK de Android y la herramienta AVD de gestin de


paquetes. Slo disponible en Windows.

23

Finalmente, vamos a incluir el directorio donde hemos instalado las libreras de


Android en el PATH del sistema operativo. En concreto, vamos a incluir los directorios tools y
platform-tools.
Si has usado el directorio recomendado, los subdirectorios son:
C:\cursos_Mentor\Android\android-sdk-windows\tools
y
C:\cursos_Mentor\Android\android-sdk-windows\platform-tools
En Windows, se puede hacer esto accediendo al Panel de control, haciendo clic en
el icono Sistema, seleccionando la pestaa Opciones avanzadas y haciendo clic en el
botn Variables de entorno. A continuacin, aadiremos los directorios anteriores a la
variable PATH de la siguiente ventana:

En Windows 7 se puede acceder a la ventana anterior abriendo el Panel de control,


haciendo clic en el icono Sistema y Seguridad, despus en "Sistema" y, para acabar, en la
opcin "Configuracin avanzada del sistema" para acceder a la ventana anterior:

24

Introduccin

Escribiendo el comando path en una ventana de comandos de Windows podemos ver


si se hemos modificado bien esta variable global del sistema:

Nota: en el resultado de este comando debemos ver el directorio de instalacin de


Android. Es muy importante que se muestre tal como aparece en la ventana anterior: con
punto y coma al final del directorio SDK y sin espacios entre el punto y coma del
directorio anterior.
En OS X (Mac) y Linux, puedes agregar la ruta a la variable PATH con el comando SET
o estableciendo la variable correspondiente en un script de inicio.

1.2.6 Definicin del dispositivo virtual de Android


Para poder hacer pruebas de las aplicaciones Android que desarrollemos sin
necesidad de disponer de un telfono Android, el SDK incluye la posibilidad de definir un
Dispositivo Virtual de Android (en ingls, AVD, Android Virtual Device). Este dispositivo
emula un terminal con Android instalado.
Para definir el AVD, hacemos clic en la opcin Android AVD Manager del men principal
Window de Eclipse:

25

Aparecer la siguiente ventana:

Hacemos clic en el botn New de la ventana anterior y la completamos como se


muestra en la siguiente ventana:

26

Introduccin

La opcin Snapshot-> Enabled permite guardar el estado del dispositivo de manera


que todos los cambios que hagamos, como cambiar la configuracin de Android o instalar
aplicaciones, queden guardados. As, la prxima vez que accedamos al emulador, se recupera
automticamente el ltimo estado.

Importante: En el curso hemos creado un dispositivo virtual que no guarda el estado porque
puede producir problemas de ejecucin con Eclipse. En todo caso, el alumno o alumna puede
usar la opcin Edit del AVD cuando crea necesario que los ltimos cambios sean
almacenados para la siguiente sesin de trabajo

Para acabar, basta con hacer clic en Create AVD:

27

En esta Introduccin puedes encontrar el vdeo Cmo instalar Eclipse y el plugin


Android, que muestra de manera visual los pasos seguidos en las explicaciones anteriores

28

INTRODUCCIN AL ENTORNO
ANDROID

NDICE
1.1

INTRODUCCIN AL ENTORNO DE ANDROID .................... 31


1.1.1 Introduccin ...................................................................................31
1.1.2 Caractersticas de Android...........................................................31
1.1.3 Arquitectura de Android ...............................................................33
1.1.4 Creacin de un proyecto por lneas de comando ....................35

1.2

CONCEPTOS DE LAS APLICACIONES ANDROID ............... 37


1.2.1 Caractersticas de las aplicaciones Android.......................... 37
1.2.2 Componentes de las aplicaciones......................................... 37

1.3

CMO CREAR UN PROYECTO ANDROID ........................... 40


1.3.1 Un vistazo general al IDE de Eclipse ..................................... 40
1.3.1.1 Editores .................................................................................41
1.3.1.2 Vistas ................................................................................ ..41
1.3.1.3 Barras de Herramientas principal y secundarias ............43
1.3.1.4 Perspectivas .........................................................................43
1.3.2 Cmo crear un proyecto Android ...............................................46
1.3.2.1 Creacin de un nuevo proyecto ........................................47
1.3.2.2 Nombre de la actividad .......................................................49
1.3.2.3 Descripcin de los ficheros por defecto del proyecto....49
1.3.2.4 Ejecucin del proyecto Android ........................................53
1.3.2.5 Cmo usar el emulador de Android (AVD) .......................56

1.4

CMO CREAR LA PRIMERA APLICACIN CON ANDROID63


1.4.1 Cambio en la Interfaz de usuario con Layout .........................66

1.5

DISEO DE LA INTERFAZ DE USUARIO .............................. 70


1.5.1 Cmo disear la interfaz de usuario mediante Vistas ..............70
1.5.2 Vistas disponibles de Android .....................................................71

Introduccin al entorno Android

1.1

INTRODUCCIN AL ENTORNO DE ANDROID

1.1.1 Introduccin
En esta Unidad vamos a explicar las caractersticas y la arquitectura de Android.
Adems, describiremos el entorno de desarrollo Eclipse y crearemos nuestro primer
proyecto Android.
Tambin, detallaremos los ficheros bsicos que componen un proyecto Android.
Finalmente, usaremos Paneles de diseo (Layout) y Componentes (View) para
disear la interfaz de usuario en ejemplos de aplicaciones de Android.

1.1.2 Caractersticas de Android


A continuacin se muetra un resumen de las caractersticas ms importantes:

Diseado para
dispositivo
pequeos

El sistema operativo es compatible con pantallas VGA (y mayores),


grficos 2D y grficos 3D presentes en muchos telfonos tradicionales.

Almacenamiento

Dispone de la base de datos ligera SQLite donde se almacenan los


datos de las aplicaciones.

Conectividad

Android soporta las siguientes tecnologas de conectividad: GSM/EDGE,


IDEN, CDMA, EV-DO, UMTS, Bluetooth, Wi-Fi, LTE y WiMAX. Algunas
son muy populares en los telfonos actuales y otras se estn
desarrollando.

Mensajera

Se pueden usar tanto SMS como MMS.

Navegador web

El navegador web incluido en Android est basado en el motor del


navegador de cdigo abierto WebKit. Este navegador es muy eficiente y
permite cargar las pginas Web rpidamente.

Soporte de Java

Aunque las aplicaciones se escriben en el lenguaje Java, no hay una


Mquina Virtual de Java en el sistema operativo para ejecutar el cdigo.
Este cdigo Java se compila en un ejecutable Dalvik y se ejecuta en la
Mquina Virtual Dalvik. Dalvik es una mquina virtual especializada,
diseada especficamente para Android y optimizada para dispositivos
mviles que funcionan con batera y que tienen memoria y procesador
limitados. Es posible incluir las libreras J2ME nativas de Java mediante
aplicaciones de terceros, como J2ME MIDP Runner.
31

Android soporta los siguientes formatos multimedia: WebM, H.263,


H.264 (en 3GP o MP4), MPEG-4 SP, AMR, AMR-WB (en un contenedor
Soporte multimedia
3GP), AAC, HE-AAC (en contenedores MP4 o 3GP), MP3, MIDI, Ogg
Vorbis, WAV, JPEG, PNG, GIF y BMP.

Soporte para
streaming
(distribucin en
Internet)

Android soporta los siguientes formatos de streaming: RTP/RTSP,


descarga progresiva de HTML (tag <video> de HTML5). Adobe Flash
Streaming (RTMP) es soportado mediante la instalacin de Adobe Flash
Player, pero slo para algunos terminales.

Android puede manejar cmaras de fotos, de vdeo, pantallas tctiles,


Soporte para
GPS, acelermetros, giroscopios, magnetmetros, sensores de
hardware adicional
proximidad y de presin, termmetro, aceleracin 2D y 3D.

Entorno de
desarrollo

El entorno de desarrollo es Eclipse 3.7 y el plugin de Herramientas de


Desarrollo de Android (ADT) que incluye un emulador de dispositivos,
herramientas de depuracin y anlisis de rendimiento.

Market
(Mercado de
aplicaciones)

El Android Market es un catlogo de aplicaciones gratuitas y de pago


que pueden ser descargadas e instaladas desde los propios dispositivos
Android.

Multi-tctil

Android tiene soporte nativo para pantallas multi-tctiles que permiten


manejar la pantalla tctil con ms de 1 dedo.

Bluetooth

En la versin 2.2 de Android se incluye la funcionalidad completa.

Videollamada

Android incluye la posibilidad de videollamada a travs de Google Talk.

Multitarea

Existe la multitarea real de aplicaciones, es decir, las aplicaciones que


no se estn ejecutando en primer plano reciben ciclos de reloj del
procesador para actualizar su estado.

Caractersticas
basadas en voz

Es posible dar rdenes de voz al terminal. Por ejemplo, la bsqueda en


Google a travs de la voz ya estaba disponible desde la primera versin.

Android incluye la comparticin de la conexin a Internet (en ingls,


Tethering
tethering), que permite usar el telfono como un punto de acceso
(comparticin de
inalmbrico, de manera que un ordenador puede usar la conexin 3G
conexin a Internet)
del mvil Android.

32

Introduccin al entorno Android

1.1.3 Arquitectura de Android


Los componentes principales de la arquitectura del sistema operativo Android son los
siguientes:
Aplicaciones: todas las aplicaciones estn escritas en lenguaje de programacin
Java. Las aplicaciones incluidas por defecto son un cliente de correo electrnico,
programa de SMS, calendario, mapas, navegador, contactos, etctera.
Todas las aplicaciones de Android usan el siguiente conjunto de servicios y sistemas:
o Un conjunto de componentes (Views) que se usan para crear las interfaces de
usuario. Por ejemplo, botones, listas, tablas, cajas de texto, etcetera.
o Proveedores de contenidos (Content Providers) que permiten a las
aplicaciones acceder a la informacin de otras aplicaciones (por ejemplo, los
Contactos del telfono) o compartir datos entre ellas.
o Gestor de recursos (Resource Manager), que permite acceder a recursos que
no sean del cdigo fuente, tales como textos de internacionalizacin, imgenes
y ficheros de estilos (layout).
o Gestor de notificaciones (Notification Manager), que permite

a todas las

aplicaciones mostrar alertas en la barra de estado de Android.


o Gestor de actividades (Activity Manager), que controla el ciclo de vida de la
aplicacin.
Marco de desarrollo de aplicaciones: los programadores tienen acceso completo a
las mismas APIs (libreras) del Framework (marco de desarrollo) utilizadas por las
aplicaciones base. La arquitectura est diseada para simplificar la reutilizacin de
componentes, es decir, cualquier aplicacin puede publicar sus capacidades y
cualquier otra aplicacin puede hacer uso de estas capacidades.
Libreras: Android incluye tambin un conjunto de libreras de C/C++ usadas por
varios componentes del sistema. Entre ellas, se encuentran: System C library
(implementacin de la librera C estndar), libreras de medios, bibliotecas de grficos,
3D y SQLite, entre otras. El programador puede hacer uso de estas libreras.
Runtime (ejecutable) de Android: Android tambin incluye un conjunto de libreras
base que proporcionan la mayor parte de las funciones del lenguaje Java. Cada
aplicacin Android ejecuta un proceso con instancia individual de la mquina virtual
Dalvik.
Ncleo Linux: Android est basado en Linux para los servicios base del sistema,
como seguridad, gestin de memoria, procesos y controladores.

33

DIAGRAMA DE LA ARQUITECTURA ANDROID

La utilidad de lnea de comandos genera automticamente todos los archivos


necesarios para crear un proyecto Android; incluso permite crear un proyecto para Eclipse.

Android usa Java como lenguaje base para el desarrollo de las aplicaciones. Por lo tanto,
hace uso de los Paquetes Java (Package en ingls). Estos paquetes son contenedores de
clases que permiten agrupar las distintas partes de un programa cuya funcionalidad tienen
elementos comunes.
El uso de paquetes proporciona las siguientes ventajas:

34

Agrupamiento de clases con caractersticas comunes

Reutilizacin de cdigo

Mayor seguridad al existir niveles de acceso

Introduccin al entorno Android

1.1.4 Creacin de un proyecto por lneas de comando


Usando la lnea de comandos vamos a crear un proyecto Android. Es importante usar
el directorio de trabajo que hemos creado anteriormente con Eclipse:
C:\cursos_Mentor\Android\proyectos.
Desde este directorio, debemos ejecutar el siguiente comando:

android create project --package es.mentor.eje1.unidad1.bienvenido --activity


Bienvenido --target android-10 --path unidad1.eje1.bienvenido

para crear los ficheros bsicos de un proyecto Android. Fjate en qu la orden anterior es una
nica lnea que debes ejecutar en la lnea de comandos de tu sistema operativo.

C:\>cd C:\cursos_Mentor\Android\proyectos
C:\cursos_Mentor\Android\proyectos> android create project --package
es.mentor.unidad1.eje1.bienvenido --activity Bienvenido --target android-10
bienvenido

--path

Created project directory: C:\cursos_Mentor\Android\proyectos\bienvenido


Created directory
C:\cursos_Mentor\Android\proyectos\bienvenido\src\es\mentor\eje1\unidad1\bienvenido
Added file
C:\cursos_Mentor\Android\proyectos\bienvenido\src\es\mentor\eje1\unidad1\bienvenido\Bienv
enido.java
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\res
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\bin
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\libs
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\res\values
Added file C:\cursos_Mentor\Android\proyectos\bienvenido\res\values\strings.xml
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\res\layout
Added file C:\cursos_Mentor\Android\proyectos\bienvenido\res\layout\main.xml
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\res\drawable-hdpi
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\res\drawable-mdpi
Created directory C:\cursos_Mentor\Android\proyectos\bienvenido\res\drawable-ldpi
Added file C:\cursos_Mentor\Android\proyectos\bienvenido\AndroidManifest.xml
Added file C:\cursos_Mentor\Android\proyectos\bienvenido\build.xml
Added file C:\cursos_Mentor\Android\proyectos\bienvenido\proguard.cfg
C:\cursos_Mentor\Android\proyectos>

35

Ahora no vamos a examinar en detalle los ficheros que hemos creado y que conjuntamente
forman el proyecto Android. En el siguiente apartado de esta Unidad los detallaremos con un
ejemplo real en Eclipse.

Este script genera los siguientes directorios y archivos principales:


/src: en este directorio es donde se almacenan los archivos de cdigo fuente Java
(con extensin .java).
/assets: en este directorio se guardan los recursos que utiliza la aplicacin.
/res: es el directorio principal de recursos (resources). Aqu guardaremos imgenes o
archivos multimedia que utilice nuestra aplicacin.
/res/drawable-Xdpi: son los directorios de recursos grficos o imgenes que utilizar
nuestra aplicacin con los nombres drawable-hdpi, drawable-mdpi y drawable-ldpi;
en ellos se almacenan las imgenes dependiendo de la densidad de puntos por
pulgada que tenga el dispositivo en el que se ejecute la aplicacin.
/res/layout: en Android hay que separar el cdigo Java de la aplicacin y la interfaz
grfica. En este directorio es donde colocaremos los archivos xml que definen las
vistas que utilizar la aplicacin.
/res/values: de igual forma que separamos el cdigo Java y la interfaz grfica,
Android separa tambin las cadenas constantes de texto (Internacionalizacin de la
aplicacin), las matrices, la paleta de colores, etctera.
AndroidManifest.xml: es el archivo de configuracin de la aplicacin en el que se
define lo que puede hacer nuestra aplicacin, es decir, en l informamos al sistema
operativo de las capacidades que tiene esta aplicacin. En este archivo tambin
indicaremos las actividades o servicios que ejecutar nuestra aplicacin y los permisos
de seguridad especiales necesarios si va a acceder a recursos compartidos del
sistema, como por ejemplo el acceso al listado de contactos, empleo del GPS o la
posibilidad de enviar mensajes SMS.
default.properties: fichero de proyecto para Eclipse.
Nota: una vez analizado los ficheros bsicos del proyecto, podramos importarlo en
Eclipse. No obstante, por simplificacin, no lo vamos a hacer, pues es ms sencillo
crear un proyecto nuevo directamente desde Eclipse.

As pues, borramos el directorio C:\cursos_Mentor\Android\proyectos\bienvenido,


para crear este mismo proyecto desde Eclipse directamente.

36

Introduccin al entorno Android

1.2

CONCEPTOS DE LAS APLICACIONES ANDROID

1.2.1 Caractersticas de las aplicaciones Android


Las Aplicaciones Android se forman con uno o ms de los siguientes componentes:
actividades, servicios, proveedores de contenidos y receptores de mensajes. Cada
componente tiene una funcionalidad diferente en la aplicacin; incluso la aplicacin puede
activar cada uno de los componentes de forma individual; es ms, otras aplicaciones tambin
los pueden activar.
El archivo de manifestacin (manifest) indica todos los componentes que usa la
aplicacin; en l tambin deben declararse todos los requisitos necesarios de la misma como,
por ejemplo, la versin mnima de Android, las configuracin mnima de hardware, etctera.
El cdigo que no es de la aplicacin, como las imgenes, cadenas de
internacionalizacin, diseo de la interfaz de usuario, otros recursos, etctera, puede incluir
distintas configuraciones en funcin del idioma del telfono o diseos de la interfaz del usuario
en funcin de los diferentes tamaos de pantalla.
Las aplicaciones de Android estn escritas en el lenguaje de programacin Java. Las
herramientas de SDK de Android compilan este cdigo, junto con sus datos y los archivos de
recursos, en un paquete Android. Este archivo tiene la extensin .apk y lo utiliza Android para
instalar la aplicacin.
Una vez instalada una aplicacin en un dispositivo, Android la aloja en su propia caja
de arena (sandbox). Sandbox, palabra del ingls que significa caja de arena (Sand+box), es
un sistema informtico que asla los procesos; de esta manera se pueden ejecutar
aplicaciones de forma independiente. Se utiliza para evitar la corrupcin de datos del sistema
donde stos se ejecutan.
El sistema Android aplica el principio de privilegios mnimos (recuerda que Android
se basa en el kernel de Linux). Es decir, cada aplicacin, por defecto, slo tiene acceso a los
componentes que necesita para hacer su trabajo y nada ms. Esto crea un entorno muy
seguro en el que una aplicacin no puede acceder a las partes de un sistema para las que no
tiene permiso.

1.2.2 Componentes de las aplicaciones


Los Componentes de las aplicaciones son los elementos esenciales de una aplicacin
Android. Cada componente es un punto diferente de entrada por el que el sistema operativo
puede interaccionar con la aplicacin. No todos los componentes son verdaderos puntos de
entrada, sino que algunos dependen unos de otros, aunque cada uno exista como una entidad
separada en Android y desempee un papel especfico que define el comportamiento general
de la aplicacin.
37

Existen los siguientes tipos de componentes de la aplicacin:

Actividades (Activities): una actividad representa una pantalla nica con una interfaz
de usuario. Por ejemplo, una aplicacin de correo electrnico puede tener una
actividad que muestra una lista de correo electrnico nuevo, otra actividad que
compone un correo y otra actividad que lee los mensajes. Aunque las actividades
trabajan conjuntamente para dar la sensacin de una nica aplicacin, cada una de
ellas es independiente de las otras. Por lo tanto, otra aplicacin externa diferente
podra iniciar cualquiera de estas actividades (si la aplicacin de correo electrnico lo
permite). Por ejemplo, una aplicacin que gestiona los contactos podra iniciar la
actividad que compone nuevos mensajes de correo indicando como destinatario del
mensaje al contacto seleccionado en la primera aplicacin.
Una actividad se implementa a partir de la clase Java Activity. Ms adelante veremos
cmo se usa.
Puedes pensar en una actividad de Android como si fuera una ventana en una
aplicacin de escritorio o una pgina HTML en una aplicacin Web. Android est
diseado para cargar muchas actividades pequeas, por lo que se permite al usuario
abrir nuevas actividades y pulsar el botn Atrs para ir a un estado anterior, al igual
que se hace en un navegador web.
Servicios (Services): un servicio es un componente que se ejecuta en segundo plano
y que realiza operaciones cada cierto tiempo. Un servicio no proporciona una interfaz
grfica al usuario. Por ejemplo, un servicio puede reproducir msica en segundo plano
mientras el usuario est en otra aplicacin, o puede obtener informacin de Internet sin
la interaccin del usuario. Otros componentes, como una actividad, pueden iniciar un
servicio e interactuar con l si es necesario.
Un servicio se implementa a partir de la clase Java Service. Ms adelante veremos
cmo se usa.
Proveedores de contenidos (Content providers): un proveedor de contenidos
maneja el conjunto de datos compartido de la aplicacin. Puede almacenar
informacin en el sistema de archivos, en una base de datos SQLite, en Internet o en
cualquier otro lugar de almacenamiento permanente al que la aplicacin tenga acceso.
A travs del proveedor de contenidos, otras aplicaciones pueden consultar e incluso
modificar los datos (si el proveedor de contenidos lo permite). Por ejemplo, Android
proporciona un proveedor de contenidos que gestiona la informacin de los contactos
del telfono. Por lo tanto, cualquier aplicacin, con los permisos adecuados, puede
hacer

una

consulta

al

proveedor

de

contenido

de

los

contactos

(ContactsContract.Data) para leer y escribir informacin sobre una persona en


particular.
38

Introduccin al entorno Android

Los proveedores de contenidos se utilizan tambin para escribir y leer datos que son
privados de la aplicacin y no se comparten. Por ejemplo, una aplicacin de Notas
puede utilizar un proveedor de contenidos para guardar las notas.
Un proveedor de contenidos se implementa a partir de la clase ContentProvider y
debe implementar un conjunto estndar de mtodos (API) que permiten a otras
aplicaciones interaccionar con l. Ms adelante veremos cmo se usa.
Receptores de mensajes (Broadcast receivers): un receptor de mensajes responde
a mensajes difundidos (broadcast) a todos los elementos del sistema. Por ejemplo, un
mensaje puede anunciar que la pantalla se ha apagado, la batera est descargada o
que se ha capturado una foto. Las aplicaciones tambin pueden emitir este tipo de
mensajes para, por ejemplo, indicar a otras aplicaciones que ciertos datos ya han sido
descargados en el dispositivo y estn disponibles para ser utilizados. Aunque estos
receptores de mensajes no muestran informacin en la interfaz del usuario, s que
pueden crear una notificacin en la barra de estado (la barra que aparece arriba en
Android) para alertar al usuario cuando se produce este tipo de mensajes.
Un receptor de mensajes se implementa a partir de la clase BroadcastReceiver y
cada mensaje emitido es un objeto del tipo Intent (Intencin). Ms adelante veremos
cmo se usa.
Componentes de la pantalla de inicio (Widgets): estos componentes visuales se
usan principalmente en la Pantalla de inicio (HomeScreen) de Android para mostrar
informacin que se actualiza peridicamente como, por ejemplo, un reloj, la previsin
del tiempo, etctera. Al tratarse de programacin avanzada no los estudiaremos en
este curso de iniciacin a Android.
Otros componentes de Android son las Carpetas animadas (Live Folders) y los
Fondos de pantalla animados (Live Wallpapers) en la Pantalla de Inicio. Las
carpetas animadas permiten a Android mostrar informacin en la pantalla inicial sin
necesidad de lanzar la aplicacin correspondiente. Igualmente, al tratarse de
programacin avanzada, no los estudiaremos en este curso de iniciacin a Android.

Un aspecto nico del diseo del sistema Android es que cualquier aplicacin puede iniciar un
componente de otra aplicacin. Por ejemplo, si es necesario para la aplicacin abierta
capturar una imagen con la cmara de fotos, seguramente ya exista otra aplicacin que hace
eso y que la aplicacin inicial puede reutilizar en lugar de desarrollar el cdigo necesario para
capturar la foto. nicamente hay que iniciar la actividad de la aplicacin de la cmara de fotos
y capturar la imagen. La sensacin del usuario es como si la cmara formara parte de la
aplicacin inicial.
Cuando el sistema arranca un componente, ste inicia un proceso (si no est ya en
ejecucin) para que la aplicacin cree las instancias de las clases necesarias del componente.
39

Por ejemplo, si una aplicacin inicia la actividad de la aplicacin de la cmara que hace fotos,
la actividad se ejecuta en el proceso que pertenece a la aplicacin de la cmara, no en el
proceso de la aplicacin original que ha hecho la llamada a la otra aplicacin. Por lo tanto, a
diferencia de otros sistemas operativos, las aplicaciones de Android no tienen un punto de
entrada nico (no hay funcin main()).
Debido a que el sistema ejecuta cada aplicacin en un proceso independiente con
permisos restringidos, sta no puede activar directamente un componente de otra aplicacin,
sino que es el sistema operativo Android el encargado de hacerlo. Por lo tanto, para activar un
componente de otra aplicacin es necesario enviar un mensaje al sistema que especifica su
intencin (clase Intent) de iniciar un componente en particular. Por lo tanto, el sistema
operativo es el encargado de activar el componente solicitado.

1.3

CMO CREAR UN PROYECTO ANDROID

1.3.1 Un vistazo general al IDE de Eclipse


Antes de crear el primer proyecto de Android, vamos a echar un primer vistazo al
entorno de desarrollo de Eclipse para conocer sus caractersticas bsicas, la forma en que
organiza el proyecto y las herramientas adicionales que ofrece.
La primera vez que se ejecuta Eclipse se puede ver una pantalla muy similar a la que
se muestra a continuacin.

40

Introduccin al entorno Android

1.3.1.1

Editores

La ventana principal (la ms grande) se llama Editor. El Editor es el espacio donde se


escribe el cdigo fuente de los programas que estamos desarrollando.
Es posible tener varios ficheros de cdigo fuente abiertos a la vez, apilados uno encima
de otro. En la parte superior de la ventana del Editor se muestran las pestaas que permiten
acceder a cada uno de los ficheros abiertos (o bien cerrarlos directamente).

Editor

1.3.1.2

Vistas

Adems del Editor, existe un segundo tipo de ventanas secundarias, que se llaman
Vistas.
Las Vistas son ventanas auxiliares para mostrar informacin, introducir datos, etctera.
Las Vistas se usan con mltiples propsitos, desde navegar por un rbol de directorios, hasta
mostrar el contenido de una consulta SQL.

41

Vistas

En funcin de las libreras de desarrollo (Android, GWT, Java, Delphi...) se definen


Editores propios y todas las Vistas necesarias.
En la ventana anterior estn abiertas dos Vistas:

La Vista vertical de la izquierda muestra el rbol de directorios de los proyectos


con los ficheros del mismo.

La Vista horizontal inferior muestra una pequea agenda de tareas pendientes


que pueden ser introducidas por el usuario, de forma directa, o por Eclipse, en
funcin de determinados eventos (compilacin del proyecto, depuracin de
cdigo, etctera).

Si deseamos cambiar las Vistas, se puede usar la opcin Show View en el men de la
pestaa Window.

42

Introduccin al entorno Android

1.3.1.3

Barras de Herramientas principal y secundarias

La barra de herramientas principal contiene los accesos directos a las operaciones ms


comunes, como abrir y guardar archivos. Adems, tambin es posible ejecutar herramientas
externas y tareas relacionadas con el Editor activo, como ejecutar un programa, depurar el
cdigo fuente, etctera.

Adems de la barra de herramientas principal (imagen anterior), cada Vista puede tener
su propia barra de herramientas secundaria.

1.3.1.4

Perspectivas

Una Perspectiva es un conjunto de ventanas (Editores y Vistas) agrupadas que


simplifican el desarrollo de un proyecto.
Al seleccionar una Perspectiva se carga una configuracin guardada de las Vistas y
Editores de nuestro entorno de desarrollo Eclipse.
Por ejemplo, existe una Perspectiva "Java Browsing" que facilita el desarrollo de
aplicaciones Java y que incluye, adems del Editor, Vistas para navegar por las clases, los
paquetes, etctera.
Se puede cambiar la perspectiva activa utilizando la opcin Open Perspective del
men de Windows. Desde este mismo men tambin es posible definir Perspectivas
personalizadas.

43

Tambin existe un botn en la barra de herramientas principal para cambiar de


Perspectiva:

Si el alumno tiene dudas sobre el uso avanzado de Eclipse, en Internet existen muchos
tutoriales que indican cmo utilizarlo.
Adems, es posible usar el men "Help" o la tecla [F1] para solicitar ayuda.
Desgraciadamente, a da de hoy, esta ayuda slo se encuentra en ingls.

Importante: para importar en Eclipse el cdigo fuente de los ejemplos del curso hay que
usar la opcin del men principal: File -> Import.

44

Introduccin al entorno Android

Despus, hay que marcar Existing Proyects into Workspace en la ventana emergente y
pulsar en el botn Next:

Finalmente, seleccionamos el directorio de trabajo donde debemos haber copiado


previamente

los

ficheros

con

el

cdigo

fuente

de

los

ejemplos:

C:\cursos_Mentor\Android\proyectos y hacemos clic en Finish:

45

Nota: en esta Unidad 1 puedes encontrar el vdeo Cmo cargar los ejemplos en Eclipse,
que muestra cmo se importan los ficheros con el cdigo fuente de los proyectos que son los
ejemplos del curso.

Importante: en el apartado Problemas al cargar proyectos de Android de Preguntas y


Respuestas (FAQ) de la Unidad 1 puedes encontrar soluciones a los problemas que ocurren
al importar el cdigo fuente de los ejemplos del curso.

1.3.2 Cmo crear un proyecto Android


A continuacin, vamos a describir cmo crear un proyecto usando Eclipse y las libreras de
Android que hemos instalado con anterioridad.
Se trata del primer proyecto que el alumno va a crear, por lo que es muy importante prestar
atencin a los pasos seguidos, ya que los proyectos siguientes se generan de manera similar.

As pues, arrancamos Eclipse.

46

Introduccin al entorno Android

1.3.2.1

Creacin de un nuevo proyecto

En el men de Eclipse hacemos clic en File->New->Project:

Tambin podemos hacer clic en el botn del men de herramientas de Eclipse


haciendo clic en la opcin Open a wizard to help create a new Android project:

A continuacin, aparece una nueva ventana en la que escribimos el nombre de proyecto


"unidad1.eje1.bienvenido", "es.mentor.unidad1.eje1.bienvenido" para el paquete de java
(Package) y Bienvenido para Activity.
El resto de opciones las dejamos como aparecen en la siguiente captura de pantalla:

47


Pulsamos el botn Finish para crear los ficheros del proyecto.
A continuacin, describimos los apartados que genera un proyecto Android:

Project Name: como su nombre indica es el nombre del proyecto Eclipse;


corresponde con el nombre del directorio que contiene los ficheros del
proyecto.

Build Target: indica la versin del Android SDK que vamos a usar para compilar
la aplicacin. Por ejemplo, si seleccionas Android 2.3, la aplicacin se compilar
para funcionar en esta versin de Android y las siguientes. La versin
seleccionada aqu no tiene que coincidir con la versin del Emulador de Android
(AVD), ya que las aplicaciones de Android estn diseadas de manera que se

48

Introduccin al entorno Android

ejecutan en la plataforma en la que se desarrollaron y en todas las versiones


superiores de Android. Por ejemplo, una aplicacin que de desarroll para la
versin 2.1 se ejecutar bien en la versin 2.3.3. Al contrario no funciona.

Application Name: es el nombre de la aplicacin que aparece en el icono del


Escritorio de Android. Es el texto que ve el usuario del telfono.

Package Name: es el nombre del paquete Java en el que se almacena todo el


cdigo fuente de la aplicacin.

Create Activity: define el nombre de la Actividad.

Importante: El nombre del paquete debe ser nico en relacin con todos los paquetes
instalados en Android. Por esta razn, es importante utilizar el estndar de dominio para
nombrar los paquetes de las aplicaciones. En el ejemplo anterior se utiliza el nombre de
paquete "es.mentor". A la hora de desarrollar tus propias aplicaciones y distribuirlas en el
Android Market (Mercado de aplicaciones Android) debes utilizar nombres propios de
paquetes.
En ningn caso debes utilizar el nombre es.mentor para distribuir aplicaciones en el Android
Market, pues slo es vlido para los ejemplos del curso.

1.3.2.2

Nombre de la actividad

Importante: El nombre de la actividad no puede incluir tildes, la letra , ni caracteres raros.

1.3.2.3

Descripcin de los ficheros por defecto del proyecto

Para ver los ficheros del proyecto Android creados por Eclipse, en la barra lateral
Package Explorer, desplegamos las entradas haciendo clic en los ficheros marcados con
flechas rojas de los diferentes paquetes:

49

A continuacin, vamos a explicar la estructura y contenido de los ficheros del proyecto.


Carpeta/src/
Contiene todo el cdigo fuente de la aplicacin, cdigo de la interfaz grfica, clases
auxiliares, etctera. Inicialmente, Eclipse crea el cdigo bsico de la Actividad (Activity)
principal de la aplicacin, debajo del paquete Java definido.
Carpeta /res/
Contiene todos los ficheros de recursos necesarios para el proyecto: imgenes, vdeos,
cadenas de texto (para internacionalizacin de la aplicacin), etctera. Los diferentes tipos de
recursos se deben distribuir entre las siguientes subcarpetas:

/res/drawable-X/: contiene las imgenes de la aplicacin. Se divide en /drawableldpi, /drawable-mdpi y /drawable-hdpi para utilizar diferentes recursos dependiendo
de la resolucin del dispositivo.

/res/layout/: contiene los ficheros de definicin de las diferentes pantallas de la


interfaz grfica. Se puede usar la carpeta /layout y /layout-land para definir los
diferentes diseos en funcin de la orientacin del dispositivo.

/res/anim/. alberga la definicin de las animaciones utilizadas por la aplicacin.

/res/menu/: contiene la definicin de los mens de la aplicacin.

/res/values/: contiene otros recursos de la aplicacin como, por ejemplo, cadenas de


texto (strings.xml), estilos (styles.xml), colores (colors.xml), etctera.

50

/res/xml/: contiene los ficheros XML utilizados por la aplicacin.

Introduccin al entorno Android

/res/raw/: contiene los recursos adicionales, normalmente en diferente formato a


XML, que no se incluyan en el resto de carpetas de recursos.

Carpeta /gen/
Rene una serie de elementos de cdigo generados automticamente al compilar el
proyecto. Cada vez que compilamos el proyecto, Android genera una serie de ficheros fuente
Java dirigidos al control de recursos de la aplicacin.
El archivo ms importante es el que se puede observar en la imagen anterior, el fichero
R.java que define la clase Java denominada R.
Esta clase R contiene un conjunto de constantes con los ID de todos los recursos de la
aplicacin incluidos en la carpeta /res/, de forma que el programador pueda acceder
fcilmente a estos recursos desde el cdigo fuente a travs de esta clase. As, por ejemplo, la
constante R.drawable.icon define el ID de la imagen icon.png contenida en la
carpeta /res/drawable/. Veamos como ejemplo la clase R creada por defecto para el
proyecto nuevo:

/* AUTO-GENERATED FILE. DO NOT MODIFY.


*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package es.mentor.unidad1.eje1.bienvenido;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
}

Importante: Esta clase la crea automticamente Android por lo que no debemos modificarla.

51

Carpeta /assets/
Alberga el resto de ficheros auxiliares necesarios para que aplicacin funcione, como
los ficheros de configuracin, de datos, etctera.
La diferencia entre los recursos incluidos en la carpeta /res/raw/ y los incluidos en la
carpeta /assets/ es que para los primeros se generar un ID en la clase R y se deber acceder
a ellos usando un mtodo de esta clase. Sin embargo, para los segundos no se generarn un
ID y se puede acceder a ellos por su ruta como a cualquier otro fichero del sistema.
Aplicaremos unos u otros segn las necesidades de nuestra aplicacin.
Carpeta /bin/
Es el directorio donde se guarda la aplicacin una vez se ha compilado.
Carpeta /libs/
Es el directorio donde se almacenan las libreras de tipo JAR que amplan las
funcionalidades de la aplicacin.
Fichero AndroidManifest.xml:
Contiene la definicin en formato XML de las caractersticas principales de la aplicacin,
como, por ejemplo, su identificacin (nombre, versin, icono, etctera), sus componentes
(Actividades, Mensajes, Servicios, etctera) o los permisos necesarios para su ejecucin.
Veremos ms adelante otros detalles de este fichero.

Importante: Haciendo doble clic sobre estos ficheros podemos abrirlos en el Editor de
Eclipse. Es importante que el alumno se familiarice con este entorno de desarrollo y pruebe
las distintas opciones del mismo.

Al abrir los distintos ficheros veremos la siguiente ventana:

52

Introduccin al entorno Android

1.3.2.4

Ejecucin del proyecto Android

Una vez hemos creado el proyecto, vamos a explicar cmo ejecutamos esta aplicacin
de prueba con Eclipse y el emulador de Android (AVD: Android Virtual Device).
Para ello, hacemos clic en el botn "Ejecutar" de la barra de herramientas principal

o en la opcin "Run" de men Run. Tambin disponemos del atajo del teclado
[Ctrl+F11]

53

Si no aparece ningn problema de compilacin, entonces aparecer la siguiente


ventana:

Eclipse inicia el emulador de Android AVD en el ordenador que ests utilizando, para
que puedas probar el proyecto que has desarrollado. Ten en cuenta que el dispositivo
virtual tarda un rato en cargar cada vez que lo inicias. Hay que tener un poco de paciencia
hasta que parezca la ventana de inicio.

54

Introduccin al entorno Android

En la consola de Eclipse puedes ir viendo el progreso de todo el proceso. Eclipse instala


automticamente la nueva aplicacin en el AVD y la ejecuta:

Cuando accedemos por primera vez al emulador, aparece la pantalla de bienvenida con
el terminal bloqueado:

Para desbloquear la pantalla hay que arrastrar el icono "candado" con el ratn hacia la
derecha.
Una vez desbloqueado el AVD, podemos ver el aspecto de la aplicacin instalada:

55

Importante: En general, cada vez que modifiquemos el cdigo fuente y deseemos probar de
nuevo nuestro proyecto no es necesario parar el emulador de aplicaciones y arrancarlo de
nuevo; simplemente hacemos clic de nuevo en el botn Run y Eclipse compilar, reinstalar
y ejecutar la aplicacin modificada.
Una vez hayamos acabado de probar nuestro proyecto, es necesario parar el emulador de
Android.

Atencin: En el Emulador de Android es posible probar varios proyectos a la vez.

Nota: En esta Unidad 1 puedes encontrar el vdeo Cmo ejecutar un proyecto Android, que
muestra cmo usar Eclipse para compilar y ejecutar los proyectos que son los ejemplos del
curso.

1.3.2.5

Cmo usar el emulador de Android (AVD)

Como puedes observar, el Emulador de Android simula un telfono con botones


(lado derecho de la ventana). Si ya sabes utilizar este tipo de telfonos no tendrs ningn
problema en manejar el emulador como si fuera un telfono ms.
Si no conoces Android, lee las siguientes instrucciones para ver cmo se maneja.
Cambiaremos el idioma del sistema operativo.
56

Introduccin al entorno Android

El botn Volver a atrs, permite cerrar la aplicacin y volver al Escritorio de Android:

En la pantalla que aparece a continuacin, debemos desbloquear el dispositivo virtual.


Para ello, arrastramos con el ratn la barra que tiene un candado dibujado hacia la derecha:

Despus, aparece la pantalla denominada Pantalla Inicial (en ingls se denomina


Home Screen), podemos acceder a todas las actividades instaladas haciendo clic en el icono
marcado con una flecha roja en la imagen siguiente:

57

Si lo hacemos, veremos las aplicaciones instaladas. A esta pantalla se la denomina


Pantalla de lanzamiento (en ingls se denomina Launcher Screen):

58

Introduccin al entorno Android

Para movernos en esta pantalla, podemos usar el ratn como si fuera el dedo de tu
mano. Es decir, para ver los iconos que estn abajo hay que hacer clic en la pantalla y, sin
soltar el botn del ratn, arrastrar la ventana hacia arriba:

Arrastrar hacia abajo


con el ratn

Haciendo clic con el ratn sobre el icono de una de las aplicaciones, el emulador la
ejecutar.
A continuacin, vamos a modificar el idioma del sistema operativo. Para ello, haciendo
clic en el icono Settings aparece la siguiente pantalla:

Arrastrar hacia
con el ratn

abajo

59

Desplazando con el ratn hacia arriba esta ventana hacemos clic en Language &
keyboard:

En la siguiente pantalla hacemos clic en Select language:

60

Introduccin al entorno Android

Para acabar, desplazamos de nuevo la pantalla hacia arriba hasta que veamos el idioma
en el que deseamos configurar Android:

Arrastrar hacia
con el ratn

abajo

Hacemos clic sobre el idioma correspondiente y el sistema operativo queda


configurado:

61

Despus, debemos desmarcar la opcin Japanese IME de esta pantalla:

Si usamos el botn Volver atrs, veremos que el idioma del sistema operativo ha
cambiado en la Pantalla Inicial (Home Screen):

62

Introduccin al entorno Android

En el apartado Uso del emulador de Android de la Unidad 2 puedes encontrar una


descripcin ms ampliada y detallada del AVD.

1.4

CMO CREAR LA PRIMERA APLICACIN CON ANDROID

A continuacin, vamos a explicar cmo crear un proyecto sencillo usando Eclipse y las
libreras Android.
Vamos a partir del proyecto de ejemplo que hemos creado en el punto anterior.
El primer proyecto Android consiste en una pantalla muy sencilla que muestra un
mensaje de bienvenida.
En la barra lateral Package Explorer de Eclipse, desplegamos las entradas haciendo
clic en las flechas de los diferentes paquetes.

Si abrimos el fichero BienvenidoActivity.java, veremos el cdigo fuente de la aplicacin


Android:

63

package es.mentor.unidad1.eje1.bienvenido;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class BienvenidoActivity extends Activity {


/** Mtodo que se llama cuando se crea una actividad. */
public void onCreate(Bundle savedInstanceState) {
// Llamamos al mtodo de la clase superior (Activity)
super.onCreate(savedInstanceState);
// Establecemos los contenidos de la Intefaz de usuario
// de forma programada.
TextView tv = new TextView(this);
tv.setText("Bienvenido al curso de Android de Mentor!");
setContentView(tv);
// Descomentar la siguiente sentencia para usar los layout en
// el diseo de la Interfaz Usuario. Si lo haces, debes
// comentar las 3 sentencias anteriores.
// setContentView(R.layout.main);
}
}

Fjate en que la clase principal BienvenidoActivity de la aplicacin se basa en la clase


Activity de Android.
Una actividad (Activity) es el componente de la aplicacin que realiza acciones. Una
aplicacin puede tener muchas actividades, si bien el usuario slo interacta con ellas de una
en una. Android llama al mtodo onCreate() cuando una actividad se inicia. En este mtodo se
lleva a cabo toda la inicializacin de variables y configuracin de la interfaz de usuario. Una
actividad no est obligada a tener una interfaz de usuario, aunque generalmente la suele tener.

En la Unidad 2 veremos en detalle todos los mtodos disponibles en esta clase


bsica de Android.
64

Introduccin al entorno Android

La interfaz de usuario de Android se compone de vistas (Views). Una vista es un


objeto que define el diseo de la interfaz de usuario. como un botn, una imagen, una etiqueta
de texto, etctera. Cada uno de estos objetos se hereda de la clase principal View. En este
ejemplo hemos utilizado la subclase TextView, que crea una etiqueta de texto.
En el ejemplo se crea una etiqueta TextView en el constructor de la Actividad. Para
crear esta etiqueta es necesario pasar como parmetro una instancia del Contexto (Context)
de la aplicacin Android. Un Contexto es un identificador del sistema que sirve para tener
acceso a recursos, a preferencias, a bases de datos, etctera, de la aplicacin. La clase
Actividad se hereda de la clase Contexto; por lo tanto, se puede pasar esta Actividad como el
Contexto de la aplicacin escribiendo this.
Con el mtodo setText() establecemos el texto contenido en la etiqueta.

Para acabar, usamos el mtodo setContentView() para indicar a la Actividad el contenido de


la interfaz de usuario.

Si ejecutas la aplicacin en Eclipse debers ver la siguiente ventana en el emulador:

65

Nota:
Al ejecutar varias veces una aplicacin desde Eclipse puede ocurrir que aparezcan los
siguientes mensajes de error en la consola:

Estos mensajes de error:


[2011112009:18:15unidad1.eje1.bienvenido]Applicationalreadydeployed.
Noneedtoreinstall.
[20111120 09:18:15 unidad1.eje1.bienvenido] Starting activity
es.mentor.unidad1.eje1.bienvenido.BienvenidoActivityondeviceemulator5554
[2011112009:18:16unidad1.eje1.bienvenido]ActivityManager:Starting:
Intent{act=android.intent.action.MAIN
cat=[android.intent.category.LAUNCHER]
cmp=es.mentor.unidad1.eje1.bienvenido/.BienvenidoActivity}
[2011112009:18:16unidad1.eje1.bienvenido]ActivityManager:Warning:
Activitynotstarted,itscurrenttaskhasbeenbroughttothefront
Indican nicamente que no se ha modificado el cdigo fuente y que la aplicacin se muestra
de nuevo en el primer plano de la pantalla del dispositivo virtual.

1.4.1 Cambio en la Interfaz de usuario con Layout


Los Layout son elementos no visibles que establecen cmo se distribuyen en la interfaz
del usuario los componentes (widgets) que incluyamos en su interior. Podemos pensar en
estos elementos como paneles donde vamos incorporando, de forma diseada, los
componentes con los que interacciona el usuario.
Nota: La clase Layout se hereda, como el resto de componente, de la clase Vista. A lo
largo del curso nos referimos a los componentes de Android como Vistas (Views) o como
Widgets, tanto si son visibles (botones, texto, mens.) como si son elementos de diseo
(layout).

En el ejemplo anterior hemos utilizado un diseo de interfaz de usuario "programado",


es decir, se construye esta interfaz con sentencias Java en el cdigo fuente. Si ya has
desarrollado interfaces de esta manera, sabrs que pequeos cambios en su diseo pueden
dar lugar a grandes modificaciones en el cdigo fuente.

66

Introduccin al entorno Android

Al ser Android un lenguaje nuevo, permite desarrollar interfaces usando archivos de


diseo (Layout) XML. La forma ms fcil de explicar este concepto es mostrar un ejemplo. El
fichero res/layout/main.xml define el diseo de la interfaz del usuario:

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/bienvenido"
/>
</LinearLayout>

La estructura general de un archivo de diseo de interfaz XML de Android es simple. Se


trata de un rbol de elementos XML, donde cada nodo es el nombre de una clase Vista (en
este ejemplo, usamos las clases LinearLayout y TextView). Puedes utilizar el nombre de
cualquier clase de tipo Vista (View) de Android o, incluso, una clase Vista personalizada por
el programador.
El Layout LinearLayout apila secuencialmente todos sus elementos hijos de forma
horizontal o vertical. En el apartado siguiente veremos diferentes tipos de paneles de diseo y
sus caractersticas.
Esta estructura XML hace que sea ms fcil y rpido crear las interfaces de usuario.
Este modelo se basa en el modelo de desarrollo web, donde se separa la presentacin
(interfaz de usuario) de la lgica de la aplicacin (encargada de leer y escribir la informacin).
En el ejemplo de XML anterior slo hay un elemento Vista: TextView, que tiene tres
atributos y un elemento de diseo Layout: LinearLayout, que tiene cuatro atributos. A
continuacin, mostramos una descripcin de los atributos:

67

Atributo

Descripcin

xmlns:android

Esta declaracin indica que vamos a usar el espacio de nombres (la


terminologa) de Android para referirnos a los atributos que se definen
a continuacin.

android:id

Asigna un identificador nico para el elemento correspondiente. Este


identificador sirve para poder acceder al componente desde el cdigo
fuente o desde las declaraciones de otros elementos en este archivo
XML.

Define el largo que debe ocupar la Vista. En este caso, indicamos que
android:layout_width el TextView debe ocupar toda la pantalla con "fill_parent.

android:layout_height

android:text

Similar al atributo anterior, en este caso, se refiere al ancho de la Vista.

Establece el texto que la Vista TextView debe mostrar. En este


ejemplo se utiliza una cadena que se establece en el archivo
res/values/strings.xml.

El ttulo de la aplicacin Unidad1 - Ejemplo 1: Bienvenido y la frase "Bienvenido al


curso de Android de Mentor!", que aparecen en el rea del usuario, se definen en el fichero
res/values/strings.xml.
El SDK de Android permite definir los ficheros de tipo XML de dos formas: a travs de
un editor visual o directamente en el archivo XML. Se puede cambiar entre las dos formas
haciendo clic en la pestaa de la parte inferior de la ventana. Por ejemplo, en el Package
Explorer, seleccionamos res/layout/main.xml y hacemos clic en Graphical Layout:

68

Introduccin al entorno Android

En esta ventana podemos disear visualmente la pantalla de la aplicacin Android


arrastrando con el ratn los componentes que aparecen en el apartador Palette.
Si en el Package Explorer seleccionamos res/layout/strings.xml y hacemos clic en
Resources:

Usando en esta ventana el botn Add podemos aadir visualmente los diferentes
tipos de recursos de Android.
Para crear la primera aplicacin hemos usado componentes (Widgets o Vistas) usuales
de Android. En el siguiente apartado de teora explicaremos en detalle el tipo de componentes
disponibles por defecto y cmo usarlos para disear las pantallas que servirn de interfaz
grfica al usuario.
Fichero AndroidManifest.xml: contiene la definicin en formato XML de las
caractersticas principales de la aplicacin, como su identificacin (nombre, versin, icono,
etctera), sus componentes (Actividades, Mensajes, Servicios, etctera) o los permisos
necesarios para su ejecucin. Ms adelante veremos otros detalles de este fichero.
En este fichero hay que declarar la Actividad para que Android tenga acceso a la misma.
Si abres el archive manifest, vers que existe el siguiente elemento <activity>:

69

<?xmlversion="1.0"encoding="utf8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
...
<activityandroid:name=".BienvenidoActivity"
android:label="@string/app_name">
...
</manifest>

En este fichero se pueden definir varios atributos para establecer la etiqueta de la


actividad, su icono o el tema de estilo de la interfaz de usuario. El nico atributo obligatorio es
android:name, que especifica el nombre de clase de la actividad. Es importante usar siempre
el mismo nombre de Actividad ya que otra aplicacin puede iniciarla.
Importante: Aunque el cdigo fuente de este ejemplo se encuentra disponible en la carpeta
de ejemplos de esta unidad, es fundamental que crees este proyecto Android desde el
principio para entender la secuencia de pasos dados y los ficheros necesarios.
Adems, si no has usado nunca el entorno de desarrollo Eclipse - Android, adquirirs soltura
utilizndolo.

1.5

DISEO DE LA INTERFAZ DE USUARIO

1.5.1 Cmo disear la interfaz de usuario mediante Vistas


Una de las caractersticas ms importante de Android es la posibilidad de usar
componentes grficos dinmicos y reutilizables (en ingls se denominan Views).
Mediante el SDK de Android, el programador puede utilizar clases prediseadas para
implementar elementos y comportamientos en la interfaz del usuario, que, de otra forma, ste
tendra que crear desde cero, tales como botones, cuadros de edicin complejos, arrastrar y
soltar, o mens en rbol.
Nota: Como en la literatura inglesa de Android se habla genricamente de Vista (View) para
referirse a estos componentes visuales y en Internet siempre aparecen referencias a esta
palabra, vamos a usar esta nomenclatura a partir de ahora.
Es importante no confundir los widgets (componentes o Vistas) que usamos al desarrollar las
interfaces de usuario en Android con Widget de la pantalla principal (Screen Home), que son
pequeas aplicaciones que el usuario del telfono puede aadir a esta pantalla, tales como un
calendario dinmico, previsin meteorolgica, etctera.

70

Introduccin al entorno Android

1.5.2 Vistas disponibles de Android


Construir interfaces de usuario en las aplicaciones de Android es muy sencillo y rpido
gracias a que podemos utilizar Vistas.
Como hemos visto en el apartado anterior, las Vistas visibles deben situarse dentro de
otro tipo de Vista denominada Layout (Panel de diseo).
Estos paneles de diseo (Layout) de Android se usan para disear la interfaz grfica del
usuario de la aplicacin. Estos paneles se usan para separar simblicamente el rea de la
aplicacin. Dentro de estos paneles se incluye la mayora de las Vistas, como botones,
cuadros de texto, etctera. Adems, dentro de un panel se pueden incluir otros paneles
para hacer diseos complejos.
Nota: Cuando se describan los mtodos ms importantes de cada Vista (View), slo se
incluirn aqullas que no se hayan sealado con anterioridad o se invoquen con diferentes
argumentos.
Adems, el entorno de Eclipse dispone de una ventana emergente de ayuda que, al
escribir cdigo fuente, muestra los diferentes mtodos disponibles para esa clase. De esta
forma, evitamos errores de codificacin y el desarrollo de las aplicaciones web es mucho ms
rpido y eficiente.
En la siguiente imagen mostramos el aspecto que tiene esta ventana de ayuda.
Con el atajo de teclado [CTRL+BARRA_ESPACIADORA] podemos acceder a esta
ventana de ayuda emergente.

71

Tipos de paneles (Layout)

Panel Marco (FrameLayout)

ste es el panel ms sencillo de todos los Layouts de Android. Un panel FrameLayout


coloca todos sus componentes hijos alineados pegados a su esquina superior izquierda de
forma que cada componente nuevo aadido oculta por el componente anterior. Por esto, se
suele utilizar para mostrar un nico control en su interior, a modo de contenedor (placeholder)
sencillo para un nico elemento, por ejemplo, una imagen.
Los componentes incluidos en un FrameLayout pueden establecer las propiedades
android:layout_width y android:layout_height, que pueden establecerse con los valores:

fill_parent para que el componente hijo tenga la dimensin del layout que lo
contiene.

wrap_content para que el componente hijo ocupe el tamao de su contenido.

Ejemplo

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<EditText android:id="@+id/TextoNombre"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>

72

Introduccin al entorno Android

Panel Lineal (LinearLayout)


Vertical y Horizontal

El panel LinearLayout apila todos sus componentes hijos de forma horizontal o


vertical, segn se establezca la propiedad android:orientation

con el valor vertical u

horizontal.
De igual forma que en un FrameLayout, se pueden establecer las propiedades
android:layout_width

android:layout_height.

Adems,

existe

la

propiedad

android:layout_weight, en el caso de un panel LinearLayout, que permite establecer las


dimensiones de los componentes contenidos proporcionales entre ellos.
Ejemplo

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<EditText android:id="@+id/TextoDato1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" />
<EditText android:id="@+id/TextoDato2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2" />
</LinearLayout>

Si incluimos en un panel vertical dos cuadros de texto (EditText) y en uno de ellos


establecemos un layout_weight=1 y en el otro un layout_weight=2 , conseguiremos que
toda la superficie del panel est ocupada por los dos cuadros de texto y, adems, que el
segundo sea el doble (relacin entre sus propiedades weight) de alto que el primero.

73

Panel Tabla (TableLayout)

El panel TableLayout permite distribuir todos sus componentes hijos como si se


tratara de una tabla mediante filas y columnas.
La estructura de la tabla se define de manera similar a una tabla en formato HTML, es
decir, indicando las filas que compondrn la tabla (objetos TableRow) y las columnas de
cada una de ellas.
Por norma general, el ancho de cada columna corresponde al ancho del mayor
componente de dicha columna, pero existen una serie de propiedades pueden modificar este
comportamiento:

android:stretchColumns: indica el nmero de columna que se expande para


ocupar el espacio libre que dejan el resto de columnas a la derecha de la
pantalla.

android:shrinkColumns: indica las columnas que se pueden contraer para


dejar espacio al resto de columnas de lado derecho de la pantalla.

android:collapseColumns: indica las columnas de la tabla que se pueden


ocultar completamente.
Todas estas propiedades del TableLayout pueden establecerse con una lista
de

ndices

de

las

columnas

separados

por

comas,

por

ejemplo:

android:stretchColumns=1,2,3 o un asterisco para indicar que se debe


aplicar a todas las columnas, de esta forma: android:stretchColumns=*.

android:layout_span: una celda determinada puede ocupar el espacio de


varias columnas de la tabla (anlogo al atributo colspan de HTML) del
componente concreto que ocupa dicho espacio.

Ejemplo

<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView android:text="Celda 1.1">
<TextView android:text="Celda 1.2">
</TableRow>

<TableRow>

74

Introduccin al entorno Android

<TextView android:text="Celda 2.1">


<TextView android:text="Celda 2.2">
</TableRow>
<TableRow>
<TextView android:text="Celda 3" android:layout_span="2" />
</TableRow>
</TableLayout>

Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Panel tabla) de la Unidad 1. Estudia
el cdigo fuente y ejectalo para mostrar en el emulador el resultado del programa anterior,
en el que hemos utilizado el Layout TableLayout.

Panel Relativo (RelativeLayout)

El panel RelativeLayout permite especificar la posicin de cada componente de forma


relativa a su elemento padre o a cualquier otro elemento incluido en el propio layout. As, al
incluir un nuevo componente X podemos indicar, por ejemplo, que debe situarse debajo del
componente Y y alineado a la derecha del layout padre (el que lo contiene).
Un panel RelativeLayout dispone de mltiples propiedades para colocar cada
componente. Las principales son:
Posicin relativa a otro control:

android:layout_above: arriba.
android:layout_below: debajo.
android:layout_toLeftOf: a la izquierda de.
android:layout_toRightOf: a la derecha de.
android:layout_alignLeft: alinear a la izquierda.
android:layout_alignRight: alinear a la derecha.
android:layout_alignTop: alinear arriba.
android:layout_alignBottom: alinear abajo.
android:layout_alignBaseline: alinear en la base.

Posicin relativa al layout padre:

android:layout_alignParentLeft: alinear a la izquierda.


android:layout_alignParentRight: alinear a la derecha.
android:layout_alignParentTop: alinear arriba.
android:layout_alignParentBottom: alinear abajo.
android:layout_centerHorizontal: alinear horizontalmente al
75

centro.

android:layout_centerVertical: alinear verticalmente al centro.


android:layout_centerInParent: centrar.

Opciones de margen (tambin disponibles en el resto de layouts):

android:layout_margin: establece el margen.


android:layout_marginBottom: establece el margen inferior.
android:layout_marginTop: establece el margen superior.
android:layout_marginLeft: establece el margen izquierdo.
android:layout_marginRight: establece el margen derecho.

Opciones de espaciado o padding (tambin disponibles en el resto de layouts):

android:padding: establece la separacin entre componentes.


android:paddingBottom: establece la separacin inferior.
android:paddingTop: establece la separacin superior.
android:paddingLeft: establece la separacin izquierda.
android:paddingRight: establece la separacin derecha.

Ejemplo
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:id="@+id/TextoNombre"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button android:id="@+id/BtnAceptar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/TextoNombre"
android:layout_alignParentRight="true" />
</RelativeLayout>

En este ejemplo, el botn BtnAceptar se coloca debajo del cuadro de texto


TextoNombre (android:layout_below=@id/TxtNombre) y alineado a la derecha del layout
padre (android:layout_alignParentRight=true); adems, se establece un margen a su
izquierda de 10 pixeles (android:layout_marginLeft=10px).

76

Introduccin al entorno Android

Panel Marco (FrameLayout

El panel FrameLayout permite superponer en el rea de la pantalla varios


componentes hijos. Por lo general, este panel debe contener pocos componentes hijos, ya
que puede ser difcil organizarlos sin que se superpongan unos con otros en los diferentes
tamaos de pantalla de los telfonos. El atributo ms importante de los componentes hijos
que contiene es layout_gravity, que permite controlar su posicin relativa.
Ejemplo

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center"
android:src="@drawable/imagen" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dip"
android:layout_gravity="center_horizontal|bottom"
android:padding="12dip"
android:background="#AA000000"
android:textColor="#ffffffff"
android:text="Imagen" />
</FrameLayout>

En este ejemplo, mostramos una imagen que ocupa todo el rea de la pantalla usando
el atributo android:scaleType="center" y una etiqueta superpuesta en la parte inferior de la
pantalla con el atributo android:layout_gravity="center_horizontal|bottom".

77

Componentes Bsicos
Como hemos comentado anteriormente, los interfaces de las aplicaciones de usuario en
Android se construyen usando componentes o Vistas que estn contenidas en paneles de
diseo (layout). Los componentes permiten al usuario interaccionar con la aplicacin. Los
paneles ordenan la posicin de estos elementos en la interfaz del usuario.
A continuacin, vamos a mostrar los componentes bsicos.
Botones

Los botones se usan para que el usuario interacte con la aplicacin Web:
El SDK de Android proporciona tres tipos de botones: el botn clsico (Button), el de
tipo on/off (ToggleButton) y el que puede albergar una imagen (ImageButton).

El componente Button es el botn bsico de Android. En el ejemplo siguiente

definimos un botn con el texto Haz clic aqu asignando la propiedad android:text.
<Button android:text="Haz clic aqu"

android:id="@+id/boton" android:layout_height="wrap_content"
android:layout_width="135dp">
</Button>

Adems,

podemos

utilizar

otras

propiedades,

como

el

color

de

fondo

(android:background), el estilo de la fuente (android:typeface), el color de fuente


(android:textcolor), el tamao de la fuente (android:textSize), etctera.

El componente ToggleButton es un tipo de botn que puede encontrarse en dos

estados: pulsado (ON) o no_pulsado (OFF). En este caso, en lugar de definir un nico
texto, podemos establecer dos en funcin del estado asignando las propiedades
android:textOn

android:textoOff,

respectivamente.

Veamos

un

continuacin:
<ToggleButton android:id="@+id/toggleButton"
android:text="ToggleButton" android:layout_width="88dp"
android:textOn="Encendido"
android:textOff="Apagado"
android:layout_height="match_parent">

78

ejemplo

Introduccin al entorno Android

</ToggleButton>

El componente ImageButton es un botn que muestra una imagen en lugar de un

texto asignando la propiedad android:src. Normalmente, indicamos esta propiedad


usando el descriptor de alguna imagen que hayamos copiado en la carpeta
/res/drawable. As, por ejemplo, en nuestro caso hemos incluido la imagen stop.png, a
la que hacemos referencia en @drawable/ok.
<ImageButton android:layout_height="wrap_content"
android:src="@drawable/stop" android:layout_width="wrap_content"
android:id="@+id/imageButton">

</ImageButton>

Los botones disponen de eventos que se puede capturar. El ms comn es el evento


onClick. En la Unidad 2 veremos qu son los Eventos y los Listerners, si bien en este
apartado se incluyen algunos ejemplos para que la teora sea consistente.
Para

definir

la

lgica

de

este

evento

hay

que

definir

un

nuevo

objeto

View.OnClickListener() y asociarlo al botn mediante el mtodo setOnClickListener(). La


forma de hacerlo es la siguiente:

final Button btnBoton1 = (Button)findViewById(R.id.boton);


btnBoton1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0)
{
lblEtiqueta.setText("Has pulsado el Botn!");
}
});

En el caso del botn de tipo ToggleButton suele ser ms til conocer el estado en el
que est el botn tras ser pulsado. Para esto, se usa el mtodo isChecked(). En el siguiente
ejemplo se comprueba el estado del botn despus de ser pulsado y se realizan diferentes
acciones segn su resultado:
79

final ToggleButton btnBoton2 =


(ToggleButton)findViewById(R.id.toggleButton);
btnBoton2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0)
{
if(btnBoton2.isChecked())
lblEtiqueta.setText("Botn Encendido");
else
lblEtiqueta.setText("Botn Apagado");
}
});

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Componentes bsicos) de la Unidad 1.


Estudia el cdigo fuente y ejectalo para mostrar en el emulador el resultado del programa
anterior.

Etiqueta (TextView)

La etiqueta (o TextView en ingls) permite mostrar un determinado texto al usuario. El


texto se establece mediante la propiedad android:text. Adems de esta propiedad, se puede
cambiar el formato del texto usando las siguientes propiedades: android:background (color
de fondo), android:textColor (color del texto), android:textSize (tamao de la fuente) y
android:typeface (estilo del texto: negrita, cursiva). Fjate en el cdigo del siguiente ejemplo:

<TextView android:id="@+id/texto1" android:text="Texto Plano"


android:textSize="30dp"
android:layout_width="wrap_content"

android:layout_height="wrap_content" />
<TextView android:id="@+id/texto2" android:text="Fuente Serif"
android:layout_width="wrap_content"

80

Introduccin al entorno Android

android:layout_height="wrap_content"
android:textSize="25dp"
android:typeface="serif" />
<TextView android:id="@+id/texto3" android:text="Negrita"
android:layout_width="wrap_content"

android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView android:id="@+id/texto4" android:text="Cursiva"
android:layout_width="wrap_content"

android:layout_height="wrap_content"
android:textSize="40dp"
android:textColor="#FF0000"
android:textStyle="italic" />

Adems, podemos modificar estas propiedades desde nuestro cdigo Java usando los
mtodos getText() para recuperar el texto de una etiqueta, setText() para actualizar el texto y
setBackgroundColor() para cambiar el color de fondo. Por ejemplo, as:
// Buscamos la etiqueta con el id texto1
final TextView lblEtiqueta = (TextView)findViewById(R.id.texto1);
String texto = lblEtiqueta.getText().toString();
texto += " abc";
lblEtiqueta.setText(texto);

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Componentes bsicos) de la Unidad 1.


Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa
anterior, en el que hemos utilizado el componente TextWiew

Imagen (ImageView)

La imagen (o ImageView en ingls), como su propio nombre indica, permite mostrar


imgenes en la aplicacin. La propiedad ms til es android:src y permite establecer la
imagen que se muestra. De nuevo, lo usual es indicar como origen de la imagen el
identificador de un recurso de la carpeta /res/drawable. Adems de esta propiedad, existen
81

otras, como las destinadas a establecer el tamao mximo que puede ocupar la imagen:
android:maxWidth y android:maxHeight. Fjate en el cdigo del siguiente ejemplo:
<ImageView android:id="@+id/ImgFoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon" />

En la lgica de la aplicacin, podemos establecer la imagen mediante el mtodo


setImageResorce():
ImageView img= (ImageView)findViewById(R.id.ImgFoto);
img.setImageResource(R.drawable.icon);

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Componentes bsicos) de la


Unidad 1. Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del
programa anterior, en el que hemos utilizado el componente ImageView.

Cuadro de Texto (Text Edit)

El Cuadro de Texto (o EditText en ingls) es el componente de edicin de texto de


Android que permite la introduccin y edicin de texto al usuario. Su propiedad ms
importante es android:text, que establece el texto que contiene. Fjate en el cdigo del
siguiente ejemplo:
<EditText android:id="@+id/editTexto"

android:layout_width="match_parent"
android:layout_height="wrap_content">

Tambin es posible recuperar y establecer este texto mediante los mtodos getText() y
setText(nuevoTexto) respectivamente:

final EditText txtTexto = (EditText)findViewById(R.id.editTexto);

82

Introduccin al entorno Android

texto = txtTexto.getText().toString();
txtTexto.setText("Esto es un texto");

En el cdigo fuente anterior hemos hecho un cambio de formato usando el mtodo


toString() sobre el resultado de getText(). El mtodo getText() no devuelve una cadena
(String), sino un objeto de tipo Editable (tipo Spanned, algo as como una cadena de
caracteres en la que podemos insertar etiquetas)
Es decir, el componente EditText permite editar texto plano y texto enriquecido o con
formato; por eso hemos tenido que usar un mtodo para cambiar la cadena perdiendo el
formato enriquecido.
Para poder obtener el texto con el formato correspondiente, podemos usar la clase
Html de Android, que dispone de los mtodos para convertir cualquier objeto de tipo
Spanned en su representacin HTML equivalente. Veamos cmo funciona:
//Obtiene el texto del componente con etiquetas de formato HTML
String aux2 = Html.toHtml(txtTexto.getText());

La sentencia anterior devolvera una cadena de texto como sta


<p>Esto es una <b>prueba</b>.</p>.

Tambin es posible realizar la operacin opuesta, es decir, establecer en un cuadro de


texto (EditText) o en una etiqueta (TextView) un texto en formato HTML. Para ello, se utiliza
el mtodo Html.fromHtml(String) as:
//Asigna el texto con formato HTML
txtTexto.setText(
Html.fromHtml("<p>Esto es una <b>prueba</b>.</p>"),
BufferType.SPANNABLE);

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Componentes bsicos) de la Unidad 1.


Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa
anterior, en el que hemos utilizado el componente EditText.

83

Cuadro de Seleccin (CheckBox)

La caja de seleccin (o CheckBox en ingls) permite al usuario marcar o desmarcar


opciones en una aplicacin. La forma de definirlo en la interfaz y los mtodos disponibles
para manipularlos son anlogos a los ya comentados para el componente ToggleButton.
Fjate en el cdigo del siguiente ejemplo:
<LinearLayout android:id="@+id/linearLayout4"
android:orientation="vertical" android:layout_width="154dp"

android:layout_height="wrap_content">
<CheckBox android:id="@+id/check1" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Android" />

<CheckBox android:id="@+id/check2" android:layout_width="wrap_content"


android:layout_height="wrap_content" android:text="iPhone" />
</LinearLayout>
<Button android:id="@+id/botonCB" android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Confirmar Seleccin"

android:layout_gravity="center_vertical" />

Respecto a la personalizacin de estilo del componente, podemos emplear casi todas


las opciones del componente TextView comentadas anteriormente.
En el cdigo de la aplicacin podemos utilizar los mtodos isChecked() para conocer
el estado del componente y setChecked(boolean) para establecer un estado en concreto.
En cuanto a los posibles eventos interesantes que puede lanzar este componente, el
ms interesante es onCheckedChanged que notifica que la seleccin ha cambiado. Por
ejemplo, as:
CheckBox.OnCheckedChangeListener CBCambioListener = new
CheckBox.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,

boolean isChecked)

84

Introduccin al entorno Android

{
if (isChecked) {
txtTexto.setText("Checkbox "+ buttonView.getText() +

" marcado!");
}
else {
txtTexto.setText("Checkbox "+ buttonView.getText() +

" desmarcado!");
}
}

};

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Componentes bsicos) de la


Unidad 1. Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del
programa anterior, en el que hemos utilizado el componente Checkbox.

Botn de radio (RadioButton)

El botn de radio (o RadioButton en ingls) permite elegir una nica opcin de un


grupo de opciones, es decir, si se marca una de ellas se desmarcar automticamente la
anterior. En Android, los botones RadioButton se agrupan dentro de un elemento
RadioGroup. Veamos un ejemplo de cmo definir un grupo de botones RadioButton en la
interfaz:
<RadioGroup android:id="@+id/gruporb" android:orientation="vertical"
android:layout_width="fill_parent"

android:layout_height="fill_parent">
<RadioButton android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

85

android:text="Opcin 1" />


<RadioButton android:id="@+id/radio2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Opcin 2" />
</RadioGroup>

En primer lugar, hemos establecido la orientacin (vertical u horizontal) como hicimos


con el componente LinearLayout. Despus, hemos aadido todos los componentes
RadioButton necesarios, indicando su ID mediante la propiedad android:id y su texto
mediante la propiedad android:text.

Una vez definida la interfaz, podemos manipular los componentes desde el cdigo java
haciendo uso de los diferentes mtodos del componente RadioGroup como, por ejemplo:

check(id): selecciona una opcin determinada mediante su ID.

clearCheck():desmarca la opcin seleccionada.

getCheckedRadioButtonId():devuelve el ID de la opcin seleccionada o -1 si


no hay ninguna marcada.

En cuanto a los eventos iniciados por este componente, como los CheckBox, el ms
til es el que informa de los cambios en el elemento seleccionado onCheckedChange. Fjate
en el siguiente ejemplo:

// Definimos el evento OnCheckedChange


final RadioGroup rg = (RadioGroup)findViewById(R.id.gruporb);
rg.setOnCheckedChangeListener(
new RadioGroup.OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup grupo, int checkedId) {

// Obtenemos el RadioButton que est seleccionado usando


// el ID marcado checkedId
final RadioButton rb = (RadioButton) findViewById(checkedId);

86

Introduccin al entorno Android

txtTexto.setText("RadioButton seleccionado: " + rb.getText());


}
});

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Componentes bsicos) de la


Unidad 1. Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del
programa anterior, en el que hemos utilizado el componente

Si ejecutas en Eclipse este Ejemplo 3, vers que se muestra la siguiente aplicacin en


el Emulador:

87

Android es un sistema operativo, inicialmente diseado para telfonos mviles


con sistemas operativos iOS (Apple), Symbian (Nokia) y Blackberry OS.
Android, basado en Linux, es un sistema operativo libre y gratuito.
Los componentes principales de la arquitectura del sistema operativo Android
son las Aplicaciones, el Marco de desarrollo (SDK), las Libreras de Android, el
Runtime (ejecutable) y el Ncleo de Linux.
Todas las aplicaciones de Android usan el siguiente conjunto de servicios y
sistemas:
o Un conjunto de componentes (Views)
o Proveedores de contenidos (Content Providers)
o Gestor de recursos (Resource Manager)
o Gestor de notificaciones (Notification Manager)
o Gestor de actividades (Activity Manager)
Android usa Java como lenguaje base para el desarrollo de las aplicaciones; por lo
tanto, emplea Paquetes Java (Package en ingls). Estos paquetes son
contenedores de clases que permiten agrupar las distintas partes de un programa
cuya funcionalidad tienen elementos comunes.
El nombre de los paquetes debe ser nico en relacin con los paquetes
instalados en Android. Por esta razn, es importante utilizar el estndar de dominio
com.dominio para nombrarlos.
Los ficheros que contengan el cdigo fuente de las actividades del alumno han de
guardarse

en

una

carpeta

personal.

Recomendamos

usar

el

directorio

C:\cursos_Mentor\Android\proyectos para este fin.


Es muy importante conocer la estructura de directorios y el nombre de los
ficheros que componen un proyecto Android.

88

Introduccin al entorno Android

Para comprobar que una aplicacin funciona, hay que hacer clic en la opcin "Run"
de men "Run" de Eclipse (Atajo del teclado [Ctrl+F11]); despus, arrancar el
Emulador (AVD) de Android para ver el resultado de su ejecucin.
Un Dispositivo Virtual de Android (en ingls, AVD, Android Virtual Device) emula
un terminal instalado con Android en el que podemos probar las aplicaciones
desarrolladas.
Todas las sentencias de Android (Java) deben acabar con ;.
Las sentencias o instrucciones compuestas contienen varias sentencias simples y
deben estar incluidas entre los signos { y }. Generalmente, una sentencia
compuesta est integrada por sentencias simples de un bucle o de la declaracin
de una funcin que deben ejecutarse como un bloque.
Para poder seguir mejor el flujo de un programa y ver ms intuitivamente su
cdigo, conviene indentar (adentrar unos espacios) las sentencias que estn
incluidas dentro de una estructura. En Eclipse podemos usar el atajo de teclado
[CTRL+I] para hacerlo automticamente.
Los comentarios ayudan mucho a comprender un programa. Los que slo
ocupan una lnea deben ir precedidos de los signos //. Si el texto ocupa ms de una
lnea, hay que incluirlo entre los signos /* y */.
Android dispone de todas las variables, funciones, expresiones y operadores
ms usuales de Java.
Una de las caractersticas ms importante de Android es la posibilidad de usar
componentes grficos dinmicos y reutilizables (en ingls se denominan Views).
Los paneles en Android se usan para disear (en ingls se denomina layout) la
interfaz grfica del usuario de la aplicacin.
El entorno de Eclipse ayuda al programador mostrando una ventana emergente
de ayuda al escribir el cdigo fuente. En ella se proponen los diferentes mtodos
disponibles

para

esa

clase.

Disponemos

tambin

del

atajo

de

teclado

[CTRL+BARRA_ESPACIADORA].

89

Las Vistas visibles estn contenidos en los paneles y permiten interaccionar al


usuario con la aplicacin.
Android define detectores de eventos (Listeners) que, asociados a un
componente, permiten controlar la interaccin del usuario sobre ste: clic del ratn,
escribir en el teclado, etctera. En la Unidad 2 se tratan en detalle estos detectores.
En Android como en cualquier lenguaje las expresiones constituyen uno de los
asuntos ms importantes de la programacin, pues intervienen en todas las
sentencias y estn integradas por todos los elementos de un lenguaje informtico.

90

DISEO DEL INTERFAZ DE


USUARIO

NDICE
2.1 ACTIVIDADES - ANDROID .................................................. 93
2.1.1 Introduccin..............................................................................93
2.1.2 Creacin de una actividad......................................................93
2.1.3 Ciclo de vida de una actividad ..............................................94
2.1.4 Cmo se implementa el ciclo de vida de una actividad ....94
2.2 EVENTOS Y LISTENERS ...................................................... 100
2.2.1 Gestionando los eventos del usuario ............................. 100
2.2.2 Uso de los Event Listeners ............................................. 100
2.2.3 Gestores de Eventos (Event Handlers)........................... 104
2.2.4 Modo tctil de pantalla ................................................... 105
2.2.5 Controlando la Vista con el foco activo.......................... 106
2.3 USO DEL EMULADOR DE ANDROID ................................. 106
2.3.1 Teclado del emulador ..................................................... 108
2.3.2 Cmo introducir tildes con el Teclado del Emulador ..... 110
2.3.3 Limitaciones del Emulador ............................................. 111
2.3.4 Tamao ventana emulador ............................................. 111
2.3.5 Otras opciones del Emulador ......................................... 112
2.3.6 Cmo configurar las opciones del Emulador ................. 113
2.4 COMPONENTES AVANZADOS ........................................... 114
2.4.1 Qu son los Adaptadores de Android (adapters) ........... 114
2.5 COMPONENTES PERSONALIZADOS................................ 127
2.5.1 Diseo de componentes personalizados ....................... 127
2.5.2 Cmo crear un componente extendido ......................... 127
2.5.3 Cmo combinar varios componentes para crear uno
compuesto ........................................................................... 130

Diseo del interfaz de usuario

2.1

ACTIVIDADES - ANDROID

2.1.1 Introduccin
Una Actividad (Activity) es un componente de Android que ofrece una pantalla
con la que los usuarios pueden interactuar con la aplicacin, como marcar el telfono,
sacar una foto, enviar un correo electrnico o ver un mapa. Cada Actividad tiene asociada una
ventana en la que se dibuja la interfaz de usuario. Normalmente, esta ventana ocupa toda la
pantalla, aunque puede ser menor que sta o flotar sobre otras ventanas.
Por lo general, una aplicacin de Android consta de mltiples actividades que estn
ms o menos ligadas entre s. Habitualmente, se define una actividad "principal", que es la que
se presenta al usuario cuando se inicia la aplicacin por primera vez. Una actividad puede
iniciar otra actividad con el fin de realizar diferentes operaciones. Cada vez que comienza una
nueva actividad, la actividad anterior se detiene y la enva a una pila de retroceso ("back
stack"). Esta pila usa el mecanismo de cola LIFO ("last in, first out"), por lo que, cuando el
usuario pulsa la tecla Volver atrs del mvil, se extrae de la pila la actividad anterior
(destruyndose la pila) y se reanuda. En la Unidad 3 veremos en detalle cmo funciona esta
pila.
Cuando una actividad se para porque se inicia una nueva actividad, se le notifica este
cambio de estado a travs de los mtodos de llamada callback

del ciclo de vida de la

actividad.
Una funcin de llamada (en ingls callback) es una funcin que se remite a Android
cuando se inicia una Actividad, para que el sistema operativo la llame durante la ejecucin
de esta Actividad.
Existen varios mtodos de llamada callback que una actividad puede recibir debido a
un cambio en su estado; por ejemplo, cuando el sistema crea la Actividad, cuando se reactiva
o cuando se destruye. El programador puede aprovechar estos mtodos para ejecutar
sentencias especficas apropiadas para el cambio de estado. Por ejemplo, cuando una
Actividad se suspende es recomendable liberar de la memoria todos los objetos grandes.
Cuando la actividad se reanuda, se puede volver a reservar los recursos necesarios y
continuar con las acciones que se interrumpieron. Estos cambios de estado forman parte
del ciclo de vida de la actividad.

2.1.2 Creacin de una actividad


Para crear una Actividad debemos usar la clase Activity de Android. En la subclase
creada es necesario implementar los mtodos callback que el sistema puede invocar cuando
hay un cambio en su ciclo de vida: la actividad se est creando, se detiene, se reanuda o se
destruye. Los dos mtodos callback ms importantes son:
93

onCreate(): es obligatorio implementar este mtodo ya que el sistema lo invoca


cuando crea su actividad. Dentro de su implementacin debemos iniciar los
componentes esenciales de la actividad, como dibujar la interfaz de usuario
empleado la funcin setContentView().
onPause(): el sistema llama a este mtodo cuando el usuario detiene la actividad,
aunque no significa que la actividad se destruya. Aqu es donde el programador debe
guardar todos los cambios que deben persistir en la siguiente sesin del usuario, ya
que ste podra no volver a la Actividad y que sta se destruyera.
Es posible utilizar otros mtodos de tipo callback para proporcionar una experiencia de
usuario fluida entre actividades y manejar el paso de una a otra. Lo veremos ms adelante en
este apartado.

2.1.3 Ciclo de vida de una actividad


Una Actividad puede mantenerse en tres estados:
Resumed: la actividad est en el primer plano de la pantalla y el usuario la est
utilizando. Este estado tambin se denomina "running".
Paused: la actividad es visible, pero hay otra actividad en primer plano que tiene el
foco. La actividad pausada sigue viva ya que se mantiene en memoria y conserva
toda la informacin de estado, si bien el sistema operativo puede eliminarla en caso
de memoria disponible muy baja.
Stopped: la actividad se oculta completamente por una nueva actividad (la actividad
anterior se ejecuta en "background"). Una actividad detenida tambin se mantiene
en memoria con toda la informacin de estado. Sin embargo, el usuario ya no la ve
visible y el sistema operativo puede eliminarla cuando se necesita memoria para otra
tarea.
Si una actividad est pausada o detenida, el sistema puede eliminarla de la memoria
invocando el mtodo finish() de la Actividad o, simplemente, puede acabar (kill) con el
proceso. Si se quiere abrir de nuevo esta actividad, despus de haber finalizado, debe ser
creada otra vez desde el principio

2.1.4 Cmo se implementa el ciclo de vida de una actividad


Cuando una actividad cambia entre los diferentes estados descritos anteriormente, el
sistema operativo le notifica el cambio mediante diferentes mtodos callback. El programador
puede usar todos estos mtodos callback para ejecutar las rdenes apropiadas. El ejemplo
siguiente incluye la estructura de cada uno de estos mtodos fundamentales del ciclo de vida
de una Actividad:
94

Diseo del interfaz de usuario

// Definimos el evento callback onCreate de la Actividad


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Usamos la clase Toast que muestra durante 1 segundo un
// mensaje pequeo al usuario
Toast.makeText(this, "Se ejecuta el mtodo onCreate", 1).show();
}
// Definimos el evento callback onPause de la Actividad
@Override
protected void onPause() {
super.onPause();
// Aqu deberamos guardar la informacin para la siguiente sesin
Toast.makeText(this, "Se ejecuta el mtodo onPause", 1).show();
}
// Definimos el evento callback onRestart de la Actividad

@Override
protected void onRestart() {
super.onRestart();
Toast.makeText(this, "Se ejecuta el mtodo onRestart", 1).show();
}
// Definimos el evento callback onResume de la Actividad
@Override
protected void onResume() {
super.onResume();
Toast.makeText(this, "Se ejecuta el mtodo onResume", 1).show();
}
// Definimos el evento callback onStart de la Actividad
@Override
protected void onStart() {
super.onStart();

95

// Aqu deberamos leer los datos de la ltima sesin para seguir la


// aplicacin donde la dej el usuario
Toast.makeText(this, "Se ejecuta el mtodo onStart", 1).show();

}
// Definimos el evento callback onDestroy de la Actividad
@Override
protected void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Se ejecuta el mtodo onDestroy", 1).show();
}
// Definimos el evento callback onStop de la Actividad
@Override
protected void onStop() {
super.onStop();
Toast.makeText(this, "Se ejecuta el mtodo onStop", 1).show();
}

IMPORTANTE: la implementacin de estos mtodos siempre debe incluir la llamada al


mtodo de la clase superior (superclase) antes de ejecutar cualquier otra sentencia, de la
forma siguiente: super.onStop();.

A continuacin, vamos a ver el ciclo de vida de una Actividad siguiendo sus estados y
los mtodos callback que desencadena cada uno de ellos:
El ciclo de vida de una Actividad ocurre entre las llamadas a los mtodos onCreate()
y OnDestroy(). En el primer mtodo la Actividad debe realizar la reserva de memoria,
el diseo de la interfaz de usuario y recuperar el estado de la sesin anterior. En el
segundo mtodo, hay que liberar todos los recursos usados con anterioridad.
El ciclo de vida "visible" de una Actividad ocurre entre las llamadas a los mtodos
OnStart() y OnStop(). Durante este tiempo el usuario puede ver e interactuar con la
pantalla de la Actividad. El sistema invoca el mtodo OnStop()cuando se inicia una
nueva actividad y la actual ya no est visible al usuario. Entre estos dos mtodos, el
programador debe definir y destruir respectivamente los recursos necesarios para
mostrar la Actividad para el usuario.

96

Diseo del interfaz de usuario


El ciclo de vida "en ejecucin" de una Actividad sucede entre las llamadas a los
mtodos OnResume() y OnPause(). Durante este tiempo la Actividad se ejecuta en
primer plano y tiene el foco del usuario. A menudo, el sistema operativo invoca estos
mtodos, por ejemplo, cuando el telfono se queda en modo espera o cuando
aparece una ventana con un mensaje de la aplicacin. Por esto, es conveniente incluir
en estos mtodos pocas y sencillas sentencias para que la aplicacin no se pare
cuando el usuario intenta hacer operaciones con ella.
A continuacin, se muestra un esquema visual de los cambios de estado posibles
dentro del ciclo de vida de una Actividad. Los rectngulos representan los mtodos callback
que el sistema operativo puede invocar en las transiciones entre los estados de la Actividad.

97

Resumen de los mtodos callback del ciclo de vida de una Actividad

Mtodo

Descripcin

Kill

Siguiente
mtodo

onCreate()

Se invoca cuando la Actividad se crea por primera


vez. Aqu es donde se reserva la memoria
necesaria, se crea la interfaz de usuario, se
recupera el estado de la sesin anterior, etctera.

No

onStart()

onRestart()

Se invoca cuando la Actividad est parada, justo


antes de iniciarse de nuevo.

No

onStart()

onResume()
o
onStop()

Se invoca justo antes de que el usuario pueda ver


la Actividad.
onStart()

A continuacin, se puede invocar el mtodo


onResume() si la Actividad vuelve al primer plano
o onStop() si se oculta.

No

onResume()

Se invoca justo antes de que el usuario comience a


interactuar con la Actividad.

No

onPause()

onResume()
o
onStop()

onPause()

Se invoca cuando el sistema est a punto de


comenzar otra Actividad. Es recomendable usar
este mtodo para confirmar con el usuario si quiere
guardar los cambios, desactivar animaciones y
cualquier cdigo que consuma CPU, etctera.
Estas sentencias deben ser muy rpidas porque la
nueva Actividad no se inicia hasta que finaliza este
mtodo.
A continuacin, se puede invocar el mtodo
onResume() si la Actividad vuelve al primer plano
o onStop() si se oculta.

98

Diseo del interfaz de usuario

Mtodo

Descripcin

onStop()

Se invoca cuando la actividad ya no es visible al


usuario. Esto puede ocurrir porque se vaya a
destruir la Actividad o porque otra Actividad
(existente o nueva) se ha reanudado y vuelve al
primer plano.

Kill

Siguiente
mtodo

onRestart()
o
onDestroy()

ninguno

A continuacin, se puede invocar el mtodo


onRestart() si la Actividad vuelve al primer plano o
onDestroy() si se destruye.

Se invoca antes de que se destruya una Actividad;


se trata, pues, del ltimo mtodo.

onDestroy()

El sistema operativo puede invocar este mtodo


porque el usuario decide finalizar la aplicacin
(mtodo finish())o porque es necesaria memoria
libre. Se puede distinguir entre estos dos
escenarios con el mtodo isFinishing().

La columna "Kill" de esta tabla indica si el sistema puede matar (kill) el proceso de la
Actividad cuando finaliza la ejecucin del mtodo sin ejecutar ninguna sentencia ms de la
Actividad. Hay tres mtodos as: onPause(), onStop() y onDestroy().
onPause() es el mtodo que se ejecuta siempre en caso de que el sistema operativo
mate una Actividad. Sin embargo, no es posible asegurar que el sistema invoque los mtodos
OnStop() y OnDestroy() porque se haya ejecutado onPause() y la Actividad haya acabado.
Por lo tanto, se debe utilizar el mtodo onPause() para guardar los datos importantes y
persistentes de la Actividad. No obstante, hay que ser selectivo sobre qu informacin debe
guardarse durante onPause(), ya que este mtodo bloquea el inicio de una nueva Actividad y
el usuario podra notar que el telfono se enlentece.
Los mtodos que se han marcado con "No" en la columna "Kill" estn, a priori,
protegidos. El sistema operativo slo los "mata" en una situacin de inestabilidad con falta de
memoria.
En la Unidad 3 veremos cmo usar esos eventos para guardar el estado de una
Actividad y poder recuperarlo cuando sta se reinicia.

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Actividades) de la Unidad 2. Estudia el


99

cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que hemos
utilizado los mtodos del ciclo de vida de una Actividad.

2.2

EVENTOS Y LISTENERS

2.2.1 Gestionando los eventos del usuario


Hasta ahora, en el curso, hemos creado elementos de la interfaz de usuario utilizando
los componentes disponibles en Android. En este apartado vamos a explicar cmo se gestiona
la interaccin del usuario sobre la interfaz de la aplicacin.
Como muchos otros entornos de desarrollo, Android est basado en Controladores
de Eventos (Event Handlers). Es decir, se ejecuta un determinado cdigo en respuesta a
algn evento que ocurra. Normalmente, estos eventos se activan cuando el usuario interacta
con la interfaz de la aplicacin.
En Android el programador puede capturar los eventos especficos del objeto Vista
(View) con la que el usuario interacta y ejecutar sentencias.
Por ejemplo, cuando el usuario toca con el dedo una Vista (por ejemplo, un botn), el
sistema operativo invoca el mtodo onTouchEvent() de ese objeto. Para interceptar este
mtodo deberamos extender (crear una nueva clase heredada del botn) la clase y reemplazar
el cdigo del mtodo de la clase original. Sin embargo, extender cada objeto Vista para
gestionar un evento no es prctico. Por esta razn, Android dispone en todas las clases View
de una coleccin de interfaces con funciones callback que se pueden utilizar con mucha ms
facilidad. Estas interfaces, que se denominan Escuchadores de eventos (Event listeners),
permiten controlar la interaccin del usuario con la interfaz de usuario.
Esto no quiere decir que no podamos extender una clase Vista para crear una clase
nueva que herede el comportamiento de la clase anterior y redefinir los eventos de la misma
directamente.

2.2.2 Uso de los Event Listeners


Un Event Listener es una interfaz de la clase Vista (View) que contiene un nico
mtodo de tipo callback. Android invoca estos mtodos cuando la Vista detecta que el usuario
est provocando un tipo concreto de interaccin con este elemento de la interfaz de usuario.
Existen los siguientes mtodos callback:
onClick(): de View.OnClickListener. Este mtodo se invoca cuando el usuario toca
un elemento con un dedo (modo contacto), hace clic con la bola de navegacin
(TrackBall) del dispositivo o presiona la tecla "Intro" estando en un objeto.
100

Diseo del interfaz de usuario


onLongClick(): de View.OnLongClickListener. Este mtodo se invoca cuando el
usuario toca y mantiene el dedo sobre un elemento (modo de contacto), hace clic sin
soltar con la bola de navegacin (TrackBall) del dispositivo o presiona la tecla "Intro"
durante un segundo estando en un elemento.
onFocusChange(): de View.OnFocusChangeListener. Se invoca cuando el usuario
mueve el cursor hacia una Vista o se aleja de sta utilizando la bola de navegacin
(Trackball) o usando las teclas de navegacin.
onKey(): de View.OnKeyListener. Se invoca cuando el usuario se centra en un
elemento y presiona o libera una tecla del dispositivo.
onTouch(): de View.OnTouchListener. Se invoca cuando el usuario realiza una
accin de tipo contacto con el dedo como presionar o soltar o cualquier gesto de
movimiento en la pantalla dentro de la Vista.
onCreateContextMenu(): de View.OnCreateContextMenuListener. Se invoca
cuando se crea un men contextual como resultado de una "pulsacin larga" sobre
un elemento. En la Unidad 3 veremos cmo se usa este tipo de men.

El siguiente ejemplo muestra cmo especificar los mtodos de los eventos sobre un
EditText:
// Definimos el evento Change del EditText
texto.addTextChangedListener(new TextWatcher() {

// Mtodo que se lanza antes de cambiar el texto

public void beforeTextChanged(CharSequence s, int start, int count,


int after) {
resultado1.setText("Texto antes de cambiar: "+s.toString());
}

// Mtodo que se lanza cuando el texto cambia

public void onTextChanged(CharSequence s, int start, int before,


int count) {
resultado2.setText("Texto cambiado: "+s.toString());
}

// Mtodo que se lanza cuando el texto cambia. La diferencia con el


// mtodo anterior es que la variable s es modificable

public void afterTextChanged(Editable s) {

// En este evento no hacemos nada

101

});// end onChange EditText

En el cdigo anterior fjate de qu manera se define el Listener de los cambios del


texto de una Vista de tipo EditText. Dentro de este Listener establecemos los mtodos que
vamos a gestionar (escuchar): beforeTextChanged, onTextChanged y afterTextChanged.
Tambin es posible definir un mtodo comn en toda la Actividad y asignarlo a las
Vistas en el fichero de diseo de la interfaz del usuario res\layout\main.xml as:

<!-- En el botn definimos el mtodo miClickHandler para indicar mediante el


diseo qu mtodo debe invocar Android cuando el usuario haga clic sobre
l -->
<Button android:id="@+id/boton" android:layout_height="wrap_content"
android:text="@string/calcular" android:onClick="miClickHandler"
android:layout_gravity="center_horizontal"
android:layout_width="104dp">
</Button>

A continuacin, se muestra la implementacin del mtodo anterior:


// Mtodo onClick que invoca el botn "Calcular"
// Este mtodo se define a nivel de Actividad y es comn a todas sus vistas
public void miClickHandler(View view) {

// Debemos ver la Vista (botn) que ha invocado el mtodo

switch (view.getId()) {

case R.id.boton: // Si se trata del botn "Calcular"


// Vemos qu tipo de clculo debemos hacer

RadioButton kilometrosButton = (RadioButton)


findViewById(R.id.radio0);

// Si no se ha escrito nada mostramos un mensaje de error

if (texto.getText().length() == 0) {
Toast.makeText(this, "Por favor, introduce un nmero",
Toast.LENGTH_LONG).show();

return;
}

102

Diseo del interfaz de usuario

// Definimos el formato del nmero resultante


// Es importante tener bien configurado el AVD para que el
// separador decimal sea la coma ","

DecimalFormat formatoNumero = new DecimalFormat("0.00");

// Obtenemos el n en formato float

float inputValue = Float.parseFloat(texto.getText().toString());

// Convertimos a la unidad correspondiente

if (kilometrosButton.isChecked()) {
distancia.setText("Kms son " +
formatoNumero.format(inputValue*0.6214) + " Millas");
} else {
distancia.setText("Millas son " +
formatoNumero.format(inputValue*1.6093) + " Kms");
}
break;

}// end switch

}// end miClickHandler

Fjate en que la funcin callback miClickHandler() no devuelve ningn resultado


(void); sin embargo, otros mtodos deben devolver un resultado lgico (boolean) para finalizar
su ejecucin. A continuacin vemos en qu consiste cada evento:
onLongClick(): este mtodo devuelve true para indicar que se han llevado a cabo
las operaciones necesarias para manejar el evento clic, por lo que ya no debe
lanzarse cualquier otro mtodo de tipo clic. En caso contrario, si el mtodo
devuelve el valor false, Android puede invocar a continuacin otro mtodo diferente
de tipo clic.
onKey(): este mtodo devuelve true o "false" para avisar si se han llevado a cabo
las operaciones necesarias para manejar el evento de teclado, por lo que ya no debe
lanzarse cualquier otro mtodo de tipo teclado.
onTouch(): en este mtodo ocurre como en los dos casos anteriores, segn devuelva
"true" o "false" para sealar si Android debe invocar los siguientes mtodos.

Recuerda que los eventos de tipo teclado siempre afectan a la Vista que est activa en
ese momento.

103

Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Eventos) de la Unidad 2. Estudia el


cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que hemos
utilizado varios mtodos de eventos de Android.

Si ejecutas el ejemplo, vers que la aplicacin tiene el aspecto siguiente:

2.2.3 Gestores de Eventos (Event Handlers)


En otro apartado de esta Unidad vamos a explicar cmo crear un componente (Vista)
personalizado. En este caso es posible redefinir varios mtodos de tipo callback utilizados
como controladores (handlers) de eventos. A continuacin vamos a hacer un resumen de los
mtodos que podemos redefinir en una clase heredada de una Vista de Android:

onKeyDown(int, KeyEvent): se invoca cuando el usuario usa el teclado del


dispositivo.

onKeyUp(int, KeyEvent): se invoca cuando el usuario suelta una tecla.

onTrackballEvent(MotionEvent): se invoca cuando el usuario utiliza la bola


de navegacin (trackball) del dispositivo.

104

Diseo del interfaz de usuario

onTouchEvent(MotionEvent): se invoca cuando el usuario toca con el dedo


sobre la pantalla del dispositivo.

onFocusChanged(boolean, int, Rect): se invoca cuando una Vista recibe o


pierde el foco del usuario.

Hay otros mtodos que, aunque no forman parte de la clase Vista, debes conocer, ya
que permiten gestionar eventos de otros componentes de Android. Son los siguientes:

Activity.dispatchTouchEvent(MotionEvent): permite que podamos gestionar


el evento que se invoca cuando el usuario toca la pantalla a nivel de Actividad
antes de que llegue a alguna Vista de la interfaz de usuario.

ViewGroup.onInterceptTouchEvent(MotionEvent):

permite

la

Vista

ViewGroup (es un conjunto de Layout) gestionar eventos antes de que se


remitan a los hijos que contiene.

ViewParent.requestDisallowInterceptTouchEvent(boolean): podemos usar


este mtodo para indicar que la Vista no debe interceptar los eventos de tipo
"toque" de pantalla.

2.2.4 Modo tctil de pantalla


Cuando un usuario interacciona con la interfaz de una aplicacin Android usando las
teclas del dispositivo o una bola de navegacin (trackball) es necesario marcar las Vistas como
activas (tienen el foco sobre ellas) colorendolas, para que el usuario sepa que puede
utilizarlas. Sin embargo, si el dispositivo tiene una pantalla tctil, el usuario puede interactuar
con la interfaz utilizando su propios dedos. En este ltimo caso, ya no es necesario resaltar las
Vistas. Este modo de interaccin se llama "modo tctil".
Cuando un usuario toca la pantalla tctil, el dispositivo cambia a modo tctil, de
manera que slo las Vistas en las que el mtodo isFocusableInTouchMode() devuelve "true"
pueden recibir el foco, como ocurre, por ejemplo, en los EditText. Otras Vistas, como los
botones, no pueden recibir el foco de la aplicacin, sino simplemente inician el mtodo clic
cuando se presiona sobre ellos.
Cada vez que un usuario pulsa una tecla o desplaza la bola de navegacin (trackball),
el dispositivo sale del modo tctil y busca una Vista donde activar de nuevo el foco de la
aplicacin. As el usuario puede volver a interactuar con la interfaz de usuario sin tocar la
pantalla.
El estado de modo tctil se mantiene a lo largo de todo el sistema operativo (todas las
ventanas y actividades). Para consultar el estado actual, se puede usar el mtodo
isInTouchMode(). De esta forma se puede saber si el dispositivo se encuentra en modo tctil.

105

2.2.5 Controlando la Vista con el foco activo


El sistema Android cambia la Vista activa (con foco) en respuesta a la interaccin del
usuario. Las Vistas indican la posibilidad de recibir el foco a travs del mtodo isFocusable().
Para establecer si una Vista puede recibir el foco, hay que utilizar el mtodo setFocusable().
Como hemos dicho, en el modo tctil, podemos usar el mtodo isFocusableInTouchMode() y
establecer si una Vista puede recibir el foco con setFocusableInTouchMode().
El cambio de foco automtico que hace Android se basa en un algoritmo que busca la
Vista vecina ms cercana en una direccin. Normalmente, este algoritmo establecido por
defecto no es el esperado por el usuario. Cuando esto ocurre, es posible definir explcitamente
en el archivo de diseo res\layout\main.xml cmo se debe cambiar el foco con los siguientes
atributos: nextFocusDown, nextFocusLeft, nextFocusRight y nextFocusUp indicando el id
de la Vista al que se debe saltar. Por ejemplo:

<LinearLayout
android:orientation="vertical"
... >
<Button android:id="@+id/arriba"
android:nextFocusUp="@+id/abajo"
... />
<Button android:id="@+id/abajo"
android:nextFocusDown="@+id/arriba"
... />
</LinearLayout>

Por lo general, en el diseo vertical anterior, intentar ir hacia arriba desde el primer
botn no cambia el foco de la aplicacin. Lo mismo ocurre con el segundo botn al intentar ir
hacia abajo. Sin embargo, al haber definido en el botn superior el nextFocusUp para pasar al
segundo botn, el foco cambia al botn de abajo y viceversa.
Si queremos indicar que una Vista que por defecto no recibe el foco de la aplicacin
pueda recibirlo, podemos usar el atributo XML de la Vista android:focusable al diseo la
interfaz de usuario. Tambin podemos usar el atributo android:focusableInTouchMode
cuando se trate del modo tctil.
Tambin desde el cdigo Java podemos usar el mtodo requestFocus() para indicar
al sistema que una Vista debe tener el foco.

2.3

USO DEL EMULADOR DE ANDROID

Como ya hemos visto, el entorno de desarrollo de Android incluye un emulador


virtual para poder ejecutar en un ordenador las aplicaciones que desarrollamos. El emulador
106

Diseo del interfaz de usuario

permite desarrollar y probar las aplicaciones de Android sin necesidad de disponer de un


dispositivo fsico. Este Emulador tiene esta apariencia:

El emulador de Android imita todas las caractersticas de hardware y software de un


dispositivo mvil fsico, exceptuando que no puede realizar llamadas a telfono reales.
Dispone de teclas de navegacin y control sobre las que se puede hacer clic con el ratn.
Tambin muestra una pantalla en la que aparece la aplicacin.
Para poder disear y probar aplicaciones con mayor facilidad, el emulador de Android
utiliza dispositivos virtuales (AVD: Android Virtual Device). Estos AVDs permiten establecer
algunos aspectos del hardware (memoria) y software (versin de Android) del telfono virtual.
Como el emulador es un sistema operativo completo de Android, la aplicacin que
desarrollamos puede utilizar los servicios, otras aplicaciones, acceder a la red, reproducir
audio y vdeo, almacenar y recuperar datos, hacer notificaciones, etctera.
Adems, el emulador incluye la capacidad de depuracin y de simulacin de
interrupciones de las aplicaciones (por ejemplo, cuando llega un mensaje SMS o una llamada
telefnica) para simular los efectos en estado de latencia.
En la Unidad 1 ya hemos visto cmo se inicia este emulador de Android; ahora vamos
a estudiar cmo se utiliza.
107

NOTA: es importante que el dispositivo virtual se encuentre configurado en el idioma Espaol


para que todos los ejemplos del curso funcionen correctamente. Se explica cmo hacerlo en
el apartado Cmo crear un proyecto Android de la Unidad 1.

2.3.1 Teclado del emulador


Se puede interactuar con el emulador como si se tratara de un dispositivo Android
real. Para usar el modo tctil de la pantalla hay que usar el puntero del ratn y para escribir
con las teclas del dispositivo hay que utilizar las teclas que aparecen a la derecha del
emulador.
Cuando accedemos por primera vez al emulador, aparece la pantalla de bienvenida del
terminal bloqueado:

Para desbloquear la pantalla hay que arrastrar con el ratn el icono "candado" hacia la
derecha.
La tabla siguiente resume las relaciones entre las teclas del emulador y las teclas del
teclado de tu ordenador:

Tecla del Emulador

Inicio
108

Tecla del ordenador


INICIO

Funcin
Se vuelve a la pantalla

Diseo del interfaz de usuario

principal de Android.

Men (tecla izquierda)

Volver

Llamar

Colgar

Buscar

Botn encender/apagar

F2 o RePg

ESC

F3

F4

F5

BLOQUE_NUM_MAS (+),
Ctrl+5

Bajar volumen

BLOQUE_NUM_MENOS (-)
Ctrl+F6

Cambio orientacin

Conexin datos

Pantalla completa

Bola navegacin

Entra momentneamente en
modo bola navegacin.

Vuelve a la pantalla anterior.

Llamar por telfono.

Acabar una llamada.

Buscar informacin.

F7

Subir volumen

Cmara

Muestra un men
desplegable con las
opciones de la pantalla
actual.

Ctrl+BLOQUE_NUM_5, Ctrl+F3 Inicia la cmara de fotos.


BLOQUE_NUM_7,
Ctrl+F11
BLOQUE_NUM_9,
Ctrl+F12

F8

Alt+Intro

F6

Suprimir

Cambia la orientacin del


mvil de horizontal
(landscape) a vertical
(portrait).
Habilita y deshabilita la
conexin de datos del
mvil.

El emulador ocupa la
pantalla completa del
monitor del ordenador.
Inicia el modo de
navegacin con bola
(trackball)
Mientras tengamos pulsada
la tecla Suprimir

109

Arrastrar izq/arriba/dcha/abajo

BLOQUE_NUM_4/8/6/2

Ten en cuenta que para usar las teclas del bloque numrico es necesario desactivar el
bloqueo numrico en tu ordenador.
NOTA: es recomendable familiarizarse con el emulador de Android usando las funciones del
dispositivo virtual e ir visitando las diferentes opciones del mismo as como utilizar los atajos
de teclado de la tabla anterior.

2.3.2 Cmo introducir tildes con el Teclado del Emulador


Para que se puedan introducir tildes en las cajas de texto de las aplicaciones es muy
importante que el dispositivo virtual se encuentre configurado en el idioma Espaol. En el
apartado Cmo crear un proyecto Android de la Unidad 1 se explica cmo hacerlo.
Para que podamos introducir tildes en las cajas de texto, hay que pulsar un rato con el
ratn en la vocal correspondiente:

Pulsar un rato

Tambin podemos usar de la misma forma el teclado externo del emulador:

110

Diseo del interfaz de usuario

Pulsar un rato

Atencin: tambin es posible pulsar un rato en la tecla de la vocal del teclado del ordenador
donde ests trabajando para poder escribir la tilde correspondiente.

2.3.3 Limitaciones del Emulador

No se pueden enviar ni recibir llamadas de telfono reales, aunque se pueden simular.

No se pueden usar conexiones por USB.

No se puede utilizar la cmara de fotos/vdeo.

No se pueden conectar unos auriculares al dispositivo virtual.

No se puede conectar a una red de telefona.

No se puede establecer la carga de la batera.

No se puede detectar cundo se inserta o se quita la tarjeta de memoria SD.

No se pueden establecer conexiones por Bluetooth.

2.3.4 Tamao ventana emulador


Es posible cambiar el tamao de la ventana de AVD del Emulador para que se vea
correctamente en la pantalla de tu ordenador. Para ellos hacemos clic en el men de Eclipse
Android SDK and AVD Manager:

111

Despus, hacemos clic en el botn Start:

En esta ventana podemos ver la escala (Scale) que se aplicar a la ventana del
emulador (un nmero entre 0,1 y 3). Para ello, podemos especificar la densidad del monitor del
ordenador en puntos por pulgada (Monitor DPI) y el tamao en pulgadas de la pantalla del
dispositivo Android (Screen Size).
Para arrancar el emulador basta con hacer clic en el botn Launch.

2.3.5 Otras opciones del Emulador


Aunque el Emulador tiene muchas posibles opciones configurables para simular casi
todos los casos posibles de un dispositivo real, vamos a describir las que son ms
importantes o usaremos ms adelante en el curso:

Geo Localizacin: la localizacin geogrfica por GPS es un servicio muy til


en los dispositivos de Android.

112

Diseo del interfaz de usuario

Encendido / Apagado dispositivo: es posible simular el encendido, apagado


y bloqueo de la pantalla del telfono. Incluso se puede simular el estado de la
batera del mvil: cargando/descargando.

Llamada de telfono: el emulador de Android incluye un mdem virtual GSM


que permite simular las funciones de telefona. Por ejemplo, se pueden simular
llamadas entrantes del telfono y establecer conexiones de datos. Lo que no
podemos usar es el audio en esta versin del Emulador.

Mensajes cortos SMS: se pueden crear mensajes SMS y dirigirlos al


dispositivo virtual de Android. Es decir, se simula la recepcin de mensajes
cortos.

2.3.6 Cmo configurar las opciones del Emulador


En Eclipse se incluye una herramienta para depurar de aplicaciones Android que se
llama "Dalvik Debug Monitor Server" (DDMS). DDMS funciona tanto con un emulador de
Android como con un telfono real conectado por cable USB. Esta herramienta tambin se
puede utilizar para configurar alguna de las opciones anteriores.
Para acceder a esta herramienta en Eclipse hay que hacer clic en la opcin del men
principal: Window -> Open Perspective -> DDMS.
Una vez abierta la perspectiva de DDMS, en la pestaa "Emulator Control", podemos
encontrar los campos necesarios para configurar estas opciones:

113

Nota: no todas las opciones posibles del Emulador estn incluidas en la ventana anterior;
algunas deben configurarse mediante rdenes en la lnea de comandos del sistema operativo
de tu ordenador.

En la Unidad 8 veremos cmo usar esta ventana para depurar aplicaciones Android.

2.4

COMPONENTES AVANZADOS

En la Unidad 1 ya hemos indicado que la interfaz de usuario en Android se construye


usando componentes o Vistas que estn contenidas en paneles de diseo (layout). Los
componentes permiten interaccionar con el usuario con la aplicacin. Los paneles ordenan la
posicin de estos elementos en la interfaz del usuario.
Android dispone de diversos componentes que permiten al usuario seleccionar una
opcin dentro de una lista de posibilidades, tales como listas desplegables (Spinner), listas
fijas (ListView), tablas (GridView). Adems, existen otros componentes especficos de la
plataforma, como las galeras de imgenes (Gallery).
Este apartado est dedicado a los componentes de tipo seleccin y vamos a describir
un elemento importante y comn a todos ellos: los adaptadores.

2.4.1 Qu son los Adaptadores de Android (adapters)


Un Adaptador (Adapter) es un objeto que permite definir el modelo de datos que usan
todos los componentes de seleccin de forma unificada. Es decir, todos los componentes de
seleccin acceden a los datos que contienen a travs de un adaptador.
El Adaptador, adems de suministrar datos a los componentes visuales, tambin es
responsable de generar las vistas especficas que se muestran dentro del componente de
seleccin. Por ejemplo, si cada opcin de una lista estuviera formada por una imagen y varias
etiquetas, el Adaptador se encarga de generar el contenido de todas estas opciones
diseadas.
Android dispone de varios tipos de adaptadores sencillos, aunque es posible extender
fcilmente mediante herencia su funcionalidad para mejorarlos segn las necesidades de la
aplicacin. Los ms comunes son los siguientes:

ArrayAdapter: es el ms sencillo de todos los adaptadores. Suministra datos


a un componente de seleccin mediante una matriz de objetos de cualquier
tipo.

114

Diseo del interfaz de usuario

SimpleAdapter: se usa para definir las opciones de un componente de


seleccin con los datos de un fichero XML.

SimpleCursorAdapter: se utiliza para obtener las opciones de un componente


de seleccin de la respuesta de una consulta a una base de datos.

A continuacin vemos cmo crear un Adaptador de tipo ArrayAdapter:

//Matriz con las opciones del Spinner


final String[] datos = new String[]{"Opcin 1","Opcin 2","Opcin 3",
"Opcin 4","Opcin 5"};
// Adaptador que usamos para indicar al Spinner dnde obtiene las opciones
ArrayAdapter<String> adaptador =
new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, datos);

Para crear el adaptador usamos tres parmetros:


1. El contexto se refiere normalmente a la actividad donde se crea el adaptador.
2. El ID de la Vista que va a mostrar la opcin seleccionada por el usuario. En
este

caso,

usamos

un

ID

de

diseo

predefinido

por

Android

(android.R.layout.simple_spinner_item) y formado por un componente


TextView. Podramos haber escrito cualquier ID de un componente de la
interfaz de usuario del proyecto.
3. La matriz de los datos que definen las opciones de la lista.
De esta forma, ya hemos creado el adaptador para mostrar las opciones
seleccionables y slo hay que asignarlo al componente de seleccin adecuado.

Lista desplegable (Spinner)


El componente Spinner permite al usuario seleccionar una nica opcin de un listado
desplegable. En el ejemplo siguiente definimos una Lista desplegable:
Ejemplo

<Spinnerandroid:id="@+id/ListadoOpciones"
android:prompt="@string/selecciona"
android:drawSelectorOnTop="true"
115

android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

Como en otros componentes de Android, podemos utilizar otras propiedades como el


color de fondo (android:background), el estilo de la fuente (android:typeface), el color de
fuente (android:textcolor), el tamao de la fuente (android:textSize), etctera.
Para enlazar el adaptador que define las opciones de este tipo de listado y tratar el
evento que ocurre cuando el usuario selecciona una de las opciones disponibles, escribimos
las siguientes sentencias:

//Indicamos el tipo de Spinner dropdown


adaptador.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);
// Establecemos el adaptador en el Spinner
listadoOpciones.setAdapter(adaptador);

// Definimos el evento setOnItemSelected


listadoOpciones.setOnItemSelectedListener(
new AdapterView.OnItemSelectedListener() {
@Override

//
Si
se
selecciona
una
// actualizamos la etiqueta

opcin,

mostramos

un

mensaje

public void onItemSelected(AdapterView<?> adapterView, View view,


int position, long id) {
Toast.makeText(getBaseContext(), datos[position], 1).show();
resultado.setText("Opcin seleccionada: " + (position+1));
}

// Si no se selecciona nada limpiamos la etiqueta. En este tipo de


// componentes siempre se selecciona una opcin por lo que no se ver
// este evento.
public void onNothingSelected(AdapterView<?> adapterView) {
resultado.setText("");
}

});

116

Diseo del interfaz de usuario

Para cambiar el aspecto de las opciones de la lista emergente hay que usar el mtodo
setDropDownViewResource(ID_layout). En este caso hemos utilizado el diseo predefinido
de Android para las listas desplegables android.R.layout.simple_spinner_dropdown_item.
Esto provoca que el diseo de la seleccin y el listado desplegable sean diferentes (fjate en el
cambio de color):

El evento de una Lista desplegable normalmente es onItemSelected, que se invoca


cuando el usuario selecciona una opcin de la lista. El manejo del evento es similar a otros
componentes usando el mtodo setOnItemSelectedListener(). En este evento definimos dos
mtodos: el primero de ellos es onItemSelected y se invoca cada vez que cambia la
seleccin de la lista desplegable; y el segundo es onNothingSelected, que se invoca cuando
no hay ninguna opcin seleccionada (esto ocurre si el adaptador no tiene datos).

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Lista desplegable) de la Unidad 2.


Estudia el cdigo fuente y ejectalo para mostrar en el emulador el resultado del programa
anterior

117

Lista de seleccin (List View)

La Lista de seleccin (o ListView en ingls) permite al usuario hacer clic sobre una lista
de opciones seleccionables. Estas opciones se muestran directamente sobre el propio
componente; por lo tanto, no se trata de una lista emergente como el Spinner. Si no se
visualizan todas las opciones disponibles en la pantalla del dispositivo porque no caben, se
puede desplazar el listado con el dedo o los botones de navegacin.
En el ejemplo siguiente vamos a usar un Adaptador particularizado para que dibuje las
opciones del men personalizadas.
Fjate en el cdigo del siguiente ejemplo que define un ListView en el fichero main.xml
de layout de la aplicacin:

Ejemplo

<ListView android:id="@+id/ListaOpciones"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

Para enlazar el adaptador que define las opciones de este tipo de listado y tratar el
evento que ocurre cuando el usuario selecciona una de las opciones disponibles, escribimos
las siguientes sentencias:
// En este caso definimos la matriz de opciones usando la clase Opcion
private Opcion[] datos = new Opcion[32];

...
// Definimos 32 Opciones en el ListView
for(int i=1; i<=32; i++)
datos[i-1] = new Opcion("Opcin " + i, "sta es la opcin " + i);

// Usamos un adaptador para dibujar las opciones de la lista


AdaptadorOpciones adaptador = new AdaptadorOpciones(this);
// Establecemos el adaptador del Listview
listaOpciones.setAdapter(adaptador);

118

Diseo del interfaz de usuario

// Definimos el evento setOnItemClick


listaOpciones.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override

// Si se hace clic sobre una opcin, mostramos un mensaje


public void onItemClick(AdapterView<?> adapterView, View view,
int position, long id) {
Toast.makeText(getBaseContext(), "Has hecho clic en '" +
datos[position].getTitulo()+"'", 1).show();

}
});

Fjate

que

en

este

caso

hemos

utilizado

un

nuevo

tipo

de

Adaptador:

AdaptadorOpciones, que personaliza el diseo de las opciones de la lista. Este Adaptador se


define con el siguiente cdigo:
// Definimos el Adaptador que dibuja la opciones del ListView

class AdaptadorOpciones extends ArrayAdapter<Opcion> {


Activity contexto;

// Contructor del adaptador usando el contexto de la aplicacin actual

AdaptadorOpciones(Activity contexto) {

// Llamamos al constructor de la clase superior


super(contexto, R.layout.listitem_opcion, datos);
this.contexto = contexto;

}
// Mtodo que dibuja la Vista de cada Opcin
// Se invoca cada vez que haya que mostrar un elemento de la lista.

public View getView(int position, View convertView, ViewGroup parent)


{

// Vista que Android indica como reutilizable


View item = convertView;
// Esta variable se usa para almacenar un objeto dentro
// de la Vista que dibuja la opcin

119

VistaTag vistaTag;
// Si Android indica que no hay una Vista reutilizable para
//la opcin, la definimos, inflamos el diseo que se define
// en el fichero listitem_opcion.xml y establecemos su contenido
If (item == null)
{
// Usamos un Inflater para inflar el diseo
// Ahora tenemos una Vista que se asocia al elemento
LayoutInflater inflater = contexto.getLayoutInflater();
// Definimos en la vista de vuelta el tipo de diseo
item = inflater.inflate(R.layout.listitem_opcion, null);
// Definimos el objeto que vamos a almacenar en el
//nuevo elemento
vistaTag = new VistaTag();
// Obtenemos los punteros a las etiquetas recin infladas
vistaTag.titulo = (TextView)item.findViewById(R.id.LblTitulo);
vistaTag.subtitulo =
(TextView)item.findViewById(R.id.LblSubTitulo);
// Guardamos el objeto en el elemento
item.setTag(vistaTag);
}
else
{
// Si estamos reutilizando una Vista, recuperamos el
// objeto interno
vistaTag = (VistaTag)item.getTag();
}
// Cargamos las opciones de la matriz de datos
vistaTag.titulo.setText(datos[position].getTitulo());
vistaTag.subtitulo.setText(datos[position].getSubtitulo());
// Devolvemos la Vista (nueva o reutilizada) que dibuja
// la opcin

return(item);
}

} // end class AdaptadorOpciones

120

Diseo del interfaz de usuario

El mtodo ms importante de un Adaptador es getView() ya que es el encargado de


mostrar los elementos de la lista.
Lo primero que debe hacer este mtodo es inflar el diseo layout XML que hemos
definido en el fichero res\layout\listitem_opcion.xml. Para ello, hemos utilizando la clase
LayoutInflater, que crea la estructura de diseo de los objetos mediante el mtodo
inflate(id_layout). Cada vez que es necesario mostrar un elemento de la lista en la pantalla,
Android invoca este mtodo para disearlo, incluso cuando ya se ha mostrado el elemento y
se ha ocultado en la pantalla al desplazar la lista.
Esto produce que, dependiendo del tamao de la lista y de la complejidad del layout,
se creen y destruyan muchos objetos aumentando el uso de la CPU y dela memoria y, al final,
provocando un mayor consumo de batera.
Sin embargo, Android permite reutilizar una Vista que ya hayamos inflado con
anterioridad y que ya no haga falta por algn motivo; por ejemplo, porque el elemento
correspondiente de la lista haya desaparecido de la pantalla al desplazar el listado. As, se
crean nicamente los objetos necesarios que se pueden visualizar en la pantalla del telfono.
Adems, todos los componentes de Android tienen una propiedad denominada Tag,
que puede almacenar dentro cualquier tipo de objeto. Para asignar y recuperar el objeto
almacenado hay que emplear los mtodos setTag() y getTag() respectivamente.
Esta facilidad de los objetos de Android permite que almacenemos dentro cada Vista
convertView las etiquetas que dibujan el diseo de la opcin del listado. Para ello, vamos a
definir una nueva clase VistaTag donde almacenamos las etiquetas TextView que hemos
inflado para dicho elemento, de forma que, posteriormente, podamos recuperarlo
fcilmente y cambiar su contenido.
Por lo tanto, la clase VistaTag slo contiene una referencia a cada uno de los
componentes del diseo que hay que manipular, en este caso, las dos etiquetas de texto.
Definimos esta clase de la siguiente forma:
// Clase que se
// de una opcin

usa

para

almacenar

las

etiquetas

de

tipo

TextView

static class VistaTag {


TextView titulo;
TextView subtitulo;
}

121

Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Lista de seleccin) de la Unidad 2.


Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior,
en el que hemos utilizado el componente ListView.

Lista de rejilla (Grid View)

La Lista de rejilla (o GridView en ingls) permite mostrar al usuario un conjunto de


opciones seleccionables distribuidas como una tabla dividida en filas y columnas. Sus
propiedades ms importantes son:

android:numColumns: establece el nmero de columnas de la tabla.


Tambin podemos escribir auto_fit si queremos que este nmero sea
calculado por el propio sistema operativo.

android:columnWidth: establece el ancho de las columnas de la tabla.

android:horizontalSpacing: establece el espacio horizontal entre celdas.

android:verticalSpacing: establece el espacio vertical entre celdas.

android:stretchMode: establece qu hacer con el espacio horizontal


sobrante. Si se fija el valor columnWidth, este espacio ser ocupado a
partes iguales por las columnas de la tabla y si se fija el valor
spacingWidth, ser ocupado a partes iguales por los espacios entre
celdas.

Fjate en el cdigo de ejemplo que define un GridView en el fichero main.xml de


layout de la aplicacin:

Ejemplo

<GridView android:id="@+id/GridOpciones"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:numColumns="auto_fit"
android:columnWidth="80px"
android:horizontalSpacing="5px"
android:verticalSpacing="10px"
android:stretchMode="columnWidth" />

Como en los anteriores ejemplos, para enlazar el adaptador que define las opciones de
este tipo de listado escribimos las siguientes sentencias:
122

Diseo del interfaz de usuario

ArrayAdapter<String> adaptador =
new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, datos);

final GridView gridOpciones = (GridView)findViewById(R.id.GridOpciones);


gridOpciones.setAdapter(adaptador);

Adems, tambin es posible gestionar el evento que ocurre cuando el usuario


selecciona una de las opciones del listado.

Cuadro de texto con sugerencias (AutoComplete TextView)

El Cuadro de texto con sugerencias (o AutoComplet TextView en ingls) permite al


usuario la introduccin y edicin de texto para que lo vea el usuario. Mientras el usuario
escribe texto, este componente propone posibles frases. Su propiedad ms importante es
android:completionThreshold, que indica el nmero de caracteres mnimo que debe escribir
el usuario para que se propongan sugerencias.
Fjate en el cdigo de ejemplo que define un AutoCompleteTextView en el fichero
main.xml de layout de la aplicacin:

Ejemplo

<AutoCompleteTextView android:id="@+id/miautocomplete"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
/>

Como en los anteriores ejemplos, para enlazar el adaptador que define las opciones de
este tipo de listado escribimos las siguientes sentencias:

123

// Debemos implemetar los mtodo de TextWatcher para poder detectar los


// eventos de abajo
public class mesesActivity extends Activity implements TextWatcher {
private AutoCompleteTextView miAutoComplete;
@Override
public void onCreate(Bundle savedInstanceState) {
String meses[]={
"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio",
"Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"};

...
miAutoComplete =
(AutoCompleteTextView)findViewById(R.id.miautocomplete);

ArrayAdapter<String> adaptador = new ArrayAdapter<String>(this,


android.R.layout.simple_dropdown_item_1line, meses);

miAutoComplete.addTextChangedListener(this);
miAutoComplete.setAdapter(adaptador);

}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {

// Aqu escribimos el cdigo que se ejecuta antes de que el texto cambie


}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{

// Aqu escribimos
cambiado
}

124

el

cdigo

que

se

ejecuta

cuando

el

texto

ha

Diseo del interfaz de usuario

@Override
public void afterTextChanged(Editable s) {

// Aqu escribimos el cdigo que se ejecuta despus de que el texto


cambie
}

Adems, dispone de las mismas propiedades y eventos que un componente EditText.

Actividad con lista de seleccin (List Activity)

Una Actividad con lista de seleccin (en ingls, ListActivity) es una actividad heredada
de la clase Activity que ya incluye el componente de seleccin ListView.
El componente ListActivity tiene un diseo predeterminado que consiste en una lista
de seleccin que ocupa toda la pantalla del dispositivo. Sin embargo, es posible personalizar
el diseo de esta lista usando la funcin setContentView() en el evento OnCreate() de la
Actividad. Para poder redisear esta lista de seleccin es obligatorio que, en el fichero
main.xml de layout de la aplicacin, se defina el componente ListView con el id
@+id/android:list.
Opcionalmente, la pantalla de la aplicacin puede contener otro objeto que se mostrar
cuando la lista de seleccin est vaca. Este componente que se muestra cuando la lista est
vaca debe tener el id android:id/empty.
El cdigo siguiente muestra un diseo personalizado poco agraciado de la pantalla.
Tiene una lista con un fondo verde, rojo y un escueto mensaje "sin datos".
Fjate en el cdigo de ejemplo que define este componente en el fichero main.xml de
layout de la aplicacin:
Ejemplo

<ListView
android:id="@+id/android:list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

125

<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0000"
android:text="No hay datos"/>

Para cambiar el diseo de las opciones del ListView interno de la actividad ListActivity
hay que definir un nuevo fichero XML de diseo dentro de la carpeta res\layout. Por ejemplo,
el fichero se puede llamar fila.xml y contener el siguiente cdigo:

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView android:id="@+id/texto"
android:textSize="16sp"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

El cdigo anterior crea un diseo de una etiqueta en negrita para definir cada opcin
del listado.
Para acabar, vamos a cargar datos en el objeto ListView de la actividad ListActivity
usando un Adaptador de tipo matriz en el fichero Java correspondiente de la aplicacin:

public class MiListado extends ListActivity {


@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

126

Diseo del interfaz de usuario

// Creamos una matriz de datos que usaremos en la ListActivity


String[] nombres = new String[] { "Firefox", "Internet Explorer",
"Opera", "Chrome", "Safari" };

// Definimos el adaptador del ListView contenido en el ListActivity.


// Hay que indicar el diseo de la opcin (R.layout.fila) y el campo
// que debemos rellenar (R.id.texto)

this.setListAdapter(new ArrayAdapter<String>(this, R.layout.fila,


R.id.texto, nombres));
}
}

En el ejemplo anterior podramos haber utilizado un matriz de objetos en lugar de


cadena y extender el Adaptador para dibujar las opciones del listado con el mtodo
getView(), tal y como hemos visto en el Ejemplo 4 de esta Unidad.

2.5

COMPONENTES PERSONALIZADOS

2.5.1 Diseo de componentes personalizados


Hasta ahora en el curso hemos estudiado los componentes bsicos que proporciona
Android. Usando estos componentes podemos disear interfaces de usuario. A veces, la
funcionalidad de la aplicacin requiere emplear componentes diseados por el programador.
Como en muchos lenguajes de programacin, Android permite crear componentes
personalizados de las siguientes maneras:
1. Extendiendo la funcionalidad de un componente ya existente.
2. Combinando varios componentes para formar otro compuesto.
3. Diseando desde cero un nuevo componente.
A continuacin, vamos a ver las formas 1 y 2 de crear componentes personalizados.

2.5.2 Cmo crear un componente extendido


A continuacin, vamos a ver cmo podemos crear un nuevo componente usando un
componente bsico ya existente de Android. A modo de ejemplo, vamos a extender la Vista

127

EditText (cuadro de texto) para que muestre el nmero de caracteres que contiene a medida
que se escribe en l.
Se trata de simular un editor de mensajes cortos SMS del propio sistema operativo que
nos avisa del nmero de caracteres que contiene el mensaje. En nuestro caso, como resultado
obtendremos un componente como el que se muestra en la siguiente imagen:

Podemos observar en la esquina superior derecha del cuadro de texto el nmero de


caracteres del mensaje de texto introducido, que se actualiza a medida que modificamos el
texto.
Lo primero que hay que hacer es crear una nueva clase Java que extienda el
componente que utilizamos de partida como base, en este caso EditText. El cdigo de esta
nueva subclase es ste:

//El componente EditTextExtendido se extiende de EditText


public class EditTextExtendido extends EditText
{
private Paint pNegro;
private Paint pBlanco;

// Hay que reescribiremos siempre los constructores heredados.


// En este caso son tres
public EditTextExtendido(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
pinceles();
}

public EditTextExtendido(Context context, AttributeSet attrs) {


super(context, attrs);

128

Diseo del interfaz de usuario

pinceles();
}
public EditTextExtendido(Context context) {
super(context);
pinceles();
}

// Funcin que inicia las pinceles que usamos para pintar el cuadradito negro
private void pinceles()
{
pNegro = new Paint(Paint.ANTI_ALIAS_FLAG);
pNegro.setColor(Color.BLACK);
pNegro.setStyle(Style.FILL);
pBlanco = new Paint(Paint.ANTI_ALIAS_FLAG);
pBlanco.setColor(Color.WHITE);
}

// Para modificar el aspecto del EditText hay que reescribir este mtodo
@Override
public void onDraw(Canvas canvas)
{

//Invocamos al mtodo de la superclase (EditText)

super.onDraw(canvas);

//Dibujamos el fondo negro del contador en la parte de arriba derecha

canvas.drawRect(this.getWidth()-30, 5, this.getWidth()-5, 20, pNegro);

//Dibujamos el nmero de caracteres sobre el contador


canvas.drawText("" + this.getText().toString().length(),
this.getWidth()-28, 17, pBlanco);
}

Es importante reescribir siempre los tres constructores heredados.


Para modificar el aspecto del componente incluyendo el contador de caracteres
tendremos hay que reescribir el mtodo onDraw() que Android invoca cada vez que hay que
dibujar el componente en la pantalla del dispositivo. Este mtodo tiene el parmetro Canvas
129

que es el lienzo sobre el que podemos dibujar todos los elementos extra necesarios en el
componente.
La clase Canvas proporciona varios mtodos para dibujar lneas, rectngulos, elipses,
texto, imgenes, etctera, sobre el espacio ocupado por el componente. En este caso
nicamente vamos a dibujar un rectngulo que sirve de fondo para el contador y el texto con
el nmero de caracteres actual que ha escrito el usuario.
Para dibujar el grfico es necesario definir dos pinceles (clase Paint). El primero
permite pintar de color negro y con relleno slido, y el segundo pinta de color blanco. Como
slo es necesario crear estos pinceles una vez, los hemos definido como atributos de la clase
y los inicializamos en los tres constructores del componente.
Finalmente, dibujamos el fondo y el texto del contador mediante los mtodos
drawRect() y drawText()del objeto Canvas usando posiciones relativas al espacio ocupado
por el componente.
Para aadir el nuevo componente a la interfaz de nuestra aplicacin hay que incluirlo en
fichero res\layout\main.xml que define el diseo de la ventana como cualquier otro
componente, teniendo en cuenta que debemos hacer referencia a l con el nombre completo
de la nueva clase creada: es.mentor.unidad2.eje5.edittextext.EditTextExtendido. El fichero
tiene este aspecto:
<es.mentor.unidad2.eje5.edittextext.EditTextExtendido
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:layout_weight="0.25"/>

Desde Eclipse puedes abrir el proyecto Ejemplo 5 (EditText Extendido) de la Unidad 2.


Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa
anterior, en el que hemos utilizado un componente extendido.

2.5.3 Cmo combinar varios componentes para crear uno compuesto


Android permite la creacin de componentes compuestos a partir de varios
componentes estndar combinando la funcionalidad de todos ellos en un nico componente
reutilizable en otras aplicaciones.

130

Diseo del interfaz de usuario

A modo de ejemplo, vamos a crear un componente de identificacin (login) formado por


varios componentes estndar de Android. Adems, en este componente compuesto hemos
definido un nuevo evento personalizado.
Lo primero que hemos hecho es disear la interfaz del componente compuesto a partir
de componentes estndar de Android: etiquetas, cuadros de texto y un botn. Para ello,
definimos

un

nuevo

layout

XML

en

la

carpeta

\res\layout

con

el

nombre

componente_login.xml. En este fichero vamos a establecer la estructura tpica de una


pantalla que muestra una ventana de login. El fichero es el siguiente:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" android:padding="10dip">

<TextView android:id="@+id/TextView01" android:layout_width="wrap_content"


android:layout_height="wrap_content" android:text="Usuario:"
android:textStyle="bold" />
<EditText android:id="@+id/TextoUsuario"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
<TextView android:id="@+id/TextView02" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Contrasea:"
android:textStyle="bold" />
<EditText android:id="@+id/TextoPassword"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
<Button android:id="@+id/BotonAceptar" android:paddingLeft="20dip"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="Entrar" android:paddingRight="20dip"
android:layout_width="104dp"></Button>
<TextView android:id="@+id/LabelMensaje" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:paddingLeft="10dip"
android:textStyle="bold" />

</LinearLayout>

131

A continuacin, creamos la clase Java asociada a este componente compuesto donde


se define toda su funcionalidad. Como el diseo est basado en la clase LinearLayout, el
nuevo componente debe heredar tambin esta clase Java de Android. Redefiniremos adems
los dos constructores bsicos:

public class ComponenteLogin extends LinearLayout


{

// Componentes que forman el componente compuesto


private EditText textoUsuario;
private EditText textoPassword;
private Button botonLogin;
private TextView labelMensaje;

// Evento que se invoca cuando el usuario pulsa el botn Entrar


private OnLoginListener listener;
// Constructor por defecto
public ComponenteLogin(Context context) {
super(context);
inicializar();
}

// Constructor que define el texto del botn de acceso


public ComponenteLogin(Context context, AttributeSet attrs) {
super(context, attrs);
inicializar();

// Procesamos los atributos XML personalizados


TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.ComponenteLogin);
String textoBoton = a.getString(R.styleable.ComponenteLogin_texto_boton);

// Escribimos el testo del botn


botonLogin.setText(textoBoton);
// Liberamos memoria
a.recycle();
}

132

Diseo del interfaz de usuario

// Este mtodo se usa para dibujar (inflar) el componente compuesto


private void inicializar()
{

//

Utilizamos el diseo layout 'componente_login'


// componente. Primero obtener el inflater

como

interfaz

del

String infService = Context.LAYOUT_INFLATER_SERVICE;


LayoutInflater li =
(LayoutInflater)getContext().getSystemService(infService);

// Inflamos el componente compuesto definido en el XML


li.inflate(R.layout.componente_login, this, true);
// Obtenemos las referencias a los distintos componentes internos
textoUsuario = (EditText)findViewById(R.id.TextoUsuario);
textoPassword = (EditText)findViewById(R.id.TextoPassword);
botonLogin = (Button)findViewById(R.id.BotonAceptar);
labelMensaje = (TextView)findViewById(R.id.LabelMensaje);

//Asociamos los eventos necesarios


asignarEventos();
}

// Establece el listener del componente compuesto


public void setOnLoginListener(OnLoginListener l)
{
listener = l;
}

// Define un nuevo evento para el componente compuesto


private void asignarEventos()
{

// Cuando el usuario hace clic sobre el botn Entrar


// entonces se lanza el evento onLogin con los datos
// de las cajas de texto
botonLogin.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v) {

133

listener.onLogin(textoUsuario.getText().toString(),
textoPassword.getText().toString());
}
});
}

// Permite escribir la etiqueta con el resultado del Login


public void setMensaje(String msg)
{
labelMensaje.setText(msg);
}
}

Como se puede observar en las sentencias anteriores, el mtodo inicializar() es el


encargado de "inflar" el diseo XML que hemos definido, de obtener las referencias a todos
los componentes internos y deasignar el evento necesario. Adems, se incluye tambin un
mtodo que permite modificar el texto de la etiqueta que muestra un mensaje con el resultado
del login.
Finalmente, hemos implementado un evento que permite responder cuando el usuario
de la aplicacin hace clic en el botn "Entrar". Para concretar los detalles de dicho evento,
definimos una interfaz Java con su listener. Esta interfaz slo tiene el mtodo onLogin() que
devuelve los dos datos introducidos por el usuario (usuario y contrasea). La interfaz es muy
sencilla:

public interface OnLoginListener


{
void onLogin(String usuario, String password);
}

Una interfaz en Java es una coleccin de mtodos abstractos y propiedades. En ella se


especifica qu se debe hacer, pero no su implementacin. En la clase de la Actividad es
donde vamos a implementar esta interfaz que describe la lgica del comportamiento de los
mtodos:

134

Diseo del interfaz de usuario

//Definimos lo que hace el evento onLogin


compLogin.setOnLoginListener(new OnLoginListener()
{
@Override
public void onLogin(String usuario, String password)
{

//Validamos el usuario y la contrasea


if (usuario.equals("admin") && password.equals("admin"))
compLogin.setMensaje("El usuario es correcto!");
else
compLogin.setMensaje("Error:

el

usuario

la

contrasea no son
correctos.");

}
});

Ahora ya podemos definir el nuevo componente en una actividad como si se tratase de


cualquier otro componente predefinido por Android. En el fichero main.xml que define la
interfaz de usuario basta con hacer referencia a l utilizando la ruta completa del paquete
Java, que en este caso sera es.mentor.unidad2.eje6.logincompuesto:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sgo="http://schemas.android.com/apk/res/es.mentor.unidad2.eje6.logincomp
uesto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip" >

<es.mentor.unidad2.eje6.logincompuesto.ComponenteLogin
android:id="@+id/ComponenteLogin"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#0000AA"

135

sgo:texto_boton="Entrar" />
</LinearLayout>

Como el componente compuesto es heredado de un LinearLayout, podemos utilizar


cualquier atributo permitido en dicho componente; en este caso hemos establecido, por
ejemplo, los atributos layout_width, layout_height y background. Adems, vamos a definir
tambin atributos xml exclusivos para este componente compuesto. Como ejemplo, definimos
el atributo texto_boton que permite establecer el texto del botn de la ventana desde el el
fichero anterior de diseo layout xml.
Para ello, hay que declarar un nuevo atributo y asociarlo al componente
ComponenteLogin. Esto se define en el fichero \res\values\attrs.xml, que incluye la etiqueta
<declare-styleable> asociada al ComponenteLogin donde indicaremos el nombre (name) y
el tipo (format) del nuevo atributo:

<resources>
<declare-styleable name="ComponenteLogin">
<attr name="boton_text" format="string"/>
</declare-styleable>
</resources>

Este atributo se usa en el segundo constructor de la clase ComponenteLogin mediante


el mtodo obtainStyledAttributes() del contexto de la aplicacin. Obtenemos el valor del
nuevo atributo definido (que es su ID formado por la concatenacin del nombre del
componente y el nombre del atributo, en nuestro caso ComponenteLogin_texto_boton) y
modificaremos el texto por defecto del botn con el nuevo texto.
Desde Eclipse puedes abrir el proyecto Ejemplo 6 (Login compuesto) de la Unidad 2.
Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa
anterior, en el que hemos utilizado un componente extendido.

Si ejecutas este ejemplo vers que el aspecto que presenta es el siguiente:

136

Diseo del interfaz de usuario

Ten en cuenta que en el cdigo de esta aplicacin nicamente se da por bueno el


usuario y contrasea admin/admin.

137

Una Actividad (Activity) es un componente de Android que ofrece una pantalla


con la que los usuarios pueden interactuar en una aplicacin.
Una aplicacin de Android consta de mltiples actividades que estn ms o
menos ligadas entre s.
Una funcin de llamada (en ingls callback) es una funcin que se remite a Android
cuando se inicia una Actividad para que el sistema operativo la llame durante la
ejecucin de esta Actividad.
El ciclo de vida de una Actividad de Android est compuesto de tres estados:
Resumed, Paused y Stopped.
Como muchos otros entornos de desarrollo, la interaccin con el usuario en
Android est basada en Controladores de Eventos (Event Handlers).
En Android el programador puede capturar los eventos especficos del objeto
Vista (View) con la que el usuario interacta y ejecutar sentencias.
Un Event Listener es una interfaz de la clase Vista (View) que contiene un nico
mtodo de tipo callback que Android invoca cuando detecta que el usuario est
provocando un tipo concreto de interaccin con este elemento de la interfaz de
usuario.
Es posible redefinir mtodos de tipo callback utilizados, como controladores
(handlers) de eventos para ampliar las funcionalidades de las Vistas.
El modo tctil permite al usuario interactuar con la interfaz utilizando su propios
dedos.
El entorno de desarrollo de Android incluye un emulador virtual para poder
ejecutar en un ordenador las aplicaciones que desarrollamos sin necesidad de
disponer de un dispositivo fsico.
Un Adaptador (Adapter) es un objeto que permite definir el modelo de datos que
usan todos los componentes de seleccin de forma unificada.

138

Diseo del interfaz de usuario

Android permite crear componentes personalizados de las siguientes maneras:

Extendiendo la funcionalidad de un componente ya existente

Combinando varios componentes para formar otro compuesto

Diseando desde cero un nuevo componente

139

140

Unidad de Aprendizaje 9

FECTOS
DE TRANSICIN
EMS
INFORMACIN
SOBRE
Y
ANANDROID
IMACIN

NDICE
3.1

INTRODUCCIN .......................................................... 143


3.1.1 Introduccin ...................................................................143
3.1.2 Gestin del botn Hacia atrs de Android ............143
3.1.3 Definicin de una tarea en los proyectos Android...145

3.2

GUARDAR Y RECUPERAR EL ESTADO DE UNA


ACTIVIDAD.................................................................... 147

3.3

PROCESOS EN HILOS EN ANDROID ........................ 154


3.3.1 Procesos ........................................................................154
3.3.2 Ciclo de vida de los procesos ....................................155
3.3.3 Hilos de ejecucin en Android ....................................157

3.4

HILOS EN SEGUNDO PLANO..................................... 158


3.4.1 Utilizacin de tareas asncronas con la clase
AsyncTask ..............................................................................159

3.5

MENS DE ANDROID ................................................. 162


3.5.1 Ejemplo de Men Principal y Submen ....................162
3.5.2 Ejemplo de Men Contextual......................................167
3.5.3 Ventanas de dilogo en Android ................................172
3.5.3.1 Ventanas de dilogo con mensaje ....................174
3.5.3.2 Ventanas de dilogo con botones ....................174
3.5.3.3 Ventanas de dilogo con seleccin ..................176
3.5.3.4 Ventanas de dilogo personalizada..................177

Ms informacin sobre Android

3.1

INTRODUCCIN

3.1.1

Introduccin
En esta Unidad vamos a explicar cmo funciona la tecla Retroceso de los

dispositivos Android. Adems, veremos cmo almacenar el estado de una Actividad de


Android.
Tambin, detallaremos el uso de Hilos en Android.
Finalmente, usaremos Mens y Ventana de dilogo para mejorar la interfaz de
usuario con ejemplos de aplicaciones de Android.

3.1.2

Gestin del botn Hacia atrs de Android


Como ya hemos visto, una aplicacin puede contener mltiples actividades. El

programador desarrolla una actividad para que el usuario realice un tipo especfico de accin.
Adems, una actividad puede arrancar otras actividades. Por ejemplo, una aplicacin de
correo electrnico puede tener una Actividad que muestre una lista con los mensajes nuevos.
As, cuando el usuario seleccione un correo electrnico, se puede abrir una nueva actividad
para mostrar su contenido.
Una

Actividad

tambin

puede

arrancar

las

actividades

que

definen

otras

aplicaciones. Por ejemplo, si una aplicacin tiene que enviar un correo electrnico, puede usar
el objeto Intencin (en ingls, Intent) de Android para realizar el envo incluyendo alguna
informacin, como la direccin de correo electrnico del destinatario y un mensaje.
En la Unidad 5 veremos cmo se usan estas Intenciones ("Intents").
Para que se pueda hacer esto, hay que declarar la Actividad que enva mensajes para
que admita este tipo de intenciones. En este caso, la intencin es enviar un correo
electrnico, por lo que la aplicacin disponible para crear correos electrnicos se inicia (si
hubiera varias Actividades que tuvieran la misma intencin, el sistema permite al usuario
seleccionar la que desea utilizar). Finalmente, cuando se enva el mensaje, la actividad inicial
se reanuda y parece que la Actividad de correo electrnico forma parte de la aplicacin
general. A pesar de que las actividades pueden pertenecer a diferentes aplicaciones, Android
mantiene la sensacin de que se trata de una nica aplicacin juntndolas en una sola Tarea
(Task).
Una Tarea es un conjunto de actividades con las que un usuario interacta. Android
organiza las actividades en una pila de ejecucin (en ingls stack) donde se van apilando las
actividades que el usuario va invocando. Cada Tarea tiene asociada su propia pila de
ejecucin independiente.
La ventana siguiente muestra la pantalla principal del dispositivo (HOME):

143

Cuando el usuario toca un icono de esta pantalla, se inicia una tarea asociada a la
aplicacin. Si es la primera vez que arrancamos la aplicacin, el sistema operativo crea una
nueva tarea y su Actividad principal ("main") queda almacenada en primer lugar en la pila de
Android.
Cuando esta Actividad principal arranca otra, la nueva Actividad se aade a la parte
superior de la pila y pasa a primer plano. La Actividad anterior permanece en la pila y se
detiene manteniendo el estado actual de la interfaz de usuario. Si el usuario pulsa la tecla de
retroceso

del telfono, la Actividad actual se quita de la parte superior de la pila, se

destruye y se reanuda la Actividad anterior en el estado que estuviera. Las actividades en la


pila no se pueden reorganizar, se trata de una pila de tipo "ltimo en entrar, primero en salir"
("last in, first out").
En el siguiente esquema visualizamos cmo cambia la pila de Android al ir abriendo
Actividades y al pulsar el botn "Volver Atrs":

144

Ms informacin sobre Android


Si el usuario contina presionando "Volver atrs", se extraer una a una cada actividad
de la pila mostrando la anterior, hasta que aparezca la pantalla de inicio u otra aplicacin que
se haya iniciado antes. Cuando esto ocurre, la tarea se destruye.
Una tarea es una unidad compuesta de actividades que puede pasar a un segundo
plano cuando los usuarios inician una nueva tarea o van a la pantalla de inicio. Cuando una
tarea se encuentra en segundo plano, todas sus actividades se detienen, aunque su pila de
ejecucin se mantiene intacta hasta que el usuario decida que vuelva al "primer plano".
Android denomina a esta manera de guardar el estado de las aplicaciones Multitarea
(Multitasking), si bien no se trata de ejecucin simultnea de las aplicaciones, sino de la
posibilidad de seguir en el estado donde la dejamos cuando ejecutemos otra aplicacin y
volvamos a la inicial.

Nota: es posible mantener muchas tareas en segundo plano a la vez; sin embargo, si el
sistema operativo necesita memoria, puede destruirlas perdiendo sus estados de ejecucin.

3.1.3

Definicin de una tarea en los proyectos Android


Si abrimos el fichero AndroidManifest.xml del Ejemplo 1 de la Unidad 1, veremos que

contiene las siguientes etiquetas:

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".BienvenidoActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

Aqu podemos establecer una Actividad como punto de entrada de la tarea


escribiendo el "android.intent.action.MAIN" como la accin principal. En este caso, la
Actividad .BienvenidoActivity est definida como principal.

145

Nota: debido a la nomenclatura de Android, es obligatorio escribir un punto al principio del


nombre.

Aunque no es lo normal, es posible definir varios puntos de entrada en un mismo


proyecto.
Un punto de entrada hace que Android cree un icono y una etiqueta en la pantalla de
inicio para la actividad correspondiente, ofreciendo a los usuarios una forma de iniciar la
actividad.
La categora de la Actividad android:name="android.intent.category.LAUNCHER es
muy importante, ya que permite a los usuarios volver a una tarea que est en segundo plano
desde el Lanzador de actividades. Para llegar a este Lanzador debemos hacer clic en el botn:

A continuacin aparecen todas las aplicaciones instaladas:

Tambin es posible ver las aplicaciones recientes pulsando un par de segundos el


botn "Inicio"

146

para acceder a las ejecutadas anteriormente:

Ms informacin sobre Android

Por esta razn, es muy importante marcar la actividad principal con la categora
"android.intent.category.LAUNCHER", para que el usuario pueda volver a una aplicacin
que se encuentre en segundo plano y que ya no es visible.

3.2

GUARDAR Y RECUPERAR EL ESTADO DE UNA ACTIVIDAD


La configuracin de un telfono puede cambiar mientras un usuario ejecuta una

aplicacin. Por ejemplo, la orientacin de la pantalla (vertical/horizontal), la disponibilidad de


teclado, el idioma, etctera. Cuando este cambio se produce, Android reinicia la Actividad
usando

el

mtodo

OnDestroy()

inmediatamente

seguido

por

onCreate().

Este

comportamiento de reinicio est diseado para que la aplicacin se adapte a la nueva


configuracin de forma automtica, por ejemplo, cuando cambia la posicin de los
componentes.
La mejor manera de manejar un cambio de configuracin para preservar el estado de
la aplicacin es usar los mtodos onSaveInstanceState() y onCreate().
En la Unidad 2 ya hemos estudiado lo que es una Actividad y su ciclo de vida.
En este apartado vamos a describir lo que hace Android cuando una Actividad se
destruye o se detiene, cmo se conserva su estado en la memoria del sistema operativo,
incluyendo todos los cambios que haya hecho el usuario en la interfaz introduciendo
informacin, seleccionando opciones, etctera.
En el siguiente esquema se muestra los eventos y estados por los que pasa una
Actividad cuando se detiene o se destruye.

147

Como se puede ver en el esquema anterior, una Actividad recupera su estado de


ejecucin si se detiene y vuelve a primer plano; o si se destruye y se vuelve a crear.
Para almacenar la informacin importante sobre el estado de ejecucin de una
Actividad podemos usar el mtodo de llamada (callback) de Android onSaveInstanceState() y
luego restaurar esta informacin cuando el sistema vuelva a crear la Actividad.
El sistema llama a este mtodo justo antes de que la Actividad se destruya y le pasa
como parmetro un objeto de tipo Bundle. Este objeto Bundle es donde podemos almacenar
la informacin del estado de la Actividad como pareja nombre-valor, utilizando el mtodo
putString(). De esta manera, si Android mata el proceso de la Actividad y el usuario vuelve a
ejecutar la misma Actividad, el sistema pasa el objeto Bundle almacenado anteriormente
como parmetro en el mtodo onCreate(), para que pueda restaurar el estado de ejecucin. Si
no hay informacin sobre este estado, el objeto Bundle es nulo.

Nota: no hay garantas de que Android invoque siempre el mtodo onSaveInstanceState()


justo antes de que la Actividad se destruya, ya que hay casos en los que no es necesario
guardar el estado de ejecucin; por ejemplo, cuando el usuario sale de la aplicacin usando la
tecla Volver atrs

, que indica que desea salir de la Actividad.

Sin embargo, incluso si no lo hacemos en el mtodo onSaveInstanceState(), la


aplicacin restaura por defecto automticamente parte del estado de la Actividad. En

148

Ms informacin sobre Android


concreto, la implementacin por defecto de este mtodo recorre cada componente de la
interfaz de usuario, guardando y restaurando automticamente estos componentes. Por
ejemplo, se almacena automticamente el texto contenido en un componente de tipo EditText
o la seleccin en un componente CheckBox. Lo nico que debemos hacer para que Android
guarde el estado de los componentes es proporcionar un identificador nico (atributo
android:id) para cada uno de ellos. Si un componente no tiene un identificador, entonces no
se guarda su estado.
Tambin es posible indicar de forma explcita que no se almacene automticamente el
estado de un componente de dos formas: en el diseo de la interfaz de usuario con el atributo
android:saveEnabled establecido a "false", o usando el mtodo del componente
setSaveEnabled(). Por lo general, no se debe desactivar esta opcin, salvo que debamos
proteger cierta informacin (contraseas) o que al restaurar el estado de la interfaz de usuario
haya que mostrar la pantalla de manera diferente.
Aunque el mtodo onSaveInstanceState() guarda la informacin til del estado de la
interfaz de usuario, puede ser necesario guardar informacin adicional, como la orientacin del
dispositivo (horizontal o vertical). Podemos usar este mtodo y la variable Bundle para
almacenar esta informacin extra. Ten en cuenta que siempre debemos invocar el mtodo de
la clase madre antes de guardar el resto de informacin:

super.onCreate(estadoAlmacenado);

Muy importante: debido a que Android no garantiza que siempre se invoque el mtodo
onSaveInstanceState(), debe usarse nicamente para almacenar el estado transitorio de la
Actividad, es decir, la interfaz de usuario. Nunca debe emplearse para almacenar datos
persistentes con preferencias del usuario o datos de la aplicacin. En la Unidad 4 veremos
cmo se implementa esta funcionalidad.

A continuacin, vamos a describir el Ejemplo 1 de esta Unidad para que queden ms


claros los conceptos anteriores.
En el fichero res\layout\main.xml hemos definido la siguiente interfaz de usuario:

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout android:id="@+id/mainLayout"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">

149

<TextView android:id="@+id/nombre" android:layout_width="wrap_content"


android:layout_height="wrap_content" android:text="Introduce el usuario:"
android:layout_marginLeft="3dip" android:layout_marginTop="10dip">
</TextView>
<EditText android:id="@+id/nombreEditText"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:textSize="18sp" android:layout_marginLeft="3dip"
android:layout_marginRight="3dip" android:layout_marginTop="3dip">
</EditText>
<TextView android:id="@+id/password" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Introduce tu contrasea (no se guardar):"
android:layout_marginLeft="3dip" android:layout_marginTop="10dip">
</TextView>

<!-- En este EditText no definimos el ID. As indicamos a Android que no


queremos que guarde el estado de este componente -->
<EditText android:id="@+id/passwordEditText"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:textSize="18sp" android:layout_marginLeft="3dip"
android:layout_marginRight="3dip" android:layout_marginTop="3dip"
android:saveEnabled="false">
</EditText>
</LinearLayout>

Se trata de una sencilla pantalla de autentificacin de usuario. Fjate que hemos


definido el atributo android:saveEnabled="false" para que no se almacene el campo
contrasea.

Despus, hemos definido la Actividad as:

150

Ms informacin sobre Android


public class GuardarestadoactividadActivity extends Activity {
@Override
protected void onCreate(Bundle estadoAlmacenado) {
// Recuperamos el estado de la Actividad
super.onCreate(estadoAlmacenado);
setContentView(R.layout.main);
if (estadoAlmacenado != null) {
// Recuperamos una variable que se almacena con el estado
Toast.makeText(this, "Evento onCreate(). Se recupera variable
almacenada: " +
estadoAlmacenado.getString("VARIABLE_ALMACENADA"),
Toast.LENGTH_LONG).show();
}
}
@Override
protected void onSaveInstanceState(Bundle estado) {
// Guardamos el estado de la Actividad (sus componentes)
super.onSaveInstanceState(estado);
// Aadimos una variable que se almacena con el estado
estado.putString("VARIABLE_ALMACENADA", "Texto guardado");
Toast.makeText(this, "El estado de la Actividad se ha guardado",
Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
Toast.makeText(this, "El sistema ha terminado la Actividad",
Toast.LENGTH_LONG).show();
}
}

En el cdigo anterior hemos usado los mtodos putString() y getString() del objeto
Bundle para almacenar el contenido de una variable extra en el estado de la Actividad.

151

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Guardar estado de una actividad) de la
Unidad 3. Estudia el cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en
la que guardamos el estado de ejecucin de la Actividad

Para entender mejor la funcionalidad que almacena el estado de la Actividad, inicia


este ejemplo en el emulador. Vers la siguiente pantalla:

A continuacin, introduce algn texto en los campos que aparecen. Despus, haz clic
en el botn "INICIO"

del emulador. Aparecer el mensaje "El estado de la Actividad se ha

guardado" en la pantalla y desaparecer la aplicacin:

Si volvemos a ejecutar la aplicacin, veremos que podemos ver tanto los datos tanto
de la caja de texto usuario como de la contrasea. Esto ocurre porque Android no ha
destruido la Actividad.

152

Ms informacin sobre Android


Para que Android pare (kill) la Actividad cuando la dejamos, hay que configurar las
herramientas de desarrollo del dispositivo conocidas como "Dev-Tools".

Para acceder a

estas herramientas hay que hacer clic en el icono "Dev Tools" que aparece en el lanzador de
aplicaciones. Despus, hacemos clic en la opcin "Development Settings" y, finalmente,
marcamos la opcin "Immediately destroy activities":

Si ahora ejecutas de nuevo la aplicacin y pulsas otra vez el botn "INICIO", vers el
mensaje "El sistema ha terminado la Actividad", que indica que la aplicacin se destruye:

Si vuelves a abrir de nuevo la aplicacin, observars que nicamente se recupera el


campo nombre de usuario.

Importante: si haces clic en el botn ATRS


del emulador, no se guarda el estado de la
actividad, ya que Android considera que el usuario desea finalizar la ejecucin de la
aplicacin. En este caso, todos los valores de los componentes se perdern.

153

3.3

PROCESOS EN HILOS EN ANDROID


Un hilo es una caracterstica de la informtica que permite a una aplicacin realizar

varias tareas a la vez (concurrentemente). Esencialmente, un hilo es una tarea que se ejecuta
en paralelo con otra tarea. Todos los hilos que se ejecutan a la vez pueden compartir una
serie de recursos, tales como la memoria, los archivos abiertos, etctera. Esta tcnica permite
simplificar el diseo de una aplicacin que puede as llevar a cabo distintas funciones
simultneamente.
Cuando un usuario ejecuta una aplicacin en Android se inicia un nuevo proceso de
Linux con un nico hilo de ejecucin. Por defecto, todos los componentes de una misma
aplicacin se ejecutan en el mismo hilo principal (en ingls se denomina "main thread"). A
este hilo tambin se lo denomina hilo de la interfaz de usuario (en ingls, "UI thread").
Si se inicia una Actividad de una aplicacin que ya se est ejecutando y, por lo tanto,
ya existe un proceso asociado, entonces la nueva Actividad se inicia dentro de ese proceso y
utiliza el mismo hilo de ejecucin. Sin embargo, es posible disear aplicaciones que ejecuten
sus diferentes componentes de la aplicacin en procesos separados y el programador puede
crear subprocesos adicionales para cualquier proceso principal.
En este apartado vamos a estudiar cmo funcionan los procesos e hilos en una
aplicacin de Android.

3.3.1

Procesos
Ya hemos comentado que, por defecto, todos los componentes de una misma

aplicacin se ejecutan en el mismo proceso. La mayora de las aplicaciones deben ejecutarse


de esta manera. Sin embargo, a veces, es necesario controlar en qu proceso se ejecuta un
componente

en

particular.

Para

hacer

esto,

debemos

modificar

el

archivo

AndroidManifest.xml del proyecto de Android.


En este fichero se definen con etiquetas los componentes de la aplicacin:

Actividad: etiqueta <activity>

Servicio: etiqueta <service>

Broadcast receiver: etiqueta <receiver>

Content provider: etiqueta <provider>

Dentro de estas etiquetas se puede definir el atributo android:process que establece


el proceso que ejecuta ese componente. Se puede establecer este atributo de manera que
algunos componentes se ejecutan en su propio proceso y otros lo comparten. Incluso tambin
es posible configurar componentes de aplicaciones diferentes para que compartan el mismo

154

Ms informacin sobre Android


proceso, siempre y cuando estas aplicaciones compartan el mismo ID de usuario de Linux y
estn firmadas con los mismos certificados del desarrollador.
Adems, tambin podemos usar el atributo android:process dentro del elemento
<application> para definir el valor por defecto del proceso que aplica a todos sus
componentes.
Android podra matar un proceso en algn momento si queda poca memoria en el
telfono y el usuario solicita que se ejecuten nuevos procesos al iniciar otra aplicacin. Los
componentes de la aplicacin del proceso destruido desaparecen y Android debe volver a
crear el proceso si la aplicacin se reinicia.
Cuando el sistema Android necesita matar algn proceso para liberar memoria para
otro, tiene en cuenta su importancia en relacin con el usuario. Por ejemplo, se termina antes
un proceso que alberga actividades que ya no son visibles en la pantalla que otro donde sus
actividades son visibles. Por lo tanto, la decisin de terminar un proceso depende del estado
de los componentes que se ejecutan en ese proceso. Las reglas que emplea Android para
acabar con un proceso se discuten a continuacin.

3.3.2

Ciclo de vida de los procesos


El sistema operativo Android trata siempre de mantener el proceso de una

aplicacin el mayor tiempo posible en memoria. Sin embargo, a veces, es necesario eliminar
procesos antiguos para liberar memoria e iniciar nuevos

procesos. Para determinar qu

procesos se deben eliminar, el sistema ordena jerrquicamente por importancia cada proceso
basndose en qu componentes se estn ejecutando dentro de l y su estado. Los procesos
de menor importancia se eliminan primero, despus los de importancia media y as
sucesivamente.
Hay cinco niveles en la jerarqua de importancia. La siguiente lista muestra los
diferentes tipos de procesos por orden de importancia (el primer proceso es ms importante y
se destruye el ltimo):
1. Proceso en primer plano (Foreground): es un proceso en el que se ejecutan
componentes que est usando el usuario en ese momento. Por lo general, slo hay
unos pocos procesos en primer plano en un momento dado. Slo se eliminan
como ltimo recurso, cuando la memoria del telfono es tan baja que no puede
continuar con la aplicacin actual y la pantalla deja de hacer caso al usuario.
2. Proceso visible: es un proceso que no tiene ningn componente en primer plano,
pero puede afectar a lo que el usuario ve en la pantalla. Esto puede ocurrir, por
ejemplo, si la actividad en primer plano abre una ventana de dilogo que permite
ver a la la actividad anterior detrs de ella.

155

3. Proceso de servicio: es un proceso que ejecuta un servicio que se ha iniciado con


el mtodo startService() y no corresponde a ninguna de las dos categoras
anteriores. Aunque los procesos de servicio no estn relacionados directamente
con lo que ve el usuario, por lo general, desempean tareas importantes para el
usuario, como reproducir msica o descargar de datos de Internet.
4. Procesos en segundo plano (Background): es un proceso que mantiene una
actividad que no se muestra al usuario (ocurre cuando Android invoca el mtodo
OnStop() de la actividad). Estos procesos no tienen un impacto directo sobre la
experiencia del usuario y el sistema puede eliminarlos en cualquier momento para
liberarmemoria.
Por lo general, hay muchos procesos ejecutndose en segundo plano, por lo que
se reordenan siguiendo el esquema "recientemente menos utilizado" (del ingls,
LRU: Least Recently Used) que asegura que se el ltimo proceso utilizado por el
usuario se elimina al final.
5. Si una actividad utiliza los mtodos de su ciclo de vida de forma correcta
guardando su estado actual y Android decide matarla, el usuario no

notar

nada ya que Android recuperar su estado.


6. Proceso vaco: es un proceso que no tiene ningn componente de la aplicacin
activa. La nica razn para mantener este tipo de proceso en memoria es para
fines de precarga en la cach y mejorar el tiempo de arranque la prxima vez que
se ejecute.
Android asigna siempre a un proceso el nivel ms alto en base a la importancia de sus
componentes internos. Por ejemplo, si un proceso contiene un servicio y una Actividad visible,
el proceso se clasifica como un proceso visible, no como un proceso de servicio.
Adems, la clasificacin de un proceso puede aumentar debido a que otros procesos
dependen de l. Por ejemplo, un proceso que da servicio a otro proceso no puede ser
clasificado nunca con un rango inferior al proceso al que da servicio.
Hemos visto que un proceso que ejecuta un Servicio se clasifica con mayor prioridad
que un proceso con una Actividad en segundo plano. Si una actividad inicia una operacin que
dura mucho, es recomendable crear un servicio para esa operacin, en lugar de crear un
subproceso, especialmente si la operacin va a durar ms que la propia actividad. Por
ejemplo, si una actividad tiene que subir una foto a un sitio Web, es mejor iniciar un servicio
para llevar a cabo esta tarea, as la carga pueda continuar en segundo plano incluso si el
usuario sale de la actividad. El uso de servicios garantiza de que la operacin tendr, por lo
menos, la prioridad de un proceso de servicio, independientemente de lo que ocurra con la
actividad.
Por esta misma razn, los receptores de mensajes (broadcast receivers) deben
emplear servicios en lugar de tareas con un hilo de ejecucin.

156

Ms informacin sobre Android


3.3.3

Hilos de ejecucin en Android


Cuando el usuario ejecuta una aplicacin, Android crea un hilo de ejecucin que se

denomina principal (main). Este hilo es muy importante porque es el encargado de gestionar
los eventos que dispara el usuario a los componentes adecuados e incluye tambin los
eventos que dibujan la pantalla. Por esta razn, a este hilo principal tambin se le llama hilo de
la interfaz de usuario (UI thread).
Este modelo de ejecucin de las aplicaciones se denomina Modelo de ejecucin
de un nico hilo (en ingls, Single Thread Model).
Android no crea un subproceso independiente para cada componente de la aplicacin.
Todos sus componentes se ejecutan dentro del mismo proceso en el hilo principal. Por lo
tanto, los mtodos que responden a eventos del sistema, como onKeyDown(), siempre se
ejecutan en este hilo principal o de la interfaz de usuario. Por ejemplo, cuando el usuario toca
con el dedo un botn de la pantalla del telfono, el hilo principal invoca el evento
correspondiente en el widget apropiado y lo redibuja.
Si un usuario interacciona mucho con una aplicacin, este modelo de ejecucin con un
nico hilo puede dar lugar a poca fluidez en ella a menos que implementemos adecuadamente
la aplicacin. Es decir, como todo lo que ejecuta la aplicacin se hace en un nico hilo
principal, llevar a cabo operaciones que van a tardar cierto tiempo, como acceder a Internet o
consultar una base de datos, bloquearn la interfaz de usuario. Cuando el hilo principal est
bloqueado la pantalla de aplicacin se bloquea (ni siquiera se dibuja) y, desde el punto de vista
del usuario, la aplicacin se bloquea. Adems, si el hilo principal est bloqueado durante 5
segundos, Android muestra al usuario una ventana con el mensaje "La aplicacin no
responde" (en ingls, ANR: Application Not Responding):

Adems, la interfaz de usuario de Android no es thread-safe. Una funcin o mtodo


thread-safe puede ser invocado por mltiples hilos de ejecucin sin preocuparnos de que los
157

datos a los que accede dicha funcin (o mtodo) sean corrompidos por alguno de los hilos ya
que se asegura la atomicidad de la operacin, es decir, la funcin se ejecuta de forma
serializada sin interrupciones
Es decir, en Android se puede manipular la interfaz de usuario desde otro subproceso
Por lo tanto, hay tener en cuenta dos reglas a la hora de disear aplicaciones en Android:
1. No bloquear nunca el hilo principal o de la interfaz de usuario.
2. No acceder a la interfaz de usuario de Android desde un hilo exterior.

3.4

HILOS EN SEGUNDO PLANO

Debido al Modelo de hilo nico explicado anteriormente, es vital que no se bloquee el


hilo principal (o de interfaz de usuario) para que la interfaz de usuario de la aplicacin responda
a la interaccin del usuario. Si la aplicacin tiene que realizar operaciones que no son
instantneas, hay que ejecutarlas en hilos separados en segundo plano.
Por ejemplo, a continuacin se muestra el cdigo para que se descargue de Internet
una imagen cuando el usuario hace clic en un botn usando un hilo separado en segundo
plano:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://pag.es/imagen.png");
miImageView.setImageBitmap(b);
}
}).start();
}

En este ejemplo hemos usado el estndar de hilos de Java, que se define en el


paquete "java.util.concurrent", para iniciar operaciones en segundo plano.
A primera vista, este cdigo parece funcionar bien ya que crea un nuevo hilo en
segundo plano para descargar la imagen. Sin embargo, no cumple la segunda regla del
modelo de un hilo nico: no acceder a la interfaz de usuario de Android desde un hilo
exterior a esta interfaz de usuario. Esto puede producir un comportamiento inesperado en la
aplicacin y muy difcil de localizar.
Para solucionar este inconveniente, Android ofrece varias formas de acceder al hilo de
la interfaz de usuario desde otros hilos en segundo plano. A continuacin se muestra la lista de
estos mtodos:

158

Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)

Ms informacin sobre Android


Por

ejemplo,

para

arreglar

el

ejemplo

anterior

podemos

usar

el

mtodo

View.post(Runnable):

public void onClick(View v) {


new Thread(new Runnable() {
public void run() {
final Bitmap b=loadImageFromNetwork("http://pag.es/imagen.png");
miImageView.post(new Runnable() {
public void run() {
miImageView.setImageBitmap(b);
}
});
}
}).start();
}

Ahora el cdigo de la aplicacin es correcto y la operacin se descarga de la imagen


se realiza en un hilo en segundo plano, mientras que el componente de tipo ImageView se
manipula desde el hilo de la interfaz de usuario.
Sin embargo, si aumenta la complejidad de la operacin que la aplicacin debe
realizar, este tipo de codificacin puede ser complicada y difcil de mantener por el
programador. Para gestionar interacciones ms complejas con hilos en segundo plano, es
mejor usa un controlador (Handler) en este hilo de sendo plano para procesar los mensajes
que se enva al hilo principal de la interfaz de usuario.
La mejor solucin es extender la clase AsyncTask que simplifica la ejecucin de las
operaciones en segundo plano.

3.4.1

Utilizacin de tareas asncronas con la clase AsyncTask


La clase AsyncTask permite realizar operaciones asncronas en la interfaz de usuario.

Lleva a cabo las operaciones que bloquean la pantalla al usuario en un proceso de segundo
plano y devuelve su resultado al hilo principal de la interfaz de usuario de manera sencilla.
Para usar esta funcionalidad de Android hay que extender la clase AsyncTask e
implementar los siguientes mtodos callback:

doInBackground(): inicia los procesos en segundo plano.

onPostExecute(): actualiza la interfaz de usuario desde el hilo principal de la


interfaz de usuario.
As, para implementar el ejemplo anterior usando la clase AsyncTask escribimos:

159

//Mtodo asociado al botn "Descargar"


public void descargarImagen(View view) {
imagen.setVisibility(View.INVISIBLE);
cargando.setVisibility(View.VISIBLE);

// Iniciamos la tarea de descarga

TareaDescargaImagen tarea = new TareaDescargaImagen();


tarea.execute(imagenURL);

}
// Clase que descarga una imagen de Internet como una tarea asncrona.
// Es decir, podemos seguir usando la interfaz de usuario.
private class TareaDescargaImagen extends AsyncTask<String, Void, Bitmap> {

// Mtodo que se ejecuta en segundo plano


protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}

// Cuando la tarea ha acabado se invoca automticamente este mtodo


protected void onPostExecute(Bitmap resultado) {
cargando.setVisibility(View.INVISIBLE);

// Cargamos la imagen su se ha podido descargar la imagen de Internet


if (resultado!=null)

imagen.setImageBitmap(resultado);

else imagen.setImageResource(R.drawable.error);
imagen.setVisibility(View.VISIBLE);

} // end onPostExecute
}

Fjate que para iniciar la tarea hemos usado el mtodo execute().

Para definir la tarea asncrona hemos extendido la clase as:


AsyncTask<String, Void, Bitmap>

El primer parmetro (String) define el tipo de variable que usamos como parmetro al
invocar doInBackground(). El segundo parmetro (Void) define el tipo de variable que
pasamos como parmetro al invocar onProgressUpdate(). Finalmente, el ltimo parmetro
(Bitmap) define el tipo de variable que pasamos como parmetro al invocar onPostExecute()

160

Ms informacin sobre Android


Aunque ms adelante en el curso veremos cmo se indican los permisos que necesita
una aplicacin para ejecutarse, para que este ejemplo funcione y se descargue una imagen de
Internet es necesario indicar el permiso en el fichero AndroidManifest.xml de la aplicacin as:
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>

Es recomendable que el alumno o alumna lea el manual de la clase AsyncTask para conocer
ms funcionalidad, entre ella se encuentra:

Es posible llamar al mtodo publishProgress() dentro de doInBackground() para


invocar onProgressUpdate() y actualizar la pantalla del usuario para que vea cmo
progresa la operacin.

Es posible cancelar la operacin en cualquier momento.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Hilos) de la Unidad 3. Estudia el


cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que usamos un
proceso en segundo plano

Para entender mejor esta aplicacin que usa un proceso en segundo plano, inicia este
ejemplo en el emulador. Vers la pantalla siguiente:

161

Si pulsas en el botn "Descargar" comprobars que puedes seguir usando el resto de


Vistas de la pantalla mientras se descarga la imagen.
Atencin: Puede ocurrir un problema al utilizar un proceso en segundo plano cuando se
produce un reinicio inesperado de la Actividad debido a un cambio de configuracin en
tiempo de ejecucin, por ejemplo, cuando el usuario cambia la orientacin de la pantalla. Esto
puede destruir el proceso en segundo plano y que la operacin no finalice correctamente.
Adems, es posible acceder a un hilo en segundo plano desde el proceso de otra aplicacin,
para ello hay que desarrollar este hilo de manera thread-safe.
Estas funcionalidades forman parte de la programacin avanzada en Android y no se describe
su uso en este curso.

3.5

MENS DE ANDROID

En informtica un Men contiene una serie de opciones que el usuario puede


elegir para realizar una determinada tarea.
En las aplicaciones de Android podemos utilizar tres tipos de mens diferentes:

Mens Principales: son los usados con ms frecuencia. Aparecen en la zona inferior
de la pantalla al pulsar el botn Men

del telfono.

Submens: son los mens secundarios que aparecen al elegir una opcin de un men
principal.

Mens Contextuales: son muy tiles y se muestran al realizar una pulsacin larga
sobre algn elemento de la pantalla. Es el equivalente al botn derecho del ratn en un
PC.
Como es habitual en Android, existen dos formas de crear un men en una aplicacin

Android: definiendo el men en un fichero XML e "inflndolo" despus o creando el men


directamente mediante cdigo Java. En este apartado veremos ambas formas.

3.5.1

Ejemplo de Men Principal y Submen


Veamos en primer lugar cmo crear un men principal con un submen a partir de su

diseo en XML. Estos ficheros XML con el diseo del men se deben guardar en la carpeta
res\menu del proyecto y tienen una estructura de este tipo (archivo menu_principal.xml):

162

Ms informacin sobre Android


<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/MenuOp1" android:title="Opcin 1"


android:icon="@drawable/menu_estrella"></item>
<item android:id="@+id/MenuOp2" android:title="Opcin 2"
android:icon="@drawable/menu_brujula"></item>
<item android:id="@+id/MenuOp3" android:title="Opcin 3"
android:icon="@drawable/menu_direccion">
<menu>
<item android:id="@+id/SubMenuOp1"
android:title="Opcin 3.1" />
<item android:id="@+id/SubMenuOp2"
android:title="Opcin 3.2" />
</menu>
</item>
</menu>

Podemos ver en el cdigo anterior que la estructura bsica del diseo del men es
muy sencilla. Aparece un elemento principal <menu> que contiene los elementos <item> que
corresponden con las diferentes opciones del men.
Estos elementos <item> tienen a su vez varias propiedades bsicas, como su ID
(atributo android:id), su texto (atributo android:title) y su icono (atributo android:icon). Los
iconos utilizados deben guardarse en las carpetas res\drawable-... del proyecto.
Adems, hemos definido un submen (men secundario) que se muestra al pulsar la
opcin 3 del un men principal. Los submens en Android se muestran en una lista emergente
cuyo ttulo contiene el texto de la opcin elegida del men principal. Este submen tiene dos
nuevas opciones secundarias. Para ello, hemos aadido un nuevo elemento <menu> dentro
del <item> correspondiente a la opcin 3.
De igual forma que otros archivos XML de un proyecto Android, podemos editarlo
visualmente haciendo clic en la pestaa Layout del archivo que define el men:

163

Una vez definido el men en el fichero XML, hay que implementar el mtodo
onCreateOptionsMenu() de la Actividad para que se cree en la pantalla. En este mtodo
debemos inflar el men de forma parecida a como ya hemos hecho con otro tipo de
componentes layouts. Primero obtenemos una referencia al objeto "inflador" mediante el
mtodo getMenuInflater() y, despus, generamos la estructura del men usando el mtodo
inflate() y pasndole como parmetro el ID del archivo XML de diseo del men. Finalmente,
el mtodo debe devolver el valor true para indicar a la Actividad que debe mostrar el men.

@Override
public boolean onCreateOptionsMenu(Menu menu) {

//Forma 1: definimos el men inflando el fichero XML con su diseo

MenuInflater inflater = getMenuInflater();


inflater.inflate(R.menu.menu_principal, menu);
return true;
}

A continuacin, vamos a ver cmo implementar el diseo del men programndolo


con sentencias Java.
Para ello, redefinimos el mtodo onCreateOptionsMenu() aadiendo las opciones del
men mediante el mtodo add() del objeto Menu, que es un parmetro del primer mtodo.
Este mtodo add() se invoca con cuatro parmetros:

164

Ms informacin sobre Android

ID del grupo asociado a la opcin: veremos qu es esto en el siguiente ejemplo con un


men contextual, por lo que establecemos el valor Menu.NONE.

ID nico para la opcin: declaramos unas constantes de la clase.

Orden de la opcin: como no queremos indicar ninguno, utilizamos Menu.NONE.

Texto de la opcin: texto que aparece en el men.


El icono de cada opcin lo establecemos mediante el mtodo setIcon() pasndole el ID

del recurso.
Veamos cmo queda el cdigo utilizando esta otra forma de implementarlo que genera
un men exactamente igual al del ejemplo anterior:

private static final int MENU_OP1 = 1;


private static final int MENU_OP2 = 2;
private static final int MENU_OP3 = 3;
private static final int SMENU_OP1 = 31;
private static final int SMENU_OP2 = 32;
...
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Forma 2: definimos el men con sentencias Java
menu.add(Menu.NONE, MENU_OP1, Menu.NONE, "Opcin 1")
.setIcon(R.drawable.menu_estrella);
menu.add(Menu.NONE, MENU_OP1, Menu.NONE, "Opcin 2")
.setIcon(R.drawable.menu_brujula);
SubMenu submenu = menu.addSubMenu(Menu.NONE, MENU_OP1, Menu.NONE,
"Opcin 3").setIcon(R.drawable.menu_direccion);
submenu.add(Menu.NONE, SMENU_OP1, Menu.NONE, "Opcin 3.1");
submenu.add(Menu.NONE, SMENU_OP2, Menu.NONE, "Opcin 3.2");
return true;
}

Para aadir el submen a la opcin 3 utilizamos el mtodo addSubMenu() en lugar de


add() y guardamos una referencia al submen donde insertamos las dos nuevas opciones
utilizando una vez ms el mtodo add().
Una vez construido el men, es necesario implementar las sentencias que se ejecutan
cuando el usuario selecciona una de las opciones, Para ello, usamos el evento

165

onOptionsItemSelected() de la Actividad. Este evento recibe como parmetro el elemento de


men (MenuItem) que ha sido elegido por el usuario y cuyo ID podemos obtener con el
mtodo getItemId(). En funcin de este ID podemos saber qu opcin ha sido pulsada y
ejecutar unas sentencias u otras. En nuestro ejemplo, lo nico que hacemos es modificar el
texto de la etiqueta labelResultado que hemos colocado en la pantalla principal de la
aplicacin:

// Si el usuario selecciona
// seleccionada en la etiqueta

una

opcin

del

men

mostramos

la

opcin

public boolean onOptionsItemSelected(MenuItem item) {


switch (item.getItemId()) {
case R.id.MenuOp1:
labelResultado.setText("Has pulsado la opcin 1");
return true;
case R.id.MenuOp2:
labelResultado.setText("Has pulsado la opcin 2");
return true;
case R.id.MenuOp3:
labelResultado.setText("Has pulsado la opcin 3");
return true;
case R.id.SubMenuOp1:
labelResultado.setText("Has pulsado la opcin 3.1");
return true;
case R.id.SubMenuOp2:
labelResultado.setText("Has pulsado la opcin 3.2");
return true;
default:
return super.onOptionsItemSelected(item);
}
}// end onOptionsItemSelected

Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Mens) de la Unidad 3. Estudia el cdigo
fuente y ejectalo para mostrar en el emulador una aplicacin en la que usamos un men
principal y un submen,

166

Ms informacin sobre Android

Para ver cmo funciona esta aplicacin que usa un men y un submen inicia este ejemplo en
el emulador. Vers la pantalla siguiente:

3.5.2

Ejemplo de Men Contextual


Los mens contextuales siempre estn asociados a un componente en concreto de la

pantalla y se muestra cuando el usuario lo pulsa un rato. Normalmente, se suele mostrar


opciones especficas para el elemento pulsado. Por ejemplo, en un componente de tipo lista
podramos tener un men contextual que aparezca al pulsar sobre un elemento en concreto de
la lista y que permita editar su texto o eliminarlo de la lista.
La creacin y utilizacin de este tipo de mens contextuales son muy parecidas a las
de los mens y submens bsicos que hemos visto anteriormente, aunque presentan algunas
particularidades que vamos a tratar a continuacin.
Vamos a partir del ejemplo 4 de esta Unidad, al que vamos a aadir un men
contextual que aparece al pulsar sobre la etiqueta de texto donde mostramos la opcin
seleccionada y un ListView con elementos sobre los que pulsar seguidamente y mostrar
opciones de edicin.
Lo primero que debemos hacer es indicar en el mtodo onCreate() de la Actividad que
la etiqueta y el listado tienen asociado un men contextual. Esto se hace usando la funcin
registerForContextMenu():

167

public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.main);

//Obtenemos las referencias a los componentes

labelResultado = (TextView)findViewById(R.id.labelResultado);
listadoPrincipal = (ListView)findViewById(R.id.ListadoPrincipal);

//Rellenamos la lista con datos de ejemplo

adaptador = new ArrayAdapter<String>(this,


android.R.layout.simple_list_item_1, datos);
listadoPrincipal.setAdapter(adaptador);

//Asociamos los mens contextuales a los componentes

registerForContextMenu(labelResultado);

registerForContextMenu(listadoPrincipal);
}// end onCreate

A continuacin, de igual forma que hicimos con los mens bsicos para crear las
opciones disponibles con el mtodo onCreateOptionsMenu(), vamos a construir los mens
contextuales asociados a los diferentes componentes de la aplicacin con el mtodo
onCreateContextMenu(). A diferencia del mtodo onCreateOptionsMenu() Android invoca
este mtodo cada vez que es necesario mostrar un men contextual. Este mtodo lo
implementaremos de misma forma que los mens bsicos, inflndolo con un archivo de
diseo XML o crendolo con sentencias Java. En este ejemplo hemos decidido disear los
mens en XML. El men contextual que aparece en la etiqueta se define en el fichero
menu_context_etiqueta.xml:

<?xml version="1.0" encoding="utf-8"?>


<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/ContextLabelOp1"
android:title="Opcin 1 de etiqueta"></item>
<item android:id="@+id/ContextLabelOp2"
android:title="Opcin 2 de etiqueta"></item>
</menu>

168

Ms informacin sobre Android


El men contextual que aparece en el ListView se define en el fichero
menu_context_lista.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/EditTextOp"
android:title="Editar texto opcin"></item>
<item android:id="@+id/ReiniciaTextOp"
android:title="Reiniciar texto opcin"></item>
</menu>

Para implementar el mtodo onCreateContextMenu() hay que tener en cuenta que


definimos varios mens contextuales en la misma Actividad, por lo que hay que construir un
men distinto dependiendo del componente asociado. Para hacerlo, obtenemos el ID del
componente al que se va a ir asociado el men contextual, que se recibe en el parmetro
(View v) del mtodo onCreateContextMenu() utilizando el mtodo getId() de dicho
parmetro:

// Mtodo donde definimos el men contextual cuando se despliega


public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
//Inflador del men contextual
MenuInflater inflater = getMenuInflater();
// Si el componente que vamos a dibujar es la etiqueta usamos
// el fichero XML correspondiente
if(v.getId() == R.id.labelResultado)
inflater.inflate(R.menu.menu_context_etiqueta, menu);
// Si el componente que vamos a dibujar es el ListView usamos
// el fichero XML correspondiente
else if(v.getId() == R.id.ListadoPrincipal)
{
AdapterView.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo)menuInfo;

169

// Definimos la cabecera del men contextual


menu.setHeaderTitle(
listadoPrincipal.getAdapter().getItem(info.position).toString());
// Inflamos el men contextual
inflater.inflate(R.menu.menu_context_lista, menu);
}
}

En el caso del men contextual para el listado hemos personalizado el ttulo del men
contextual mediante el mtodo setHeaderTitle(), para que muestre el texto del elemento
seleccionado en el listado.
Para hacer esto es necesario conocer la posicin del elemento seleccionado en el
listado mediante el ltimo parmetro menuInfo. Este parmetro contiene informacin adicional
del componente sobre el que el usuario ha pulsado para mostrar el men contextual. En este
caso en particular del componente ListView contiene la posicin del elemento pulsado. Para
obtenerlo, hacemos un cambio de formato (typecasting) del parmetro menuInfo a un objeto
del tipo AdapterContextMenuInfo y accedemos a su propiedad position.
Por ltimo, para implementar las acciones que hay que ejecutar cuando el usuario
selecciona una opcin determinada del men contextual vamos a implementar el mtodo
onContextItemSelected() de manera similar a cmo hacamos con onOptionsItemSelected()
para los mens bsicos:

// Si el usuario selecciona una opcin del men contextual mostramos


// la opcin seleccionada en la etiqueta
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info =
(AdapterContextMenuInfo) item.getMenuInfo();

switch (item.getItemId()) {
// Se selecciona la opcin 1 de men contextual de la etiqueta
case R.id.ContextLabelOp1:
labelResultado.setText("Etiqueta: Opcin 1");
return true;

170

Ms informacin sobre Android

// Se selecciona la opcin 2 de men contextual de la etiqueta


case R.id.ContextLabelOp2:
labelResultado.setText("Etiqueta: Opcin 2");
return true;

// Se selecciona la opcin "Editar texto opcin" de men contextual


// de la etiqueta
case R.id.EditTextOp:
labelResultado.setText("Opcin " + info.position + " listado:
Cambio de texto");
// Cambiamos el contenido de la matriz de datos
datos[info.position]="Opcin "+info.position+" listado
modificado";
// Reiniciamos el adaptador para que recargue los datos y
// actualice el ListBox
adaptador.notifyDataSetChanged();
return true;
// Se selecciona la opcin "Reiniciar texto opcin" de men
// contextual de la etiqueta
case R.id.ReiniciaTextOp:
labelResultado.setText("Opcin " + info.position + " listado:
Reinicio texto");
datos[info.position]="Opcin "+info.position+" listado";
adaptador.notifyDataSetChanged();
return true;
default:
return super.onContextItemSelected(item);
}
}

Fjate en el cdigo anterior que se puede mantener pulsado el dedo sobre la etiqueta
azul o sobre una opcin del listado y seleccionar una de las opciones del men contextual.

En el cdigo anterior tambin hemos

utilizado

la

informacin

del objeto

AdapterContextMenuInfo para saber qu elemento de la lista se ha pulsado aunque, en esta

171

ocasin, lo obtenemos llamando al mtodo getMenuInfo() de la opcin de men MenuItem


recibida como parmetro.
Adems,

para

modificar

la

opcin

del

listado

hemos

usado

el

mtodo

notifyDataSetChanged() del adaptador para que se recarguen los elementos del listado una
vez hemos modificado el texto de uno de ellos.

Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Men contextual) de la Unidad 3.


Estudia el cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que
usamos un men contextua.

Para ver cmo funciona esta aplicacin que usa un men contextual inicia este
ejemplo en el emulador. Vers la pantalla siguiente:

3.5.3

Ventanas de dilogo en Android


El programador utiliza a menudo pequeas ventanas de dilogo que para que el

usuario tome una decisin o reciba un mensaje informativo.


A diferencia de una ventana completa, estas pequeas ventanas de dilogo tienen la
particularidad de que pueden aparecer flotando sobre la pantalla de fondo, que queda
inactiva.
Android define cuatro tipos diferentes de ventanas de dilogo:
172

Ms informacin sobre Android


1. AlertDialog: puede contener hasta tres botones (incluso ninguno) o mostrar una lista
de elementos seleccionables como CheckBox o RadioButton. Es recomendable
utilizar este tipo de ventana de dilogo en la mayora de las aplicaciones. Se hereda de
la clase Dialog de Android.
2. ProgressDialog: muestra una barra de progreso o la tpica rueda de progreso. Se
hereda de la clase AlertDialog y, por lo tanto, tambin se pueden incluir botones.
3. DatePickerDialog: este dilogo permite seleccionar una fecha.
4. TimePickerDialog: este dilogo permite seleccionar una hora.
Una ventana de dilogo siempre se crea y se muestra como parte de una Actividad.
Normalmente debemos crear la ventana de dilogo en el mtodo onCreateDialog() de
la Actividad para asociarla a sta.
En el caso de que creemos una ventana de dilogo fuera del mtodo anterior,
debemos indicar qu Actividad es la que alberga la ventana de dilogo mediante
setOwnerActivity(Activity).
Para mostrar un dilogo desde cualquier parte del cdigo, hay que usar el mtodo
showDialog() indicando como parmetro un entero nico en la actividad que identifica el
dilogo que queremos mostrar. En el ejemplo 5 de esta unidad definimos estos identificadores
as:

private static final int DIALOGO_MENSAJE = 1;


private static final int DIALOGO_DOS_BOTONES = 2;
private static final int DIALOGO_TRES_BOTONES = 3;
...

Desde Eclipse puedes abrir el proyecto Ejemplo 5 (Ventanas de dilogo) de la Unidad 3.


Estudia el cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que
usamos varios tipos de ventanas de dilogo

A continuacin, explicamos algunos tipos de ventanas de dilogo que aparecen en el


ejemplo 5:

173

3.5.3.1

Ventanas de dilogo con mensaje

Es una ventana de dilogo que obliga a que el usuario vea un mensaje bloqueando la
pantalla hasta que pulse la tecla volver

. Sus mtodos ms importantes son stos:

setTitle(): establece el ttulo de la ventana de dilogo.

setMessage(): indica el mensaje que contiene la ventana de dilogo.

setIcon():establece la propiedad Icon con una de las imgenes predefinidas del


sistema.

show(): muestra la ventana de dilogo.

Si hacemos clic sobre el primer botn del ejemplo del curso, veremos que aparece el
siguiente mensaje:

El cdigo fuente es el siguiente:


ventana = new AlertDialog.Builder(this);
ventana.setTitle("Atencin");
ventana.setMessage("Tienes un mensaje nuevo. Pulsa el botn Atrs para volver
a la pantalla principal.");
ventana.setIcon(android.R.drawable.ic_dialog_email);
ventana.show();

3.5.3.2

Ventanas de dilogo con botones

En esta ventana, a diferencia de la anterior, se obliga al usuario a pulsar uno de los


botones creados mediante los mtodos setPositiveButton(), setNegativeButton() o
setNeutralButton().
Adems, con el mtodo setCancelable() podemos inhabilitar la tecla de escape, para
evitar que el usuario cierre la ventana sin tomar una decisin.
Dispone adems, de los mismos mtodos de la ventana anterior.

174

Ms informacin sobre Android


Si hacemos clic sobre el segundo o el tercer botn del ejemplo del curso, veremos que
aparecen los siguientes mensajes:

El cdigo fuente es el siguiente:


ventana = new AlertDialog.Builder(this);
ventana.setIcon(android.R.drawable.ic_dialog_info);
ventana.setTitle("Encuesta");
ventana.setMessage("Te gusta la msica clsica?");
ventana.setCancelable(false);
ventana.setPositiveButton("S", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int boton) {
/* Sentencias si el usuario pulsa S */
Toast.makeText(getApplicationContext(),

"Has

pulsado

el

botn

'S'",

1).show();
}
});
ventana.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int boton) {
/* Sentencias si el usuario pulsa No */
Toast.makeText(getApplicationContext(), "Has pulsado el botn
'No'",1).show();
}
});
ventana.setNeutralButton("A veces", new

DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int boton) {


/* Sentencias si el usuario pulsa A veces */
Toast.makeText(getApplicationContext(),

"Has

pulsado

el

botn

'A

veces'", 1).show();
}

175

});
ventana.show();

3.5.3.3

Ventanas de dilogo con seleccin

Esta ventana permite al usuario seleccionar una nica opcin de un listado de


RadioButton. Dispone de los mtodos vistos anteriormente, salvo setMessage(), ya que
vamos a usar el mtodo setSingleChoiceItems() para definir las posibles opciones del listado.
Si hacemos clic sobre el sexto botn del ejemplo del curso, veremos que aparece el
siguiente mensaje:

El cdigo fuente es el siguiente:


ventana = new AlertDialog.Builder(this);
ventana.setIcon(android.R.drawable.ic_dialog_info);
ventana.setTitle("Selecciona un modelo de telfono");

// No se puede incluir un mensaje dentro de este tipo de dilogo!!!


final CharSequence[] telefonos = {"iPhone", "Nokia", "Android"};
ventana.setSingleChoiceItems(telefonos, 0, new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int opcion) {
// Evento que ocurre cuando el usuario selecciona una opcin
Toast.makeText(getApplicationContext(),
"Has
seleccionado
"
+telefonos[opcion], Toast.LENGTH_SHORT).show();
}
});

176

Ms informacin sobre Android


ventana.setPositiveButton("Aceptar", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int boton) {
Toast.makeText(getApplicationContext(), "Has pulsado el botn
'Aceptar'", 1).show();
}
});
ventana.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int boton) {
Toast.makeText(getApplicationContext(), "Has pulsado el botn
'Cancelar'", 1).show();
}
});
ventana.show();

3.5.3.4

Ventanas de dilogo personalizada

Esta ventana permite al programador disear mediante archivos XML la ventana de


dilogo que queremos mostrar al usuario. Dispone de los mtodos vistos anteriormente, salvo
setMessage(), ya que vamos a usar el mtodo setView() para definir el contenido de la
ventana.
Si hacemos clic sobre el octavo botn del ejemplo del curso, veremos que aparece el
siguiente mensaje:

El cdigo fuente es el siguiente:


// Primero preparamos el interior de la ventana de dilogo inflando su fichero
XML
String infService = Context.LAYOUT_INFLATER_SERVICE;

177

LayoutInflaterli
=(LayoutInflater)getApplicationContext().getSystemService(infService);
// Inflamos el componente compuesto definido en el XML
View inflador = li.inflate(R.layout.dialogo_entrada_texto, null);
// Buscamos los componentes dentro del Dilogo
final TextView nombreEdit = (TextView)
inflador.findViewById(R.id.nombre_edit);
final TextView passwordEdit = (TextView)
inflador.findViewById(R.id.password_edit);
ventana = new AlertDialog.Builder(this);
ventana.setTitle("Indica usuario y contrasea");
// Asignamos
previamente)

el

contenido

dentro

del

dilogo

(el

que

hemos

inflado

ventana.setView(inflador);
// No se puede incluir un mensaje dentro de este tipo de dilogo!!!
ventana.setPositiveButton("Aceptar", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int boton) {
Toast.makeText(getApplicationContext(), "Has escrito el nombre '"+
nombreEdit.getText().toString() +"', la contrasea '"+
passwordEdit.getText().toString() +"' y has pulsado el
botn 'Aceptar'", 1).show();
}
});
ventana.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int boton) {
Toast.makeText(getApplicationContext(), "Has pulsado el botn
'Cancelar'", 1).show();
}
});
ventana.show();

Nota: es muy importante no usar el mtodo setMessage() cuando usemos ventanas de


dilogo cuyo interior se define mediante listas de seleccin o un archivo de diseo XML, ya
que no se mostrar bien la ventana si lo hacemos.

En el Ejemplo 5 de esta Unidad puedes ver otros tipos de ventanas de dilogo muy

178

Ms informacin sobre Android


similares a las estudiadas en este apartado. Recomendamos al alumno o alumna que estudie a
fondo el cdigo fuente de este ejemplo.

179

Una aplicacin puede estar compuesta de una o varias Actividades.


Una Tarea es un conjunto de actividades con las que un usuario interacta.
Android organiza las actividades en una pila de ejecucin (en ingls stack)
donde se van apilando las actividades que el usuario invoca.
Es posible mantener muchas tareas en segundo plano a la vez; sin embargo, si
el sistema operativo necesita memoria, puede destruirlas perdiendo sus estados de
ejecucin.
La configuracin de un dispositivo Android puede cambiar mientras se ejecuta
una aplicacin; por ejemplo, pueden cambiar la orientacin de la pantalla
(vertical/horizontal), la disponibilidad de teclado, el idioma, etctera. Cuando esto
ocurre la Actividad se destruye.
Para almacenar la informacin importante sobre el estado de ejecucin de una
Actividad podemos usar el mtodo de llamada onSaveInstanceState() y
despus restaurar esta informacin cuando el sistema vuelva a crear la Actividad.
No

hay

garantas

de

que

Android

invoque

siempre

el

mtodo

onSaveInstanceState() justo antes de que la Actividad se destruya, ya que hay


casos en los que no es necesario guardar el estado de ejecucin; por ejemplo,
cuando el usuario sale de la aplicacin usando la tecla Volver atrs.
Debido a que no se garantiza que Android invoque siempre el mtodo
onSaveInstanceState(), debe usarse nicamente para almacenar el estado
transitorio de la Actividad, es decir, la interfaz de usuario. Nunca debe emplearse
para almacenar datos persistentes con preferencias del usuario o datos de la
aplicacin.
Un hilo es una tarea que se ejecuta en paralelo con otra tarea.
Todos los componentes de una misma aplicacin Android se ejecutan en el mismo
hilo principal (en ingls se denomina "main thread"). A este hilo tambin se lo
denomina hilo de la interfaz de usuario (en ingls "UI thread").

180

El sistema operativo Android sigue el modelo de ejecucin de aplicaciones


denominado Modelo de ejecucin de un nico hilo (en ingls Single Thread Model).
Hay tener en cuenta dos reglas cuando se disean aplicaciones en Android:
1. No bloquear nunca el hilo principal ni el de la interfaz de usuario.
2. No acceder a la interfaz de usuario de Android desde un hilo exterior.

Si una aplicacin Android tiene que realizar operaciones que no son instantneas y
llevan cierto tiempo, hay que ejecutarlas en hilos separados en segundo plano.
Un Men es una serie de opciones que el usuario puede elegir para realizar una
determinada tarea.
En las aplicaciones Android podemos utilizar tres tipos de mens diferentes:
1. Mens Principales: aparecen en la zona inferior de la pantalla al pulsar el
botn Men del telfono.
2. Submens: mens secundarios que aparecen al elegir una opcin de un
men principal.
3. Mens Contextuales: se muestran al realizar una pulsacin larga sobre
algn elemento de la pantalla. Es el equivalente al botn derecho del ratn
en un PC.
Como es habitual en Android, existen dos formas de crear un men en una
aplicacin Android: mediante un fichero XML e "inflndolo" despus o creando el
men directamente mediante cdigo Java.
Las ventanas de dilogo son pequeas ventanas que el programador utiliza a
menudo para que el usuario tome una decisin o reciba un mensaje informativo.

181

Unidad de Aprendizaje 9

EFECTOS
TRABAJANDO
DE TRANSICIN
CON
Y
FICHEROS
ANIMACIN

NDICE
4.1 FICHEROS EN ANDROID .......................................................... 185
4.1.1 Introduccin ..................................................................... 185
4.1.2 Gestin de informacin en Android ................................. 185
4.1.3 Gestin del sistema de archivos en Android ................... 185
4.1.4 Clase Fichero File ............................................................. 186
4.1.4.1 Constructores ms importantes .............................. 186
4.1.4.2 Mtodos ms importantes....................................... 187
4.1.5 Ficheros en la memoria interna del diapositivo ............... 188
4.1.6 Fichero de recurso de la aplicacin ................................. 190
4.1.7 Fichero en almacenamiento externo................................ 191
4.1.8 Aadir datos a un fichero ................................................. 196
4.1.9 Gestionando las excepciones en la gestin de ficheros . 196
4.2 PREFERENCIAS DE UNA APLICACIN ANDROID ................. 197
4.2.1 Preferencia de las aplicaciones Android.......................... 197
4.2.2 Pantallas de opciones ...................................................... 200
4.3 RECURSOS DE LAS APLICACIONES ANDROID ..................... 207
4.3.1 Tipos de recursos............................................................. 208
4.3.2 Crear literales en ficheros de recursos con Eclipse......... 208
4.3.3 Recursos de tipo Cadena (String Resources) .................. 209
4.3.4.1 Cadena (String) ....................................................... 210
4.3.4.2 Matriz de cadenas (String Array)............................. 211
4.3.4.3 Cadenas de cantidad (Quantity Strings) ................. 212
4.3.4 Formatear y cambiar el estilo de las cadenas de recursos213
4.3.4.1 Escapando comillas simples y dobles.................... 213
4.3.4.2 Formatear cadenas de texto................................... 214
4.3.4.3 Cambio de estilo ..................................................... 214
4.4 ACCESO A INTERNET CON ANDROID ..................................... 215
4.4.1 Ejemplo de conexin a Internet........................................ 216
4.4.2 Conexin a travs de proxy ............................................. 220
4.5 QU SON JSON (JavaScript Object Notation? ........................ 221
4.5.1 Cmo usar JSON en nuestras aplicaciones a Android.... 222
4.5.2 Cmo escribir ficheros en formato JSON ........................ 225

4.1

4.1.1

FICHEROS EN ANDROID

Introduccin
En esta Unidad vamos a explicar cmo gestionar ficheros en Android, tanto en la

memoria interna del telfono como en su tarjeta de memoria externa SD.


Adems, veremos cmo almacenar las preferencias de una Aplicacin de Android.
Tambin detallaremos el uso de ficheros JSON en Android.
Finalmente, accederemos a Internet desde aplicaciones Android y veremos lo que
son los Recursos (Resources).

4.1.2

Gestin de informacin en Android


En Android existen tres formas de almacenar informacin, para poder usarla en las

aplicaciones:

Preferencias de la aplicacin

Ficheros locales en el sistema de archivos del sistema operativo

Base de datos SQLite

En esta Unidad 4 trataremos las dos primeras formas, y en la Unidad 6 veremos las
bases de datos.

4.1.3

Gestin del sistema de archivos en Android


Como en cualquier sistema operativo, en Android tambin podemos manipular

ficheros de forma muy parecida a como se hacen en Java.

185

En Android, por defecto, los ficheros son privados y nicamente puede acceder a ellos
la aplicacin que los crea. Para compartir informacin de estos ficheros se utilizan los Content
Providers que veremos en la Unidad 7.
Lo primero que hay debemos tener en cuenta es dnde queremos almacenar estos
ficheros y la manera en que vamos a acceder a ellos: lectura o escritura.
Podemos leer y escribir ficheros localizados en:
1. La memoria interna del dispositivo: como fichero o como recurso de la
aplicacin.
2. La tarjeta SD externa, si existe, tambin denominada almacenamiento
externo.

4.1.4

Clase Fichero File


La clase File de Android se usa para identificar y gestionar archivos y directorios del

sistema operativo. Un archivo en Android puede estar identificado por su ruta absoluta,
relativa al directorio raz del sistema de archivos, o por su ruta relativa, que es directorio actual
en el que se ejecuta la aplicacin. Esta clase File est basada en la clase de Java.
El acceso a los ficheros es similar al Java estndar: se deben crear inputs y outpus
streams.
La clase File puede hacer referencia a un archivo que ya exista o a uno que vayamos a
crear. Aunque el nombre de esta clase sea File, tambin puede, referirse a un directorio o un
enlace (link) de Linux.
Esta clase proporciona una funcionalidad limitada para obtener y establecer permisos
del archivo, cambiar el tipo de archivo o establecer su fecha de ltima modificacin.

4.1.4.1

Constructores ms importantes
File(File dir, String nombre): crea un fichero usando el nombre y el
directorio especificados como parmetros.

File(String dir): crea un fichero en el directorio especificado.

File(URI uri): crea un fichero usando el camino especificado en el parmetro


URI (del ingls Uniform Resource Identifier), que es un identificador uniforme
de recurso. Se trata de una cadena de caracteres corta que identifica
inequvocamente un recurso (servicio, pgina, documento, direccin de correo
electrnico, enciclopedia, etctera.).

186

4.1.4.2

Mtodos ms importantes

boolean canExecute(): comprueba si la aplicacin puede ejecutar el fichero.

boolean canRead(): indica si el fichero se puede leer.

boolean canWrite(): comprueba si la aplicacin puede escribir datos en el


fichero.

boolean createNewFile(): crea un archivo nuevo y vaco en el sistema de


archivos de acuerdo con la ruta almacenada.

static File createTempFile(String prefijo, String sufijo, File directorio): crea


un archivo temporal vaco en el directorio establecido usando un prefijo y un
sufijo para formar el nombre del mismo.

static File createTempFile(String prefix, String suffix): crea un archivo


temporal vaco usando un prefijo y un sufijo para formar el nombre del mismo.

boolean delete(): borra el fichero.

boolean exists(): indica si existe un fichero.

String getAbsolutePath(): obtiene el camino absoluto del fichero.

long getFreeSpace(): indica el nmero de bytes libres en la particin actual


(donde se encuentra el fichero).

String getName(): devuelve el nombre del archivo o del directorio que


representa al archivo.

String getPath(): devuelve el camino del fichero.

long getTotalSpace(): indica el tamao total en bytes de la particin actual


(donde se encuentra el fichero).

boolean isAbsolute(): indica si el camino del fichero es absoluto.

boolean isDirectory(): indica si la clase File contiene un directorio.

boolean isFile(): indica si la clase File contiene un archivo de datos.

boolean isHidden(): indica si el fichero es de tipo oculto en el sistema


operativo.

long lastModified(): devuelve la fecha de la ltima modificacin del archivo


medido en nmero de milisegundos desde el 1 de enero 1970.

long length(): indica el tamao del fichero en bytes.

String[] list(): devuelve un listado con los nombres de todos los ficheros y
subdirectorios contenidos en un directorio.

File[] listFiles(): devuelve un listado de tipo File con todos los ficheros y
subdirectorios contenidos en un directorio.

boolean mkdir(): crea un subdirectorio en el directorio actual.

187

4.1.5

boolean mkdirs(): crea recursivamente un subdirectorio incluyendo todos los


subdirectorios superiores que completan el camino.

boolean renameTo(File nuevoNombre): renombra un fichero.

boolean setExecutable(boolean executable, boolean ownerOnly): Cambia


los permisos del fichero indicando si es un ejecutable y si nicamente lo puede
ejecutar el usuario que lo ha creado.

boolean setLastModified(long
modificacin del fichero.

boolean setReadable(boolean readable, boolean ownerOnly): cambia los


permisos del fichero indicando si se puede leer y si nicamente lo puede leer el
usuario que lo ha creado.

boolean setWritable(boolean writable, boolean ownerOnly): cambia los


permisos del fichero indicando si se puede escribir en l y si nicamente puede
escribir en el mismo el usuario que lo ha creado.

URI toURI(): devuelve el identificador URI del fichero.

time):

establece

la

ltima

fecha

de

Ficheros en la memoria interna del diapositivo


Al almacenar archivos en la memoria interna debemos tener en cuenta las limitaciones

de espacio que tienen muchos dispositivos, por lo que no deberamos abusar de este espacio
guardando ficheros de gran tamao.
Crear ficheros en la memoria interna es muy sencillo. Android dispone del mtodo
openFileOutput(), que recibe como parmetros el nombre del fichero y el modo de acceso al
mismo. Este modo de acceso puede ser:
o

MODE_PRIVATE: es el valor por defecto, que nicamente permite el acceso privado


desde nuestra aplicacin.

MODE_APPEND: permite aadir datos a un fichero ya existente.

MODE_WORLD_READABLE: permite a otras aplicaciones leer el fichero.

MODE_WORLD_WRITABLE: permite a otras aplicaciones escribir en el fichero.


El mtodo openFileOutput() devuelve una referencia a un objeto de tipo stream de

Java asociado al fichero (ms en concreto, se trata de un objeto FileOutputStream), a partir


del cual podemos utilizar los mtodos de manipulacin de ficheros tradicionales del lenguaje
Java.
En el siguiente ejemplo convertimos este stream al tipo OutputStreamWriter para
escribir una cadena de texto en el fichero:
try
{

188

OutputStreamWriter fileout=
new OutputStreamWriter(openFileOutput("fichero_interno.txt",
Context.MODE_PRIVATE));
fileout.write("En un lugar de la Mancha...");
fileout.close();
}
catch (Exception excepcion)
{
Log.e("Fichero", "Error al escribir fichero en memoria interna");
}

Android almacena este archivo de texto en la memoria interna del dispositivo, en un


directorio determinado y siempre el mismo, sin que el programador pueda cambiarlo. Sigue
este patrn:
/data/data/paquete_java/files/nombre_del_fichero
En el ejemplo anterior se almacena en:
/data/data/es.mentor.unidad4.eje1.ficheros/files/fichero_interno.txt
Si ejecutamos el Ejemplo 1 de esta Unidad podemos comprobar en el DDMS cmo se
crea el fichero correctamente en el directorio indicado. Para acceder a esta herramienta dentro
de Eclipse, hay que hacer Clic en la opcin del men principal: Window -> Open Perspective
-> DDMS:

189

Leer ficheros desde la memoria interna es igual de sencillo. Procedemos de forma


anloga utilizando esta vez el mtodo openFileInput(), para abrir el fichero y usamos los
mtodos de lectura mostrados anteriormente para leer el contenido.
try
{
BufferedReader filein = new BufferedReader(
new InputStreamReader(openFileInput("fichero_interno.txt")));

String texto = filein.readLine();


filein.close();
}
catch (Exception ex)
{
Log.e("Ficheros", "Error al leer fichero de memoria interna");
}

4.1.6

Fichero de recurso de la aplicacin


Otra forma de almacenar ficheros en la memoria interna del dispositivo es incluirlos

como un recurso de la propia aplicacin. Aunque este mtodo es muy til, nicamente
debemos utilizarlo cuando no necesitemos realizar modificaciones sobre el fichero, ya que el
acceso al mismo es de slo lectura.
Para incluir un fichero como recurso de la aplicacin debemos colocarlo en la carpeta
/res/raw del proyecto Android. Esta carpeta no suele estar creada por defecto, por lo que
debemos crearla manualmente en Eclipse.
Una vez creada la carpeta raw, podemos aadir en ella cualquier fichero que
necesitemos incluir con la aplicacin en tiempo de compilacin en forma de recurso. En el
Ejemplo 1 de esta Unidad hemos incluido el fichero de texto prueba_raw.txt.
Posteriormente, en tiempo de ejecucin, podemos acceder a este fichero, slo en modo de
lectura, de una forma similar a la que ya hemos visto anteriormente para el resto de ficheros en
la memoria interna del dispositivo.
Estos ficheros de tipo recurso tambin pueden ser binarios: por ejemplo, imgenes,
vdeos, etctera.
En primer lugar, para acceder a este fichero, obtenemos los recursos de la aplicacin
con
190

el

mtodo

getResources()

sobre

ste

utilizamos

el

mtodo

openRawResource(id_del_recurso) para abrir el fichero en modo lectura. Este mtodo


devuelve un objeto de tipo InputStream que podemos manipular.
En el ejemplo del curso, convertimos el InputStream en un objeto BufferedReader
para leer el texto contenido en el fichero, tal y como haramos en Java. A continuacin,
mostramos el cdigo fuente:
try
{
InputStream ficheroraw = getResources().openRawResource(R.raw.prueba_raw);
BufferedReader brin = new BufferedReader(new InputStreamReader(ficheroraw));
while (true) {
texto = brin.readLine();
// Si ya no hay ms lneas que leer hemos acabado de leer el fichero
if (texto==null) break;
resultado.append("\n"+Html.fromHtml(texto));
} // end while
ficheroraw.close();
}
catch (Exception ex)
{

Log.e("Ficheros", "Error al leer fichero de recurso de aplicacin");


}

4.1.7

Fichero en almacenamiento externo


La mayora de los dispositivos Android disponen de una tarjeta SD externa para que el

sistema tenga un espacio extra de almacenamiento de informacin. Normalmente en esta


tarjeta se guardan las fotos, los vdeos y, en las versiones ltimas de Android, incluso se
instalan aplicaciones.
A diferencia de la memoria interna, la memoria externa no tiene por qu estar presente
en el dispositivo o puede que el sistema no reconozca su formato. Por lo tanto, antes de usar
ficheros en la memoria externa, hay que comprobar que dicha memoria est presente y
disponible para leer y/o escribir en ella.
Para esto, Android proporciona en la clase Environment el mtodo esttico
getExternalStorageStatus(), que indica si la memoria externa est disponible y si se puede
leer y escribir en ella. Este mtodo devuelve una serie de valores que sealan el estado de la
memoria externa. Entre ellos, los ms importantes son los siguientes:

191

MEDIA_MOUNTED: indica si la memoria externa est disponible y es posible leer y


escribir en ella.

MEDIA_MOUNTED_READ_ONLY: indica que la memoria externa est disponible, pero


nicamente podemos leer informacin.

Otros valores que indicarn que existe algn tipo de problema y que, por lo tanto, no
podemos usar la memoria externa (MEDIA_UNMOUNTED, MEDIA_REMOVED, etctera).
Se puede consultar todos estos valores en la documentacin oficial de la clase
Environment.
Teniendo en cuenta esto, en el Ejemplo 1 realizamos comprobaciones previas del

estado de la memoria externa del dispositivo con el siguiente mtodo:

// Mtodo que comprueba si el almacenamiento externo est activo y si se puede


escribir en la tarjeta.
private void compruebaAlmacenamientoExt(){

// Obtenemos el estado del almacenamiento externo del telfono


String estado = Environment.getExternalStorageState();
// La tarjeta est activa y se puede escribir en ella
if (Environment.MEDIA_MOUNTED.equals(estado)) {
hayAlmacenamientoExt = almacenamientoExtEscritura = true;
resultado.append("\n\nEl

telfono dispone de almacenamiento externo


conectado y se puede escribir en l.");

} else

// Slo se puede leer el almacenamiento externo


if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(estado)) {
hayAlmacenamientoExt = true;
almacenamientoExtEscritura = false;
resultado.append("\n\nEl

telfono dispone de almacenamiento externo


conectado pero no se puede escribir en l.");

} else {

// No se puede leer el almacenamiento externo


hayAlmacenamientoExt = almacenamientoExtEscritura = false;
resultado.append("\n\nEl

}
if (hayAlmacenamientoExt) {

192

telfono

no

tiene

ningn
almacenamiento
externo conectado.");

// Mostramos el directorio donde est el almacenamiento externo

File dir = android.os.Environment.getExternalStorageDirectory();


dirAlmacExt=dir.getAbsolutePath();
resultado.append("\n\nDirectorio almacenamiento externo: "+dir);

}
} // end compruebaTarjetaSD

Una vez comprobado el estado de la memoria externa y dependiendo del resultado


obtenido, podemos leer o escribir en ella cualquier tipo de fichero.
Hemos aprovechado el mtodo anterior para obtener la ruta al directorio raz de esta
memoria. Para ello utilizamos el mtodo getExternalStorageDirectory() de la clase
Environment, que devuelve un objeto File con la ruta de dicho directorio.
Empecemos por la funcionalidad de escritura en la memoria externa. Lo primero que
hemos hecho ha sido crear el directorio ejemplo_curso_Mentor dentro de la tarjeta SD
usando el mtodo mkdirs de la clase File.
Despus, para escribir un fichero en ese directorio de la tarjeta SD creamos un nuevo
objeto File que combina ambos elementos. Para acabar, slo hay que encapsularlo en algn
objeto de escritura de ficheros de la API de Java y escribir algn dato. En este caso lo
convertimos de nuevo a un objeto OutputStreamWriter para escribir un archivo de texto. El
cdigo fuente es el siguiente:
//Si la memoria externa est disponible y se puede escribir
if (hayAlmacenamientoExt && almacenamientoExtEscritura)
{
try
{
// Creamos un directorio de prueba
File directorio = new File (dirAlmacExt + "/ejemplo_curso_Mentor");
directorio.mkdirs();
resultado.append("- Creamos el directorio " + dirAlmacExt +
"/ejemplo_curso_Mentor");
// Abrimos in fichero en el raz de la tarjeta SD
File fichero = new File(directorio, "prueba_sd.txt");
OutputStreamWriter fout = new OutputStreamWriter(new
FileOutputStream(fichero));
resultado.append("\n\n- Abrimos fichero '" + dirAlmacExt +
"/ejemplo_curso_Mentor/fichero_externo.txt' para escritura en
memoria externa");

193

fout.write("Caminante no hay camino se hace camino al andar...");


resultado.append("\n\n- Escribimos los datos");
fout.close();
resultado.append("\n\n- Cerramos fichero");
}
catch (Exception ex)
{
Log.e("Ficheros", "Error al escribir fichero en memoria externa");
resultado.append("Error al escribir fichero en memoria externa");
}
}

else

resultado.append("No

hay

almacenamiento externo disponible o no


se puede escribir en l.");

Hay que tener en cuenta que es preciso que especificar en el fichero


AndroidManifest.xml que la aplicacin necesita el permiso de escritura en la memoria
externa. Para "manifestar" que la aplicacin necesita este permiso usamos la clusula <usespermission> utilizando el valor android.permission.WRITE_EXTERNAL_STORAGE. El
fichero queda as:

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad4.eje1.ficheros"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />

<application android:icon="@drawable/icon" android:label="@string/app_name">


<activity android:name=".ficherosActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

194

</application>

<!-- Damos permisos a la aplicacin para que pueda escribir en la tarjeta SD


-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

Si ejecutamos ahora el Ejemplo 1 y abrimos al explorador de archivos del DDMS


podemos comprobar que se ha creado correctamente el fichero en el directorio especificado
de la tarjeta SD:

La forma de leer un fichero de la tarjeta SD es muy parecida a si estuviera almacenado


en la memoria interna del telfono; nicamente cambia el directorio del que leemos el fichero.
El cdigo fuente tiene este aspecto:
try
{
File fichero = new File(dirAlmacExt + "/ejemplo_curso_Mentor",
"prueba_sd.txt");
BufferedReader fin = new BufferedReader(new InputStreamReader(
new FileInputStream(fichero)));
resultado.append("- Abrimos fichero '"+ dirAlmacExt +
"/ejemplo_curso_Mentor/fichero_externo.txt' para lectura de memoria
externa");
String texto = fin.readLine();
resultado.append("\n\n- Leemos el contenido del fichero:\n");
resultado.append(texto);
fin.close();
resultado.append("\n\n- Cerramos fichero");

195

}
catch (Exception ex)
{
Log.e("Ficheros", "Error al leer fichero de memoria externa");
}

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Ficheros) de la Unidad 4. Estudia el


cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el
que hemos utilizado los mtodos de manipulacin de archivos

4.1.8

Aadir datos a un fichero


Por defecto, Android sobrescribe siempre un fichero si ste ya existe perdiendo los

datos almacenados anteriormente. Esto ocurre en los ejemplos anteriores.


Para poder aadir informacin a un fichero existente podemos cambiar el constructor
FileOutputStream(File fichero) por el constructor FileOutputStream (File fichero, Boolean
append), que permite indicar si queremos agregar la informacin al final del fichero con el
parmetro append =true.

4.1.9

Gestionando las excepciones en la gestin de ficheros


Fjate en que todo el cdigo fuente que gestiona el fichero est contenido dentro de un

bloque try-catch. De esta manera se controlan los errores de ejecucin de las sentencias
mediante el manejo de excepciones estndar de Java.
Por ejemplo, Android puede lanzar las siguientes excepciones cuando ocurre un error
al manipular un fichero:
1. Si el fichero indicado en el parmetro del constructor FileOutputStream(File)
no existe, se lanza la excepcin FileNotFoundException.

2. Si ocurre algn error al cerrar un fichero con el mtodo close(), entonces


Android lanza la excepcin IOException.
El programador debe gestionar esas Excepciones para poder mostrar al usuario un
mensaje del sistema operativo que entienda, en lugar de en ingls. La forma estndar de
hacerlo tiene el siguiente aspecto:

196

try {
//

Sentencias
excepciones

que

queremos

ejecutar

que

pueden

lanzar

.
.
} catch (ExceptionType excepcion1) {
// Sentencias que gestiona la excepcin 1
.
.
} catch (ExceptionType excepcion2) {
// Sentencias que gestiona la excepcin 2
.
.
}

En el caso del Ejemplo 1 del curso hemos capturamos todas las excepciones en un
nico bloque para mostrar al usuario y en la consola de Eclipse un mensaje sencillo de error:

catch (Exception ex)


{
Log.e("Ficheros", "Error al escribir fichero en memoria externa");
resultado.append("Error al escribir fichero en memoria externa");

4.2

4.2.1

PREFERENCIAS DE UNA APLICACIN ANDROID

Preferencia de las aplicaciones Android


Las preferencias de una aplicacin son datos que una aplicacin guarda y

recupera para personalizar la experiencia del usuario. Por ejemplo, se debe almacenar
informacin personal, configuracin de la aplicacin, opciones de presentacin, etctera.
En el apartado anterior hemos visto cmo almacenar informacin en ficheros. Las
preferencias de una aplicacin se podran almacenar utilizando este mtodo, aunque Android
proporciona otro mtodo alternativo diseado especficamente para administrar este tipo de
datos: las Preferencias compartidas (shared preferences en ingls).
197

Cada preferencia se almacena siguiendo la estructura clave-valor. Es decir, cada una


de ellas est compuesta por un identificador nico (por ejemplo, email) y un valor asociado a
dicho identificador (por ejemplo, correo@email.com). Estos datos se guardan en un fichero
XML.
Para gestionar las preferencias de una aplicacin hay que usar la clase
SharedPrefences. Una misma aplicacin Android puede gestionar varias colecciones de
preferencias que se diferencian por un identificador nico. Para obtener la referencia a una
coleccin determinada utilizamos el mtodo getSharedPrefences() indicando el identificador
de la coleccin y el modo de acceso a la misma. El modo de acceso indica qu aplicaciones
tienen acceso a esta coleccin de preferencias y qu operaciones podrn realizar sobre ellas.
Existen tres tipos de permisos principales:
o

MODE_PRIVATE:nicamente la aplicacin tiene acceso a las preferencias.

MODE_WORLD_READABLE:todas las aplicaciones pueden leer las preferencias, aunque


nicamente la matriz puede modificarlas.

MODE_WORLD_WRITABLE: todas las aplicaciones pueden leer y modificar las


preferencias.
Por

ejemplo,

para

obtener

la

referencia

la

coleccin

de

preferencias

MisPreferencias, con modo de acceso exclusivo,para la aplicacin que las gestiona,


escribimos la siguiente sentencia:

SharedPreferences preferencias =
getSharedPreferences("MisPreferencias",Context.MODE_PRIVATE);

Una vez hemos creado el objeto que nos permite acceder a la coleccin de
preferencias, podemos leer, insertar o modificar claves de preferencias utilizando los mtodos
get() o put() correspondientes al tipo de dato de cada preferencia. Por ejemplo, para obtener
el valor de la preferencia email de tipo String escribimos la siguiente setencia:

String correo = preferencias.getString("email", "correo@email.com");

Al mtodo getString() le pasamos como parmetros el nombre de la preferencia que


queremos leer y el valor por defecto (por si no contiene nada o no existe). A parte del mtodo
getString(), existen mtodos anlogos para el resto de tipos de datos bsicos de Java. Por
ejemplo, getInt(), getLong(), getFloat(), getBoolean(), etctera.
198

Actualizar o aadir nuevas claves de preferencias es muy parecido. En lugar de usar el


objeto SharedPreferences, utilizaremos SharedPreferences.Editor para editar preferencias.
Accedemos a este objeto mediante el mtodo edit() de la clase SharedPreferences.
Una vez obtenida la referencia al editor, utilizamos los mtodos put() oportunos en
funcin del tipo de dato de cada clave de preferencia para actualizar o insertar su valor.
Por ejemplo, putString(clave, valor) actualiza una preferencia de tipo String. De igual
forma, existen mtodos get() para todos los tipos de datos bsicos de Java: putInt(),
putFloat(), putBoolean(), etctera.
Para acabar, una vez actualizados/insertados todos las claves de preferencias
necesarias invocamos el mtodo commit() para confirmar los cambios. Fjate en el siguiente
ejemplo sencillo:

SharedPreferences preferencias =
getSharedPreferences("MisPreferencias", Context.MODE_PRIVATE);

SharedPreferences.Editor editor = preferencias.edit();


editor.putString("email", "correo@email.com");
editor.putString("nombre", "Pedro del Cielo Lindo");
editor.commit();

Las preferencias se almacenan en un fichero XML dentro de un directorio con el


siguiente patrn:
/data/data/paquetejava/shared_prefs/nombre_coleccion.xml
En este caso encontraremos el fichero de preferencias en el directorio:
/data/data/es.mentor.unidad4.eje2.preferencias/shared_prefs/MisPreferencias.xml
Si descargamos este fichero desde el DDMS y lo abrimos con cualquier editor de
texto, veremos el contenido siguiente:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>


<map>
<string name="nombre">Pedro del Cielo Lindo</string>
<string name="email">correo@email.com</string>
</map>

199

En este fichero XML observamos cmo se almacenan las dos preferencias del ejemplo
anterior con sus claves y valores correspondientes.

4.2.2

Pantallas de opciones
Si abrimos cualquier pantalla de preferencias estndar de Android, veremos que todas

comparten una interfaz comn, similar a la siguiente imagen (Preferencias de "Ajustes de


sonido" de Android):

Si nos fijamos en la imagen anterior, vemos que las distintas opciones se organizan
dentro de una pantalla de opciones que incluye varias categoras (General, Llamadas
entrantes, etctera).
Dentro de cada categora, aparecen varios tipos de opciones. Por ejemplo, de tipo
CheckBox (Modo Silencio) o de tipo lista de seleccin (Vibrar).
Android permite mejorar y simplificar la gestin de las preferencias de una aplicacin
mediante el uso de una pantalla de opciones.
Para definir la pantalla de opciones vamos a usar un fichero de diseo (layout) XML,
que guardamos en la carpeta /res/xml/pantallapreferencias.xml del proyecto.
El contenedor principal de nuestra pantalla de preferencias ser el elemento
<PreferenceScreen> que aparece en el fichero opciones.xml. Este elemento representa la
pantalla de opciones en s, dentro de la cual incluiremos el resto de componentes de la interfaz
de usuario.
Dentro de esta pantalla podemos incluir una lista de opciones organizadas por
categoras, que se representan mediante el elemento <PreferenceCategory>, al que
200

aadimos un texto descriptivo utilizando el atributo android:title. Dentro de cada categora


podemos incluir varias opciones. stas pueden ser de distintos tipos, por ejemplo:

CheckBoxPreference: caja seleccionable.

EditTextPreference: caja de texto.

ListPreference: lista con valores seleccionables (slo uno).

MultiSelectListPreference: lista con valores varios seleccionables.


A continuacin, vamos a describir los diferentes atributos de estos tipos de opciones:

CheckBoxPreference

Se usa para introducir una opcin de preferencia que slo puede tener dos valores:
activada (marcada) o desactivada (desmarcada). Es el equivalente al componente de tipo
CheckBox. En este caso, hay que especificar los atributos: nombre interno de la opcin
(android:key),

texto

que

muestra

(android:title)

descripcin

de

la

opcin

(android:summary). Veamos un ejemplo:

<CheckBoxPreference android:key="opcion1"
android:title="Bsqueda automtica"
android:summary="Iniciar bsqueda automtica en Internet" />

EditTextPreference

Se utiliza para introducir una opcin de preferencia que contiene una cadena de texto.
Al pulsar sobre una opcin de este tipo, se muestra un cuadro de dilogo sencillo que solicita
al usuario un texto. Para este tipo de opcin, adems de los tres atributos comunes a la
opcin anterior, tambin hay que indicar el texto que aparece en el cuadro de dilogo
mediante el atributo android:dialogTitle. Por ejemplo:

<EditTextPreference android:key="opcion2"
android:title="Texto de la bsqueda"
android:summary="Indica el texto por defecto de la bsqueda"
android:dialogTitle="Introduce un texto" />

ListPreference

Se emplea para que el usuario seleccione una nica opcin de preferencia de una lista
de valores predefinida. Adems de los cuatro atributos anteriormente comentados, hay que
201

aadir dos ms: uno para indicar la lista de valores que se visualizan en la lista, y otro para
sealar los valores internos que guardaremos para cada uno de los valores de la lista anterior.
Por ejemplo, al usuario podemos mostrarle una lista de buscadores con el texto Google y
Bing, pero internamente almacenarlos como www.google.es y www.bing.com.
Esta lista de valores la definimos en el fichero XML /res/xml/opciones.xml con los
tipos de recursos <string-array> necesarios. En este caso son dos: uno para la lista de
valores visibles y otro para la lista de valores internos que se guardan en el fichero de
preferencias. Este fichero tiene este aspecto:

<?xml version="1.0" encoding="utf-8"?>


<resources>
<string-array name="nombre">
<item>Google</item>
<item>Bing</item>
<item>Yahoo</item>
</string-array>
<string-array name="url">
<item>www.google.es</item>
<item>www.bing.com</item>
<item>www.yahoo.es</item>
</string-array>
</resources>

En

la

opcin

de

preferencia

utilizamos

los

atributos

android:entries

android:entryValues para hacer referencia a estas listas, como vemos a continuacin:

<ListPreference
android:key="opcion3"
android:title="Buscadores"
android:summary="Indica el buscador por defecto"
android:dialogTitle="Selecciona buscador"
android:entries="@array/nombre"
android:entryValues="@array/url" />

202

MultiSelectListPreference

Se emplea para que el usuario seleccione una o varias opciones de preferencia de una
lista de valores predefinida. Los atributos que debemos establecer son, por lo tanto, los
mismos que para el tipo ListPreference. Vemos un ejemplo a continuacin:

<MultiSelectListPreference
android:key="opcion4"
android:title="Varios buscadores"
android:summary="Indica varios buscadores"
android:dialogTitle="Selecciona buscadores"
android:entries="@array/nombre"
android:entryValues="@array/url" />

A continuacin, mostramos el fichero completo pantallapreferencias.xml que usamos


en el Ejemplo 2 de esta Unidad:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Categora 1">
<CheckBoxPreference
android:key="opcion1"
android:title="Bsqueda automtica"
android:summary="Iniciar bsqueda automtica en Internet" />
<EditTextPreference
android:key="opcion2"
android:title="Texto de la bsqueda"
android:summary="Indica el texto por defecto de la bsqueda"
android:dialogTitle="Introduce un texto" />
</PreferenceCategory>
<PreferenceCategory android:title="Categora 2">
<ListPreference
android:key="opcion3"
android:title="Buscadores"

203

android:summary="Indica el buscador por defecto"


android:dialogTitle="Selecciona buscador"
android:entries="@array/nombre"
android:entryValues="@array/url" />
</PreferenceCategory>
</PreferenceScreen>

Una vez est definida la estructura de la pantalla de opciones, hay que implementar
una nueva Actividad que la llamaremos cuando queramos mostrar la pantalla de preferencias,
y que se encargar internamente de gestionar todas las opciones, guardarlas, modificarlas,
etctera, a partir de la definicin XML.
Android facilita el trabajo al programador ofreciendo la clase PreferenceActivity, que
se encarga de gestionar todas las operaciones internamente. nicamente hay que crear la
nueva Actividad PantallaPreferencias que se extiende de esta clase e implementar su
mtodo onCreate() para invocar el mtodo addPreferencesFromResource() indicando el
fichero XML en el que hemos definido la pantalla de opciones. El cdigo fuente se encuentra
en el fichero PantallaPreferencias.java:

public class PantallaPreferencias extends PreferenceActivity {


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pantallapreferencias);
Toast.makeText(getBaseContext(), "Pulsa la tecla 'Volver atrs' para
guardar las preferencias y volver a la aplicacin.", 1).show();
}
}

La nueva actividad, al descender de la clase PreferenceActivity, se encarga


automticamente de crear la interfaz de usuario con la lista de opciones siguiendo el diseo
del fichero XML y se ocupa de mostrar, modificar y guardar estas opciones cuando sea
necesario.
Para que la aplicacin pueda llamar a esta nueva Actividad, hay que incluirla en el
fichero AndroidManifest.xml como otra actividad ms de la aplicacin:

204

<activity android:name=".PantallaPreferencias"
android:label="@string/app_name">
</activity>

Para acabar la aplicacin, hay que aadir algn mecanismo que nos permita mostrar la
pantalla de preferencias. Esta opcin suele estar en un men, pero para simplificar, en el
Ejemplo 2 vamos a incluir el botn preferenciasBtn en la interfaz del usuario, para mostrar la
ventana de preferencias.
Al pulsar este botn se muestra la ventana de preferencias mediante el mtodo
startActivity() al que pasamos como parmetros el contexto de la aplicacin y la clase de la
ventana de preferencias (PantallaPreferencias.class).

preferenciasBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(PreferenciasActivity.this,
PantallaPreferencias.class));
}
});

En la Unidad 5 trataremos en detalle cmo usar Intents para iniciar Actividades de


nuestra aplicacin o de otra externa.
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Preferencias) de la Unidad 4. Estudia el
cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior.

Si ejecutamos la aplicacin en el emulador y pulsamos el botn de preferencias,


aparece la siguiente pantalla de opciones:

205

Podemos marcar o desmarcar directamente la primera opcin pulsando sobre el


CheckBox. Si pulsamos sobre la segunda opcin de tipo texto, se muestra una pequea
ventana que permite introducir el valor de la opcin:

Por ltimo, la tercera opcin de tipo lista muestra una ventana emergente con la lista
de valores posibles, donde nicamente podemos seleccionar uno:

Una vez hayamos establecidos los valores de las preferencias, podemos salir de esta
ventana de opciones pulsando el botn Atrs del dispositivo o del emulador

. La Actividad

PantallaPreferencias se habr ocupado por nosotros de guardar correctamente los valores


de las opciones.
Para

comprobarlo

vamos

aadir

la

interfaz

de

usuario

el

botn

obtenerOpcionesBtn, que recupera el valor actual de las tres preferencias y las muestra en la
pantalla.
Para acceder a las preferencias compartidas de la aplicacin usaremos el mtodo
getDefaultSharedPreferences(). Como hemos hecho anteriormente, usamos los distintos
mtodos get() para recuperar el valor de cada opcin dependiendo de su tipo:

206

obtenerPreferenciasBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences prefe =
PreferenceManager.getDefaultSharedPreferences(PreferenciasActivity.this);
lblResultado.setText("");
Log.i("", "Opcin 1: " + prefe.getBoolean("opcion1", false));
lblResultado.append("Bsqueda automtica: " +
prefe.getBoolean("opcion1", false));
Log.i("", "Opcin 2: " + prefe.getString("opcion2", ""));
lblResultado.append("\n\nTexto por defecto: " +
prefe.getString("opcion2", ""));
Log.i("", "Opcin 3: " + pref.getString("opcion3", ""));
lblResultado.append("\n\nBuscador: " +
prefe.getString("opcion3", ""));
}
});

Si ejecutamos ahora la aplicacin, establecemos las preferencias y pulsamos el botn


de consulta de las preferencias, veremos la siguiente pantalla:

4.3

RECURSOS DE LAS APLICACIONES ANDROID

Los Recursos de Android son archivos almacenados en el directorio /res del proyecto.
Estos ficheros de recursos pueden ser de tipo audio, vdeo, imgenes, texto, XML,
etctera, y se pueden integrar en la aplicacin.
Por qu usar ficheros con recursos?

207

1. Los recursos se integran en la aplicacin de manera transparente y se pueden


cambiar sin necesidad de modificar el cdigo fuente en Java.
2. Android genera un identificador nico (ID) para cada archivo de recurso para
acceder a ellos directamente desde el cdigo fuente en Java. Todos estos
identificadores de recursos se incluyen automticamente en el archivo
/gen/nombre_paquete/R.Java.
3. Android trata automticamente los archivos de recursos de tipo XML de forma
que se puede acceder a los valores definidos en ellos directamente.
4. El uso de Recursos es muy til en la Localizacin e Internacionalizacin de la
aplicacin para desarrollar aplicaciones en varios idiomas. As, se modifican
los textos de las etiquetas, su alineacin, imgenes, direcciones de Internet o
cualquier tipo de archivo en funcin del idioma del dispositivo Android.

4.3.1

Tipos de recursos

Cadenas (String), colores, matrices (arrays) y dimensiones. Se definen en el


directorio res/values/ del proyecto. Son muy tiles para internacionalizar la
aplicacin.

Las imgenes e iconos se almacenan en el directorio res/drawable.

Las animaciones se guardan en el directorio res/anime/.

Los ficheros XML se encuentran en el directorio res/xml/.

Diseos (Layout) de las pantallas o de elementos visibles (como una opcin de


un ListView) de la aplicacin. Se definen en el directorio res/layout/ del
proyecto.

Definicin de mens principales y contextuales en el directorio res/menu/.

En el curso ya hemos usado recursos en las aplicaciones anteriores. En este apartado


vamos a explicar en detalle cmo se incorporan stos dentro de las mismas. Sobre todo,
centraremos la explicacin en la Cadenas (String), ya que es la funcionalidad ms compleja y
es muy til para el programador.

4.3.2

Crear literales en ficheros de recursos con Eclipse


Usando Eclipse se pueden definir fcilmente atributos en los ficheros de Recursos de

los proyectos Android, como cadenas, colores, etctera.


Para ello, hay que hacer doble clic en el fichero de recursos que queramos editar; por
ejemplo, en el fichero res/values/string.xml y hacer clic en el botn "Add":
208

Despus, basta con seleccionar el tipo de recursos que necesitamos dar de alta y
rellenar el nombre y el valor que debe contener:

4.3.3

Recursos de tipo Cadena (String Resources)

Un recurso de tipo Cadena (String) permite definir cadenas de texto para usarlas en la
aplicacin Android: Incluso podemos cambiar su estilo y formato. Hay tres recursos de tipo
Cadena:

String: recurso que incluye una nica cadena de texto.

String Array: recurso que incluye una matriz de cadenas de texto.

Quantity Strings (Plurals): recurso que incluye dos cadenas segn una
cantidad sea singular o plural.

209

Ms adelante veremos cmo se puede modificar el estilo y formatear el contenido de


todos estos recursos de tipo cadena.

4.3.4.1 Cadena (String)


Se trata de una Cadena de texto simple que se puede utilizar dentro del cdigo fuente
Java o en otro fichero (layout) de diseo XML, como los que se usan para definir las pantallas.
Para definir recursos de cadenas de texto debemos usar la siguiente sintaxis:
<string name="nombre_cadena">texto de la cadena </string>
Si abrimos el fichero res/values/strings.xml del Ejemplo 3, veremos que en l
aparece la cadena:

<string name="etiqueta1">Este texto se carga de res/layout/main.xml</string>

El atributo name de <string> se usa para identificar la cadena unvocamente. Por lo


tanto, es el identificador (ID) de esta cadena.
Dentro de la etiqueta <string> incluimos el literal al que hace referencia.
Para hacer referencia a este recurso de cadena debemos escribir lo siguiente:

En el cdigo fuente Java: R.string.etiqueta1

En un fichero XML (de diseo o de recursos): @string/etiqueta1

En el Ejemplo 3 de esta Unidad puedes ver cmo se hace referencia a esta etiqueta
desde el fichero XML que define el diseo de la pantalla principal main.xml:

<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/etiqueta1"
android:text="@string/etiqueta1"
android:layout_margin="1dip"/>

Tambin podemos usar el recurso de tipo cadena en el cdigo fuente Java de la


aplicacin:
etiqueta2.setText(getString(R.string.etiqueta2));

210

4.3.4.2 Matriz de cadenas (String Array)


Es un recurso similar al de tipo Cadena, pero en este caso almacenamos varias
cadenas en una matriz. Se puede utilizar, igualmente, dentro del cdigo fuente Java o en otro
fichero (layout) de diseo XML, como los que usan para definir las pantallas.
Para definir recursos de tipo matriz debemos usar la siguiente sintaxis:

<string-array name="nombre_matriz">
<item>texto elemento 1</item>
<item>texto elemento 2</item>
...
</string-array>

Si abrimos el fichero res/values/matrices.xml del Ejemplo 3, veremos que aparece la


matriz:

<string-array name="horoscopo">
<item>Cancer</item>
<item>Capricornio</item>
<item>Aries</item>
<item>Leo</item>
<item>Libra</item>
</string-array>

El atributo name de <string-array> se usa para identificar la matriz unvocamente. Por


lo tanto, es el identificador (ID) de esta matriz.
Dentro de la etiqueta <string-array> incluimos varias etiquetas <item> con los
literales que forman los elementos de la matriz.
Para hacer referencia a este recurso de cadena en el cdigo fuente Java debemos
escribir R.array.horoscopo.
En el Ejemplo 3 de esta Unidad puedes ver cmo se usa este recurso de tipo matriz
para cargar las opciones de una caja de seleccin:
Spinner s = (Spinner) findViewById(R.id.spinner);
s.setPrompt("Elige el horscopo");
ArrayAdapter<CharSequence> adapter =
ArrayAdapter.createFromResource(this, R.array.horoscopo,
android.R.layout.simple_spinner_item);

211

adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
;
s.setAdapter(adapter);

Para obtener los elementos de la matriz desde el cdigo fuente en Java podemos usar
la funcin getStringArray:

Resources res = getResources();


String[] horoscopo = res.getStringArray(R.array.horoscopo);

4.3.4.3 Cadenas de cantidad (Quantity Strings)


Cada idioma tiene diferentes reglas de concordancia gramatical con las cantidades. En
espaol por ejemplo, escribimos "un libro" y, para cualquier otra cantidad, escribimos "3
libros". Esta distincin entre el singular y el plural es muy comn en los idiomas. Incluso hay
idiomas que hacen distinciones ms sutiles. Android incluye un conjunto completo y distingue
entre cero (zero), uno (one), dos (two), pocos (few), muchos (many) y otros (other).
Las reglas de concordancia pueden ser muy complejas dependiendo del idioma, por lo
que Android dispone del mtodo getQuantityString(), que selecciona el recurso apropiado
segn la cantidad numrica que estemos tratando.
Para definir recursos de tipo cantidad debemos usar la siguiente sintaxis:

<plurals name="nombre_plural">
<item quantity=["zero" | "one" | "two" | "few" | "many" | "other"]>
texto del literal</item>
</plurals>

Si abrimos el fichero res/values/strings.xml del Ejemplo 3, veremos que aparece el


elemento cantidad:
<plurals name="numeroDeContactos">
<item quantity="one">Se ha encontrado un contacto.</item>
<item quantity="other">Se han encontrado %d contactos.</item>
</plurals>

El atributo name de <plurals> se usa para identificar la cadena de cantidad


unvocamente; por lo tanto, es su identificador (ID).
Dentro de la etiqueta <plurals> incluimos varias etiquetas <item quantity> con los
literales que forman las distintas opciones de plurales. Podemos escribir los siguientes tipos
de plurales en el atributo quantity:

212

Valor

Descripcin

zero

Cuando el idioma requiere un tratamiento especial del nmero 0 (como el


rabe).

one

Cuando el idioma requiere un tratamiento especial del nmero 1 (como el


espaol, el ingls, etctera).

two

Cuando el idioma requiere un tratamiento especial del nmero 2 (como el gals).

few

Cuando el idioma requiere un tratamiento especial de pequeas cantidades


(como los nmeros 2, 3 y 4 en checo).

many

Cuando el idioma requiere un tratamiento especial de los nmeros grandes


(como los nmeros que terminan en 11-99 del malts).

other

Valor por defecto del resto de cantidades.

Para hacer referencia a este recurso de cadena en el cdigo fuente Java debemos
escribir R.plurals.numeroDeContactos.
En el Ejemplo 3 de esta Unidad puedes ver cmo se usa este recurso de tipo matriz
para cambiar el literal en funcin del nmero que escribe el usuario en una caja de texto:
//Obtenemos los recursos de la aplicacin
Resources res = getResources();
int total = Integer.parseInt(s.toString());
// Cambiamos el texto de la etiqueta plurales en funcin del contador
String contactosStr = res.getQuantityString(R.plurals.numeroDeContactos,
total, total);
txtPlurales.setText(contactosStr);

Hemos usado la funcin getQuantityString() para obtener el literal que corresponde a


la cantidad total y, adems, volvemos a pasar como tercer parmetro total, para que lo use
para formatear el literal en caso necesario. A continuacin, veremos cmo formatear las
cadenas de recursos,

4.3.4

Formatear y cambiar el estilo de las cadenas de recursos


4.3.4.1 Escapando comillas simples y dobles
A la hora de escribir comillas simples o dobles dentro del literal de una cadena de

texto debemos "escaparlas", para que Android no las interprete como parte del fichero XML y
muestre errores de sintaxis.

213

Es necesario incluir siempre las comillas simples dentro de las dobles o usar el
carcter "\" para escaparlas. Adems, no se permite incluir entidades de HTML para los
caracteres singulares como "&aacute;".
A continuacin, mostramos algunos ejemplos que funcionan y otros que no:
<string
<string
<string
<string

name="buen_ejemplo_1">"Esto 'est' bien"</string>


name="buen_ejemplo_2"> Esto \'est\' bien </string>
name="ejemplo_incorrecto_1">Esto 'no' funciona</string>
name="ejemplo_incorrecto_2">Esto no funcionar&aacute;</string>

4.3.4.2 Formatear cadenas de texto


Podemos tambin formatear cadenas usando la funcin

String.format(String,

Object...) incluyendo en sta los argumentos que sean necesarios para formar el literal. Por
ejemplo, la siguiente cadena de recurso con formato
<string name="FormatoCadena">Hola %1$s! Tienes %2$d mensajes nuevos</string>

tiene dos argumentos: %1$s, que es una cadena y %2$d, que es un nmero decimal.
En el Ejemplo 3 de esta Unidad hemos usado as esta cadena con formato para formatear un
literal:
String strFormat=getString(R.string.FormatoCadena);
String texto=String.format(strFormat, "Usuario", 4);
etiqueta3.setText(texto);

4.3.4.3 Cambio de estilo

Es posible definir una cadena de recurso que contenga cambios de estilo HTML en el
texto. As, en el Ejemplo 3 de esta Unidad definimos:
<string name="RecursoEstilo">Esto es un <u>ejemplo</u> de <i>Recurso</i> con
<b>Diseo</b> </string>

Las etiquetas HTML que se pueden usar son:

<b> para texto en negrita.

<i> para texto en cursiva.

<u> para subrayar un texto.

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Recursos) de la Unidad 4. Estudia


el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior.

214

Si ejecutamos la aplicacin en el emulador, veremos que aparece la siguiente pantalla:

Para completar este ejemplo hemos incluido tambin un recurso de tipo imagen en el
directorio drawable y hemos definido un color para cambiar el aspecto de una de las
etiquetas.
Importante: Al definir recursos de tipo cadena hay que tener cuidado en no escribir caracteres
que Android no sepa interpretar. Cuando esto ocurra Eclipse mostrar el siguiente mensaje de
error y no permitir compilar el proyecto:

4.4

ACCESO A INTERNET CON ANDROID

Android incluye la biblioteca Apache de cliente HTTP (Apache HttpClient library) que
permite a las aplicaciones conectar con servidores Web de Internet.

215

Android tambin permite usar la librera estndar Red de Java Java Networking API
(paquete java.net), aunque, si usamos este paquete java.net, Android utiliza internamente la
librera de Apache.
A partir de Android 2.2 tambin se puede utilizar la clase AndroidHttpClient. Para
construir un objeto a partir de esta clase hay que usar el constructor newInstance(), que
permite especificar el nombre de navegador (agente) que usa para conectar a una pgina de
Internet. La clase AndroidHttpClient incluye el protocolo seguro SSL y mtodos GZIP para
comprimir y descomprimir los datos recibidos.
Para que una aplicacin Android acceda a Internet, es necesario declararlo en el fichero
AndroidManifest.xml, que requiere el permiso "android.permission.INTERNET".

4.4.1

Ejemplo de conexin a Internet


A continuacin, vamos a mostrar mediante un ejemplo cmo conectar una aplicacin

Android a Internet para descargar la pgina de un servidor Web.


Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Acceso Internet) de la Unidad 4. Estudia el
cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior.
Si ejecutamos la aplicacin en el emulador y pulsamos el botn "Descargar pgina
web", aparece la siguiente pantalla:

En este ejemplo hemos usado tambin preferencias en la aplicacin para guardar la


direccin de la pgina Web que el usuario escribe en esta pantalla. Hemos implementado esta
funcionalidad con las siguientes sentencias:
216

public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Cargamos las preferencias y mostramos la ltima direccin en el EditText
cargaPreferencias();
pagWeb.setText(ultimaUrl);
...
}
// Carga las preferencias de la aplicacin
private void cargaPreferencias() {
SharedPreferences preferences = getSharedPreferences(PREFERENCIAS,
Activity.MODE_PRIVATE);
// Obtenemos la ltima direccin sobre la que se ha descargado
ultimaUrl = preferences.getString(URL, "http://www.google.es");
}

@Override
protected void onPause() {
super.onPause();
// En el caso de que la aplicacin se pause, guardamos las preferencias
SharedPreferences preferences = getSharedPreferences(PREFERENCIAS,
Activity.MODE_PRIVATE);
Editor preferenceEditor = preferences.edit();
preferenceEditor.putString(URL, pagWeb.getText().toString());
// No hay que olvidar nunca hacer el commit
preferenceEditor.commit();
}

Fjate que en el cdigo anterior definimos el evento onPause() de la Actividad, para


que se guarden las preferencias en el caso de que el ciclo de vida pase por este estado. As,
nos aseguramos de que se guardar siempre la direccin de la pgina Web.
Para descargar la pgina de Internet hemos usado una tarea asncrona que hemos
estudiado en la Unidad 3. En este caso hemos definido el mtodo onProgressUpdate() para
ir actualizando la pantalla segn se va descargando la pgina de Internet:

217

// Mtodo onClick del botn Descargar


public void onClickHandler(View view) {
switch (view.getId()) {
case R.id.descargPagWeb:
ResultadoLabel.setText("");
if (! hayConexionInternet()) ResultadoLabel.setText("ERROR: no hay
conexin a Internet");
else {
cargando.setVisibility(View.VISIBLE);
// Iniciamos la tarea de descarga
TareaDescargaPaginaWeb tarea = new TareaDescargaPaginaWeb();
tarea.execute(pagWeb.getText().toString());
}
break;
}
} // end onClick

// Clase que descarga una pgina de Internet como una tarea asncrona.
// Es decir, podemos seguir usando la interfaz de usuario.
private class TareaDescargaPaginaWeb extends
AsyncTask<String, String, String> {
// Mtodo que se ejecuta en segundo plano
protected String doInBackground(String... urls) {
try {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(urls[0]);
HttpResponse respuesta = client.execute(request);
// Obtenemos la respuesta
BufferedReader rd = new BufferedReader(new InputStreamReader(
respuesta.getEntity().getContent()));
String linea = "";
String resultado = "";
// Mientras podamos leer una lnea de la pgina Web
while ((linea = rd.readLine()) != null) {

218

resultado+=linea;
if (resultado.length()>1024) {
publishProgress(resultado);
resultado="";
}
} // end while
}

catch (Exception e) {
System.out.println("Error al descargar la pgina.");
return "ERROR al descargar la pgina: "+e.getMessage();
}
return null;
}
/** Actualiza la etiqueta al ir descargando la pgina */
protected void onProgressUpdate(String... values) {
ResultadoLabel.append(values[0]);
}
/** Cuando la tarea ha acabado, se invoca automticamente este mtodo */
protected void onPostExecute(String resultado) {
if (resultado !=null) ResultadoLabel.append(resultado);
cargando.setVisibility(View.INVISIBLE);
} // end onPostExecute
}

Para descargar la pgina de Internet hemos usado la clase DefaultHttpClient y


llamado al mtodo HttpGet(). Despus, usamos la clase InputStreamReader para leer los
datos como si se tratara de un fichero ms.
Es evidente que un dispositivo Android no tiene siempre conexin a Internet. Por esto,
es bueno comprobar que tiene acceso a Internet a travs del siguiente cdigo:

public boolean hayConexionInternet() {


// Comprobamos si hay conexin a Internet
ConnectivityManager cm = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);

219

NetworkInfo networkInfo = cm.getActiveNetworkInfo();


// Si la variable networkInfo<> null, entonces tenemos conexin a Internet
if (networkInfo != null && networkInfo.isConnected()) {
return true;
}
return false;
}

En el cdigo anterior usamos la clase ConnectivityManager para conocer el estado


de la conexin a Internet. Adems, para poder hacerlo, hay que declarar en el fichero Manifest
de la aplicacin el permiso correspondiente:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
</uses-permission>

4.4.2

Conexin a travs de proxy


Si ests programando aplicaciones Android y te conectas a Internet a travs de un

Proxy, es interesante configurar el emulador de Android para que acceda a Internet a travs de
ese Proxy.
Para configurarlo, haz clic en el icono "Ajustes" y establece las siguientes opciones:
Conexiones inalmbricas->Redes mviles ->APN->Telkila-> Proxy/Puerto

220

4.4

QU SON JSON (JavaScript Object Notation?

JSON es el acrnimo en ingls de JavaScript Object Notation; es un formato ligero


para el intercambio de datos en aplicaciones Web. JSON tiene la ventaja de que no requiere el
uso de XML.
La simplicidad de JSON ha provocado la generalizacin de su uso; es una buena
alternativa, especialmente, al formato XML.
JSON es ms fcil de utilizar como formato de intercambio de datos que XML, porque
es mucho ms sencillo escribir un analizador semntico de JSON.
El formato JSON se basa en los tipos de datos y sintaxis del lenguaje JavaScript. Es
compatible con cadenas, nmeros, boolean y valores nulos. Tambin se pueden combinar
valores en matrices y objetos.
Los objetos en JSON son simplemente conjuntos desordenados de parejas
nombre/valor, donde el nombre es siempre una cadena y el valor es cualquier tipo de datos
vlido para JSON, incluso otro objeto. A continuacin, se muestra un ejemplo simple de
definicin de los datos de un producto usando JSON:
{
"producto": {
"nombre": "Widget",
"compania": "ACME, Inc",
"numero": "7402-129",
"precios": [
{ "cantMin": 1, "precio": 12.49 },
{ "cantMin": 10, "precio": 9.99 },
{ "cantMin": 50, "precio": 7.99 }
]
}
}

221

Puedes ver ms ejemplos en el siguiente enlace: json.org/example.html.


Twitter es una fuente muy grande que usa el formato JSON. En el Ejemplo 5 de esta
Unida vamos a cargar el Twitter de Mentor en una aplicacin Android. La direccin es la
siguiente:
http://twitter.com/statuses/user_timeline/MinisterioEduc.json
Fjate en que la cuenta de la que obtenemos los datos se llama #MinisterioEduc.

4.5.1

Cmo usar JSON en nuestras aplicaciones a Android


Android incluye la biblioteca JSON, que permite tratar este formato de dato. Las

clases ms importantes de este paquete son:

JSONArray: permite cargar y tratar una matriz de elementos en formato JSON.

JSONObject: permite tratar un nico elemento en formato JSON.

Desde Eclipse puedes abrir el proyecto Ejemplo 5 (JSON) de la Unidad 4. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior.
Si ejecutamos la aplicacin en el emulador, veremos la siguiente pantalla:

222

Vamos a mostrar con un ejemplo prctico cmo usar una fuente de datos en formato
JSON en una aplicacin Android. Si abrimos el fichero de cdigo fuente del Ejemplo 5,
veremos las siguientes sentencias:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Definimos la matriz que vamos a usar de adaptador en la ListActivity
ArrayList<String> datosAdaptador = new ArrayList<String>();
// Leemos los datos de la cuenta de Twitter
String datosCuentaTwitter = leeCuentaTwitter();
// Leemos el contenido interno del fichero obtenido
if (datosCuentaTwitter!= null)
try {
// Usamos una matriz de JSON
JSONArray matrizJSON = new JSONArray(datosCuentaTwitter);
TextView nNoticias = (TextView) findViewById(R.id.nNoticias);
nNoticias.setText("Nmero de noticias: " + matrizJSON.length());
// Recorremos ahora todos los elementos de la matriz
for (int i = 0; i < matrizJSON.length(); i++) {
// Leemos cada objeto y lo aadimos a la matriz de datos
JSONObject jsonObjeto = matrizJSON.getJSONObject(i);
datosAdaptador.add(jsonObjeto.getString("text"));
} // end for
} catch (Exception e) {
e.printStackTrace();
}
// Indicamos el adaptador de la ListActivity
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, datosAdaptador));
}

// Mtodo que lee las noticias de una cuenta de Twitter


public String leeCuentaTwitter() {

223

// String que permite ir aadiendo lneas


StringBuilder builder = new StringBuilder();
// Usamos un cliente HTTP para obtener el fichero JSON
HttpClient cliente = new DefaultHttpClient();
// Definimos un objeto para hacer una peticin HTTP GET
HttpGet httpGet = new HttpGet(
"http://twitter.com/statuses/user_timeline/MinisterioEduc.json");
try {
// Ejecutamos la peticin GET
HttpResponse respuesta = cliente.execute(httpGet);
// Obtenemos la respuesta del servidor
StatusLine statusLine = respuesta.getStatusLine();
// Y su cdigo de estado asociado
int statusCode = statusLine.getStatusCode();
// 200 el servidor responde con datos
if (statusCode == 200) {
// Obtenemos un puntero a la respuesta
HttpEntity entity = respuesta.getEntity();
// Obtenemos el contenido de la respuesta
InputStream contenido = entity.getContent();
// Leemos el contenido como si fuera un fichero
BufferedReader reader = new BufferedReader(new
InputStreamReader(contenido));
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
} else {
Log.e(JSONActivity.class.toString(), "No se puede descargar el
fichero");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {

224

e.printStackTrace();
}
return builder.toString();
} // end leeCuentaTwitter

En el cdigo anterior hemos usado la clase DefaultHttpClient para descargar los


datos en formato JSON. Esta clase ya la hemos visto con anterioridad en otros ejemplos.
Una vez hemos obtenido los datos JSON del servidor, nicamente hay que crear un
objeto de la clase JSONArray cuyo parmetro sean los datos anteriores. Despus, slo hay
que recorrer esta matriz de elementos de tipo JSONObject y usar el mtodo getString() para
obtener el contenido del mismo.

NOTA: Para que una aplicacin Android acceda a Internet es necesario declararlo en el fichero
AndroidManifest.xml, que requiere el permiso "android.permission.INTERNET".

4.5.2

Cmo escribir ficheros en formato JSON


Escribir datos en formato JSON es muy sencillo. Basta con crear un objeto del tipo

JSONObject o del tipo JSONArray y utilizar el mtodo toString() para transformar este objeto
en datos JSON.
Veamos un ejemplo prctico:

public void escribirJSON() {


JSONObject objeto = new JSONObject();
try {
objeto.put("nombre", "Pedro del Cielo Lindo");
objeto.put("edad", new Integer(33));
objeto.put("altura", new Double(1,77));
objeto.put("ciudad", "Avila");
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println(objeto);
}

225

En Android existen tres formas de almacenar informacin para usarla en las


aplicaciones:
o

Preferencias de la aplicacin (slo lectura).

Ficheros locales en el sistema de archivos del sistema operativo.

Base de datos SQLite.

Android tambin permite manipular ficheros de forma similar a como se hacen


en Java.
En Android, por defecto, los ficheros son privados y nicamente puede acceder
a ellos la aplicacin que los crea.
Podemos leer y escribir ficheros localizados en:
o

La memoria interna del dispositivo: como fichero (lectura y escritura) o


como recurso de la aplicacin (slo lectura).

La tarjeta SD externa, si existe, tambin denominada almacenamiento


externo.

La clase File de Android se usa para identificar y gestionar archivos y directorios


del sistema operativo.
Es necesario manifestar que una aplicacin necesita el permiso de escritura
en la memoria externa en su arhivo Manifest.xml.
Las preferencias de una aplicacin son datos que una aplicacin guarda y
recupera para personalizar la experiencia del usuario.
Para gestionar las preferencias de una aplicacin hay que usar la clase
SharedPrefences, donde cada preferencia se almacena siguiendo la estructura
clave-valor.

226

Las preferencias se almacenan en formato XML en un fichero en la memoria del


dispositivo.
Android permite simplificar la gestin de las preferencias de una aplicacin
mediante el uso de una pantalla de opciones dividida en categoras que tienen
opciones de diversos tipos.
Los Recursos de Android son archivos contenidos en el proyecto de tipo audio,
vdeo, imgenes, texto, XML, etctera, que se pueden usar en la aplicacin. Estos
recursos son de slo lectura..
Un recurso de tipo Cadena (String) permite definir cadenas de texto para usarlas
en la aplicacin Android; incluso podemos cambiar su estilo y formato. Son muy
tiles para el programador, pues facilita la Internacionalizar las aplicaciones.
Android incluye las bibliotecas necesarias que permiten a las aplicaciones
conectar con servidores Web de Internet.
Es necesario manifestar que una aplicacin necesita el permiso de acceso a
Internet en su arhivo Manifest.xml.
JSON es el acrnimo en ingls de JavaScript Object Notation; es un formato ligero
para el intercambio de datos en aplicaciones Web. JSON tiene la ventaja de que
no requiere el uso de XML.
Android incluye la biblioteca JSON que permite leer y escribir este formato de
dato.

227

INTENTS EN ANDROID

NDICE
5.1 INTENTS EN ANDROID.......................................................... 231
5.1.1 Introduccin ...........................................................................231
5.1.2 Intenciones(Intents) ..............................................................231
5.1.3 FicherosManifest ...................................................................232
5.1.4 Declararcapacidadesdeloscomponentesdelasaplicaciones233
5.1.5 Usodeintenciones .................................................................234
5.1.6 Arranqueexplcitodeunaactividad.......................................234
5.1.7 Arranqueimplcitodeunaactividad ......................................235
5.1.7.1 Ejecutar subactividades .....................................235
5.1.8 Filtrosdeintenciones .............................................................239
5.1.9 Resolucindeintencionesimplcitas......................................240
5.1.10 Usodeintencionesparaextenderaplicaciones ....................241
5.2 USO DE INTENTS ................................................................... 243
5.2.1 UsodeIntents ........................................................................243
5.2.2 InvocacinExplcita ................................................................243
5.2.3 InvocacinImplcita................................................................249
5.2.4 RegistroAccinparaInvocacinImplcita..............................252
5.2.5 DetectarAccionesdeIntents..................................................255
5.3 PERMISOS Y SEGURIDAD EN ANDROID ........................... 256
5.3.1 ArquitecturadeseguridaddeAndroid ...................................256
5.3.2 Firmadeaplicacin.................................................................256
5.3.3 IDdeusuarioyAccesoaficheros ...........................................257
5.3.4 Permisosdeaplicaciones........................................................257
5.3.5 AutoproteccindeaplicacionesAndroid ...............................261
5.3.6 Asignarpermisosdecomponentesinternosdelaaplicacin 262
5.3.7 Cmoobtienespermisosestasaplicaciones ..........................263
5.3.8 NotassobreseguridadenAndroid .........................................264
5.4 Tab.Layout .............................................................................. 264
5.4.1 PantallasconpestaasconTabLayout ..................................264

Intents

5.1

5.1.1

INTENTS EN ANDROID

Introduccin
En esta Unidad vamos a explicar cmo usar Intenciones (Intents) en Android para

arrancar Actividades o servicios.


Adems, veremos cmo definir los permisos de una Aplicacin de Android.
Finalmente, disearemos pantallas de aplicaciones con pestaas con el
componente Tab Layout.

5.1.2

Intenciones (Intents)
Las Intenciones (Intents) permiten a las aplicaciones de Android expresar la intencin

de que se desea ejecutar una accin sobre unos datos usando algn componente de sta o
de otra aplicacin. Las intenciones permiten interconectar componentes de la misma o de
distintas aplicaciones mediante mensajes.
De los cuatro componentes de Android, las Actividades, los Servicios y los Receptores
de mensajes de difusin se activan con un mensaje asncrono que se denomina Intencin. Los
Proveedores de contenidos quedan excluidos.
Para crear una Intencin hay que usar el objeto Intent de Android.
Las intenciones se utilizan para arrancar componentes de dos formas:

Explcita: invocando la clase Java del componente que queremos ejecutar.


Normalmente, esto se usa para invocar componentes de una misma
aplicacin.

Implcita: invocando la accin y los datos sobre los que aplicar dicha accin.
Android selecciona, en tiempo de ejecucin, la actividad receptora que cumple
mejor con la accin y los datos solicitados.

Para las Actividades y Servicios, una intencin define la accin que queremos realizar
(por ejemplo, "ver" o "enviar" algo) y puede especificar el identificador URI de los datos que
va a utilizar esa accin. Por ejemplo, una intencin podra hacer una peticin para arrancar una
actividad que muestre una imagen o abra una pgina Web.
En algunos casos, se puede iniciar una subactividad para recibir un resultado, en
cuyo caso esta subactividad devuelve el resultado en otra nueva intencin. Por ejemplo, se
puede arrancar un Intent para que el usuario elija un contacto del telfono y lo devuelta a la
Actividad principal (esta intencin de respuesta se devuelve tambin como un identificador
URI que apunta al contacto seleccionado).

231

Las Intenciones son mensajes asncronos entre componentes de aplicaciones que se usan
para realizar acciones e intercambiar datos, tanto en la peticin como en la respuesta,.
De esta manera el usuario tiene la sensacin de estar usando una nica aplicacin cuando, en
realidad, son componentes de varias.
A continuacin, vemos un esquema que muestra cmo funciona un Intent:

Las Intenciones de difusin (en ingls Broadcast Intents) se envan a mltiples


destinatarios del sistema y pueden ser procesadas por cualquier receptor de mensajes de
difusin (Broadcast Receiver). Por ejemplo, el sistema genera este tipo de intenciones de
difusin para anunciar situaciones diversas, como que la batera del telfono se agota, que
llega una llamada de telfono o un mensaje SMS, etctera. Cualquier componente puede
registrar un receptor de mensajes de difusin para que est informado de estos eventos.
El otro tipo de componente de Android, el Proveedor de contenido (Content Provider),
no se activa mediante intenciones, sino mediante una solicitud de un ContentResolver. En la
Unidad 7 de este curso veremos cmo funciona este procedimiento.
Uno de los usos principales de las intenciones es arrancar, parar y cambiar entre las
actividades y los servicios de una aplicacin.

5.1.3

Ficheros Manifest
Para que Android pueda iniciar un componente de una aplicacin, el sistema debe

conocer que existe este componente. Ya hemos visto que, para ello, se declaran los
componentes de una aplicacin en el fichero AndroidManifest.xml. Este fichero se encuentra
en el directorio raz del proyecto Android.
232

Intents

Por ejemplo, para declarar una Actividad debemos escribir:


<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:icon="@drawable/app_icon.png" ... >
<activity android:name="com.ejemplo.projecto.EjemploActividad"
android:label="@string/ejemplo_label" ... >
</activity>
...
</application>
</manifest>

En el elemento <activity> usamos el atributo android:name para especificar el


nombre completo de clase de la actividad y el atributo android:label especifica la cadena de
texto visible que se muestra al usuario cuando la utiliza.
Hay que declarar todos los componentes de la aplicacin de esta forma usando las
siguientes etiquetas:

<activity>: Actividades

<service>: Servicios

<receiver>: Receptores de mensajes de difusin (Broadcast receivers)

<provider>: Proveedores de contenido (Content providers)

Es obligatorio incluir en este fichero "AndroidManifest" todas las actividades, los


servicios y los proveedores de contenido, ya que, si no lo hacemos, no son visibles para el
sistema y, en consecuencia, no se pueden ejecutar.
Los receptores de mensajes de difusin pueden declarase en este fichero
"AndroidManifest" o bien podemos crearlos de forma dinmica en el cdigo fuente Java.

5.1.4

Declarar capacidades de los componentes de las aplicaciones


Tal y como se ha mencionado anteriormente, si queremos iniciar los componentes de

una aplicacin hay que utilizar una intencin para ejecutar actividades, servicios y receptores
de mensajes de difusin.
Se puede hacer de forma explcita indicando el nombre de la clase del componente
destino.
Sin embargo, el potencial de uso de las intenciones radica en el concepto de Accin
mediante las invocaciones implcitas. En una Accin el programador slo tiene que describir
el tipo de accin que desea realizar y, opcionalmente, los datos sobre los que desea realizar
esa accin. Si hay varios componentes que pueden llevar a cabo esta accin de la intencin,
entonces el usuario del dispositivo puede seleccionar cul aplicar.

233

Android identifica qu componentes pueden responder a una Accin determinada


buscndola en los filtros de intencin (intent filters) que se declaran en el archivo
"AndroidManifest" de todas las aplicaciones del dispositivo.
Cuando se incluye un componente en el fichero "AndroidManifiest" de una aplicacin,
se pueden especificar filtros de intencin que declaren la capacidad de este componente para
que pueda responder a las Acciones de otras aplicaciones.
Para incluir un filtro de intencin de un componente, hay que aadir el elemento
<intent-filter> dentro de la declaracin del componente.
Veamos un ejemplo: una aplicacin de correo electrnico con una Actividad que
componga un nuevo correo electrnico puede declarar el filtro de intencin ACTION_SEND
que responda a la intencin "send" (enviar) que, lgicamente, enva un mensaje. Una actividad
de otra aplicacin puede entonces simplemente iniciar una intencin con la accin
(ACTION_SEND) que provocar que Android busque la actividad o actividades que
concuerdan con esta accin en iniciar la intencin "send". Es decir, el programador no tiene
que conocer el nombre de la intencin, nicamente, debe invocar su accin. Ms adelante
veremos otro ejemplo.

5.1.5

Uso de intenciones
Para arrancar una Actividad sin esperar una respuesta de la subactividad iniciada,

debemos usar la siguiente funcin:


startActivity(anIntent);

Para arrancar una Actividad y esperar una respuesta de la subactividad iniciada,


debemos usar la siguiente funcin:
startActivityForResult(anIntent, INTENT_COD);

Ambos mtodos se pueden usar tanto en las invocaciones explcitas como implcitas.
La diferencia radica en que el primero inicia la subactividad y no espera respuesta de sta; el
segundo mtodo espera recibir una respuesta de la ejecucin de la subactividad.

5.1.6

Arranque explcito de una actividad


Para iniciar explcitamente una Actividad hay que especificar en la intencin el

contexto de la aplicacin que la invoca y la clase de la actividad que se quiere arrancar:


Intent intent = new Intent(MiActividad.this, MiOtraActividad.class);
startActivity(intent);

234

Intents

En la Unidad 4, en el apartado de pantallas de preferencias, ya hemos usado esta


manera de invocar una Intencin explcitamente.

5.1.7

Arranque implcito de una actividad


Una intencin implcita especifica la accin requerida y los datos sobre los que acta.

Es importante insistir en que las aplicaciones de Android deben publicar las acciones que
ofrecen.
Por ejemplo, la aplicacin dialer de marcado de llamadas de telfono de Android
ofrece la accin Intent.ACTION_DIAL que se invoca as:

if (...) {
Intent intent = new Intent(Intent.ACTION DIAL,
Uri.parse("tel:91-6666"));
startActivity(intent);
}

El cdigo anterior inicia una llamada de telfono al nmero indicado como parmetro
en la intencin. Como hemos usado el mtodo startActivity(), no trataremos la respuesta de
su ejecucin.

5.1.7.1

Ejecutar subactividades

Se puede arrancar una actividad como subactividad de

otra actividad principal.

Cuando termina esta subactividad, se invoca al mtodo onActivityResult de la actividad


principal desde la que se inici.
Cualquier actividad registrada en el fichero "AndroidManifiest" puede ser invocada
como

subactividad.

Para

arrancar

una

subactividad

hay

que

usar

el

mtodo

startActivityForResult(), pasando como parmetro la intencin y un cdigo de peticin, que


se utiliza para identificar a qu subactividad corresponde la respuesta.
Veamos un ejemplo sencillo que inicia una subactividad desde una actividad principal:

private static final int SUBACTIVIDAD = 1;


Intent intent = new Intent(this, MiOtraActividad.class);

// Aadimos un dato extra en la intencin que pasa a la subactividad


intent.putExtra("nombre", "Nadie");
startActivityForResult(intent, SUBACTIVIDAD);

235

En la Intencin hemos incluido una coleccin de datos Extras con informacin


adicional. Para ello, usamos el mtodo putExtra() de la clase Intent para incorporar la
informacin adicional.
Veamos ahora un ejemplo del cdigo de una subactividad:
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.subactividad);
// Obtenemos el intent que invoca esta actividad
Intent intent = getIntent();
// Obtenemos el contenido de los datos del intent
Uri datos = intent.getData();
// Obtenemos el contenidos de la variable extras
Bundle extra = intent.getExtras();
// Usamos los datos que hemos obtenido
if (extra == null) return;
// Leemos los contenidos de los datos de invocacin y variables extra
String valor1 = extra.getString("nombre");
// Formamos la URL que debemos cargar
URL url = new URL(datos.getScheme(), datos.getHost(),
datos.getPath());
}

Button okButton = (Button) findViewById(R.id.ok_button);


okButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Uri dato = Uri.parse("content://dato/" + id_dato_seleccionado);
Intent resultado = new Intent(null, dato);
resultado.putExtra(TODO_CORRECTO, correcto);
resultado.putExtra(DATO_SELECCIONADO, datoSeleccionado);
setResult(RESULT_OK, resultado);
finish();
}
});

236

Intents

Button cancelarButton = (Button) findViewById(R.id.cancel_button);


cancelarButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
setResult(RESULT_CANCELED, null);
finish();
}
});

A continuacin, vamos a explicar los mtodos ms importantes:

getIntent(): obtiene la intencin que ha invocado la actividad. Normalmente este


mtodo se usa en el constructor de la Actividad con el mtodo onCreate(). Una
vez obtenida la intencin, podemos conseguir ms informacin contenida en ella
mediante los mtodos:
o

getData(): obtiene el dato contenido en la intencin en formato Uri. Para


leer los contenidos internos de este dato podemos usar los mtodos
getScheme() (esquema del dato), getUserInfo() (informacin de usuario),
getHost()(direccin del servidor), etctera.

getExtras(): obtiene los datos extras contenidos en la intencin en


formato Bundle. Para leer los campos de este tipo de dato podemos usar
los mtodos getString("nombre"), getInteger("nombre"), etctera,

setResult(): acepta dos parmetros: un cdigo de respuesta y una intencin con


los datos devueltos.
o

Cdigo de resultado: Activity.RESULT_OK,


Activity.RESULT_CANCELED o cualquier nmero entero.

Intencin con datos devueltos: identificador URI a los contenidos


devueltos (un contacto, un nmero de telfono, una imagen, etctera) y
una coleccin de datos Extras con informacin adicional. Para ello
creamos una clase Intent y usamos el mtodo putExtra() para incorporar
la informacin adicional.

finish():

devuelve a la actividad principal el resultado de la ejecucin de la

subactividad. En el cdigo Java de la subactividad se debe invocar siempre el


237

mtodo setResult() antes que el mtodo finish(), para devolver los resultados de la
ejecucin de sta.

Para recuperar los resultados devueltos a la actividad principal debemos definir el


mtodo onActivityResult(). Veamos un ejemplo del cdigo de una actividad principal que
trata la respuesta de una subactividad:

private static final int SUB_ACTIVIDAD_UNA = 1;


private static final int SUB_ACTIVIDAD_DOS = 2;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case (SUB_ACTIVIDAD_UNA) : {
if (resultCode == Activity.RESULT_OK) {
Uri dato = data.getData();
// Obtenemos el campo TODO_CORRECTO con el valor por defecto false
boolean correcto = dato.getBooleanExtra(TODO_CORRECTO,false);
String datoSeleccionado = dato.getStringExtra(DATO_SELECCIONADO);
}
break;
}
case (SUB_ACTIVIDAD_DOS) : {
if (resultCode == Activity.RESULT_OK) {
// Tratar el resultado de la Subactividad 2
}
break;
}
}
}

El mtodo onActivityResult() recibe los siguientes parmetros:

238

Intents

Cdigo de peticin: cdigo que se utiliza para iniciar la subactividad.

Cdigo

de

resultado:

cdigo

que

devuelve

la

subactividad,

Activity.RESULT_OK, Activity.RESULT_CANCELED o cualquier nmero


entero.

Intencin con datos devueltos: identificador URI con la intencin con los
resultados devueltos por la subactividad. Esta segunda actividad reciben
datos de la primera a travs de la clase Bundle que pueden ser recuperados a
travs de dos formas:
o

mtodo data.getExtras() en combinacin con cualquiera de sus


mtodos getString("nombre_dato"), getBoolean("nombre_dato"),
getInteger("nombre_dato"), etctera.

mtodos getData() en combinacin con cualquiera de los mtodos


getStringExtra("nombre_dato"), getBooleanExtra("nombre_dato"),
getIntegerExtra("nombre_dato"), etctera. En estos mtodos se
puede definir un valor por defecto en el caso de que no est definido
el dato.

Podemos comprobar con el mtodo data.hasExtra("nombre_dato") si la intencin


devuelta contiene el dato extra nombre_dato antes de tratar esta informacin.

5.1.8

<b Filtros de intenciones


La accin de una intencin implcita puede estar en la misma aplicacin, provenir de

un servicio nativo o de otra aplicacin.


Los filtros de intenciones registran Actividades, Servicios y Receptores de mensajes
de difusin (broadcast receivers) como potenciales receptores de acciones sobre cierto tipo
de datos.
En el fichero "Manifest" se utiliza la etiqueta <intent-filter> en la seccin del
componente de la aplicacin que recibe la accin, especificando la accin, la categora y los
datos para invocarla. Estos parmetros se usan para especificar lo siguiente:

Accin: atributo android:name, que especifica la accin que se sirve a la misma


aplicacin o a otras aplicaciones . Debemos definir una cadena nica, por lo que
se debe utilizar la notacin de paquetes (es.mentor.)

Categora: atributo android:category, que especifica bajo qu circunstancias se


debe servir la accin. Es posible especificar varias categoras. Este atributo aade
informacin adicional sobre la accin que se debe ejecutar. Por ejemplo,
CATEGORY_LAUNCHER indica que la accin debe aparecer en la pantalla de
Inicio (Launcher) como una aplicacin, mientras que CATEGORY_ALTERNATIVE
239

indica que debe incluirse en una lista de acciones alternativas que el usuario
puede aplicar a un conjunto de datos.

Datos: permite especificar mediante atributos los tipos de datos sobre los que
puede actuar el componente. Por ejemplo, android:host, android:mimetype,
android:path, etctera.

Veamos un ejemplo del cdigo de la actividad EjemploActividad que declara la


accin VER_TEXTO al sistema operativo para que otra Actividad la invoque:

<activity android:name=".EjemploActividad"
android:label="Ver Texto">
<intent-filter>
<action android:name="es.mentor.intent.action.VER_TEXTO">
</action>
<category android:name="android.intent.category.DEFAULT"/>
<category
android:name="android.intent.category.CATEGORY_ALTERNATIVE"
/>
<data android:mimeType="vnd.visualizador.cursor.item/*"/>
</intent-filter>
</activity>

<b
5.1.9

Resolucin de intenciones implcitas


Puede ocurrir que exista ms de una actividad que haya registrado un filtro de

intencin (Accin) con el mismo nombre. Por ejemplo, el dispositivo Android puede disponer
de varios programas para navegar por Internet; si el usuario solicita abrir una pgina HTML,
entonces se le muestra un listado con las opciones posibles que completan las acciones.
Android elige la intencin implcita que se resuelve con varias actividades posibles as:
1. Android genera una lista interna en el sistema operativo con todos los filtros de
intenciones posibles incluyendo los de las aplicaciones nativas preinstaladas.
2. Elimina los filtros que no coinciden con la accin solicitada.
3. Eliminan las categoras que no coinciden con la categora invocada.
4. Cada parte de la URI de los datos de la intencin que se invoca se compara
con la etiqueta <data> del filtro. Los parmetros especificados en el filtro han
de coincidir con los de la intencin. Es decir, no podemos hacer una llamada
240

Intents

de telfono con un fichero de msica, sino que debemos usar un nmero de


telfono.
5. Si despus de los pasos anteriores, existe ms de un filtro candidato, se
escoge el de mayor prioridad o se permite al usuario que seleccione la
Intencin.
Con los mtodos getIntent(), getAction() y getData() el componente elegido por el
proceso de resolucin puede saber qu accin tiene que ejecutar y el dato sobre el que
ejecutarla. Fjate en este ejemplo:

@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.main);
Intent intent = getIntent();
String action = intent.getAction();
Uri data = intent.getData();
}

Con el mtodo startNextMatchingActivity() un componente puede derivar el


procesamiento de una intencin implcita a otro componente cuando no quiere atenderla; por
ejemplo, cuando no es la adecuada para realizar esa accin. Fjate en el siguiente ejemplo:

Intent intent = getIntent();


if (!noHayConexionInternet) startNextMatchingActivity(intent);

De esta manera, un componente de una aplicacin Android puede decidir si responde


a una accin o cede el trabajo a otro componente ms apropiado.

5.1.10

<b Uso de intenciones para extender aplicaciones


Es posible servirse de intenciones implcitas para proporcionar funcionalidad

(acciones) de nuevos componentes de aplicaciones desde mens de opciones de aplicaciones


compiladas, antes de que exista la accin en el sistema.

241

El mtodo addIntentOptions() de la clase Menu permite especificar los datos sobre


los que puede operar una accin futura. Se especifican nicamente los datos, no la accin en
s. Cuando Android resuelve la intencin y devuelve una lista de acciones apropiadas para el
dato, se crea una nueva opcin en el men de la aplicacin.
Muchas aplicaciones del sistema operativo emplean este mecanismo para extender su
funcionalidad a medida que nuevas actividades van implementando las acciones previstas.
Para declarar nuevas acciones susceptibles de ser invocadas desde mens
preexistentes en otras actividades, hay que exportarlas en los filtros de intenciones. La
etiqueta <category> del filtro debe ser ALTERNATIVE y/o SELECTED ALTERNATIVE. La
etiqueta <android:label> aparecer en la opcin del men correspondiente.
Por ejemplo:
<activity android:name=".ActividadExtra">
<intent-filter android:label="Lanzar nueva actividad">
<action android:name="es.mentor.LANZAR"/>
<data android:mimeType="es.mentor.cursor.item/*"/>
<category android:name="android.intent.category.ALTERNATIVE"/>
<category
android:name="android.intent.category.SELECTED_ALTERNATIVE"
/>
</intent-filter>
</activity>

El mtodo addIntentOptions() del objeto men recibe como parmetro una intencin
que especifica los datos para los que se quiere proporcionar una accin. Se invoca este
mtodo desde los mtodos onCreateOptionsMenu() o onCreateContextMenu(), que ya
hemos

estudiado.

La

CATEGORY_ALTERNATIVE

intencin
o

slo

especifica

los

datos

la

CATEGORY_SELECTED_ALTERNATIVE.

No

especificar ninguna accin ya que es lo que buscamos.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
// Creamos un intent que se usa para resolver que acciones aplican a
// ese tipo de datos.
Intent intent = new Intent();

242

categora
se

debe

Intents

intent.setData(Dato.CONTENT_URI);
intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);
// Buscamos el men que coincide con esos datos.
menu.addIntentOptions(
R.id.intent_group, // Menu group donde aadimos las nuevas opciones
0,
// ID nico de la opcin (ninguno)
0,
// Orden de la opcin (ninguno)
this.getComponentName(),
// Nombre Actividad actual
null,
// Opciones que se colocan al principio (ninguna)
intent, // Intent creado con los datos y la categora
0,
// Parmetro adicional (ninguno)
null); // Matriz de MenuItems que correla opciones (ninguna)

return true;
}

5.2

USO DE INTENTS

5.2.1

<b Uso de Intents


Debido a la complejidad de uso de las Intenciones (Intents), vamos a describir cmo

se usan, tanto implcita como explcitamente.


5.2.2

<b Invocacin Explcita


El Ejemplo 1 de este Unidad muestra cmo se pueden transferir datos entre dos

actividades. Para ello, vamos a invocar intenciones explcitamente entre dos actividades. La
primera actividad llama a la segunda, tambin llamada subactividad, a travs de una intencin
explcita. Esta segunda actividad recibe datos de la primera a travs de la clase Bundle, que
pueden ser recuperados a travs de intent.getExtras().
La segunda actividad (o subactividad) puede finalizar mediante el botn de retroceso
del telfono o por una accin del usuario en sta, por ejemplo, un clic en un botn o la
seleccin de una opcin en un listado. En este caso se lanza el mtodo finish(), en el que se
pueden transferir algunos datos como respuesta de la ejecucin a la actividad principal inicial.
En esta actividad principal se utiliza el mtodo startActivityForResult() para recibir estos
datos de la subactividad.
A continuacin, vamos a ver cmo funciona la invocacin explcita de Intenciones en
Android mediante un ejemplo sencillo que simula una aplicacin que gestiona un nico
contacto. En este ejemplo debemos prestar atencin al cdigo Java que interrelaciona ambas
Actividades.
Lo primero que vamos a hacer es definir el fichero XML que incluye los diseos (layout)
de ambas actividades. Puedes encontrar este diseo en los ficheros del proyecto Android:
243

res/layout/main.xml: actividad principal.

res/layout/segundaactividad.xml: actividad secundaria o subactividad.

Despus, creamos el cdigo fuente Java para las dos actividades. La segunda
actividad, que se invoca desde la primera, muestra los datos recibidos e indica si devuelve
datos a la actividad principal. Veamos el cdigo fuente de la actividad principal:
public class IntencionexplicitaActivity extends Activity {
private static final int COD_PETICION = 10;
private String nombre, apellidos;
private TextView resultadoLbl;
private Button ModContactoBtn, AltaContactoBtn;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Subrayamos la etiqueta que hace de ttulo
TextView tituloLbl = (TextView) findViewById(R.id.tituloLbl);
SpannableString contenido = new SpannableString(tituloLbl.getText());
contenido.setSpan(new UnderlineSpan(), 0, contenido.length(), 0);
tituloLbl.setText(contenido);
resultadoLbl = (TextView) findViewById(R.id.resultadoLbl);
// Al arrancar la aplicacin como no hay un contacto no se puede
modificar
ModContactoBtn = (Button) findViewById(R.id.ModContactoBtn);
AltaContactoBtn = (Button) findViewById(R.id.AltaContactoBtn);
ModContactoBtn.setEnabled(false);
resultadoLbl.setText("No has dado de alta ningn contacto");
// Cargamos las variables con el dato
nombre="";
apellidos="";
}
public void onClick(View view) {
Intent i = new Intent(this, ActividadDos.class);

244

Intents

switch (view.getId()) {
case R.id.AltaContactoBtn:
i.putExtra("operacion", "alta");
i.putExtra("nombre", "");
i.putExtra("apellidos", "");
break;
case R.id.ModContactoBtn:
i.putExtra("operacion", "modifica");
i.putExtra("nombre", nombre);
i.putExtra("apellidos", apellidos);
break;
}
// Invocamos explcitamente la actividad con los datos "i"
startActivityForResult(i, COD_PETICION);
}

@Override
// Este mtodo se invoca cuando la subactividad finaliza
protected void onActivityResult(int requestCode, int resultCode, Intent
data) {
// Si el Cd. peticin no coincide (otra aplicacin est usando esta
// actividad tambin) no tratamos la informacin
if (requestCode != COD_PETICION) return;
// Si la subactividad responde OK
if (resultCode == RESULT_OK) {
resultadoLbl.setText("");
if (data.hasExtra("nombre")) {
resultadoLbl.setText("Nombre: " +
data.getExtras().getString("nombre")+"\n");
nombre= data.getExtras().getString("nombre");
}
if (data.hasExtra("apellidos")) {
resultadoLbl.append("Apellidos: " +
data.getExtras().getString("apellidos")+"\n");
apellidos= data.getExtras().getString("apellidos");

245

}
if (!nombre.isEmpty() || !apellidos.isEmpty()) {
AltaContactoBtn.setEnabled(false);
ModContactoBtn.setEnabled(true);
String auxStr = "modificado";
String operacion = data.getExtras().getString("operacion");
if (operacion.equals("alta")) auxStr = "dado de alta";
Toast.makeText(this, "Has " + auxStr + " el contacto
correctamente", Toast.LENGTH_SHORT).show();
} else {
AltaContactoBtn.setEnabled(true);
ModContactoBtn.setEnabled(false);
}

} else
Toast.makeText(this, "Has salido de la subactividad sin pulsar
el botn 'Aceptar'", Toast.LENGTH_SHORT).show();
}
}

En el cdigo anterior es importante fijarse en cmo se invoca la subactividad mediante


el mtodo startActivityForResult() y cmo se trata la respuesta de sta en el mtodo
onActivityResult() de la actividad principal.
Veamos ahora el cdigo fuente de la actividad secundaria o subactividad:

public class ActividadDos extends Activity {


private EditText nombre, apellidos;
private TextView tituloLbl;
private String operacion;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.segundaactividad);
// Buscamos los componentes de la UI

246

Intents

nombre = (EditText) findViewById(R.id.nombre);


apellidos = (EditText) findViewById(R.id.apellidos);
tituloLbl

= (TextView) findViewById(R.id.tituloLbl);

// Obtenemos el contenidos de la variable extras


Bundle extra = getIntent().getExtras();
// Si no hay variable extra no ejecutamos las siguientes sentencias
if (extra == null) return;
// Leemos los contenidos de las variables extra
String valor1 = extra.getString("nombre");
String valor2 = extra.getString("apellidos");
operacion = extra.getString("operacion");
if (valor1 != null && valor2 != null && operacion != null) {
nombre.setText(valor1);
apellidos.setText(valor2);
// Cambiamos el ttulo de la pantalla en funcin de la operacin
if (operacion.equals("alta")) tituloLbl.setText("Alta de
contacto. Indica el nombre y los apellidos.");
else tituloLbl.setText("Modificar contacto. Cambia el nombre y
los apellidos.");
}
}
// Cuando el usuario pulsa aceptar devolvemos la informacin de nuevo
public void onClick(View view) {
Intent datos = new Intent();
datos.putExtra("nombre", nombre.getText().toString());
datos.putExtra("apellidos", apellidos.getText().toString());
datos.putExtra("operacion", operacion);
// Indicamos OK en el resultado
setResult(RESULT_OK, datos);
finish();
}
@Override
// Mtodo de la actividad que se invoca cuando sta finaliza
public void finish() {

247

super.finish();
}
}

En el cdigo anterior es importante fijarse en cmo se usa la clase Bundle en el


mtodo onCreate() para leer los datos extra que incorpora la actividad principal al invocar la
actividad secundaria y cmo se devuelve la respuesta de sta con los mtodos setResult() y
finish() a la actividad principal.
Finalmente, declaramos la actividad secundaria en el fichero "AndroidManifest.xml":

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad5.eje1.intencionexplicita"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />

<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".IntencionexplicitaActivity"
android:label="@string/app_name">
<intent-filter>

<action android:name="android.intent.action.MAIN" />


<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Declaramos la actividad secundaria -->
<activity android:label=" @string/actividadSecundaria"
android:name=".ActividadDos">
</activity>
</application>
</manifest>

248

Intents

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Intencin explcita) de la Unidad 5.


Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior,
en el que hemos utilizado Intenciones invocadas de manera explcita.

Si ejecutamos la aplicacin, vemos que al hacer clic en cualquiera de los botones de la


pantalla siguiente:

se inicia la actividad secundaria para hacer una tarea en concreto:

5.2.3

<b Invocacin Implcita


El Ejemplo 2 de este Unidad muestra cmo invocar intenciones implcitamente entre

dos actividades mediante Acciones. Para ello, vamos usar actividades de aplicaciones que el
sistema operativo instala por defecto.
Lo primero que vamos a hacer es definir el fichero XML que incluye el diseo (layout)
de la actividad principal. Puedes encontrar este diseo en el fichero res/layout/main.xml del
proyecto Android.
Para poder usar Intents con componentes de Android hay que aadir los siguientes
permisos a la aplicacin en el fichero "AndroidManifest.xml":

<!-- Permisos que necesita la aplicacin -->

249

<uses-permission android:name="android.permission.CALL_PRIVILEGED"></usespermission>
<uses-permission android:name="android.permission.CALL_PHONE"></usespermission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.READ_CONTACTS"></usespermission>

Despus, creamos el cdigo fuente Java para la actividad principal:


// Mtodo que usan los botones de la pantalla principal
public void invocaIntent(View view) {
Intent intent = null;
// Invocamos un Intent con una Accin y con un dato de informacin para la
// accin: un telfono, una direccin Internet, etctera.
switch (view.getId()) {
case R.id.navegadorBtn:
intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://www.google.es/"));
startActivity(intent);
break;
case R.id.llamarTfnoBtn:
intent = new Intent(Intent.ACTION_CALL,
Uri.parse("tel:(+34)12345789"));
startActivity(intent);
break;
case R.id.marcarTfnoBtn:
intent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:(+34)12345789"));
startActivity(intent);
break;
case R.id.contactosBtn:
intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("content://contacts/people/"));
startActivity(intent);
break;
case R.id.selContactoBtn:

250

Intents

intent = new Intent(Intent.ACTION_PICK,


Uri.parse("content://contacts/people/"));
// Ejecutamos este Intent indicando que esperamos la respuesta
startActivityForResult(intent, SELEC_CONTACTO);
break;
} // end switch
}

@Override
// Mtodo que se lanza cuando un Intent acaba su tarea. En este caso slo la
// accin que selecciona un contacto devuelve informacin
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == SELEC_CONTACTO) {
// En la Unidad 7 veremos los Content Providers. En este caso usamos
los contactos del telfono
Uri contactoData = data.getData();
Cursor c = managedQuery(contactoData, null, null, null, null);
if (c.moveToFirst()) {
String nombre= c.getString(c.getColumnIndexOrThrow(
ContactsContract.Contacts.DISPLAY_NAME));
Toast.makeText(this, "Nombre seleccionado: "+ nombre,
Toast.LENGTH_LONG).show();
}
} // end RESULT_OK
}

En el cdigo fuente anterior podemos ver que algunas actividades se inician con el
mtodo startActivity() porque no queremos saber el resultado de la ejecucin de la
subactividad. Sin embargo, el segundo botn "Seleccionar contacto" se inicia con el mtodo
startActivityForResult() para obtener el resultado de la ejecucin de la subactividad en el
mtodo onActivityResult(). En la prctica lo que hacemos es obtener el contacto
seleccionado mediante un identificador URI de la base de datos de contactos del telfono.

Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Intencin implcita) de la Unidad 5. Estudia
el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el
que hemos utilizado Intenciones invocadas de manera implcita.

251

Si ejecutamos la aplicacin, veremos que al hacer clic en cualquiera de los botones de


la pantalla se inicia una nueva Actividad:

Fjate que en el cdigo fuente no especificamos la aplicacin que tiene que lanzarse
para esa Accin, simplemente dejamos que Android decida qu aplicacin es ms apropiada
para la tarea solicitada.

5.2.4

<b Registro Accin para Invocacin Implcita


En nuestras aplicaciones Android podemos informar al sistema que la aplicacin

dispone de componentes que pueden ser utilizados por otras aplicaciones mediante Acciones.
El Ejemplo 2 de este Unidad muestra cmo definir filtros de intenciones (Filter Intens)
de una Actividad interna de la aplicacin para que pueda ser invocada implcitamente desde
otra aplicacin mediante su Accin correspondiente.
En este caso, hemos definido en el sistema operativo un nuevo navegador de Internet.
Por simplificacin, esta nueva actividad del navegador de Internet lo vamos a iniciar desde la
misma aplicacin, si bien esta opcin aparecer tambin en otras aplicaciones del sistema
operativo que deseen navegar por Internet.
Para que Android sepa que hay una nueva Accin disponible en el sistema operativo,
tenemos

que

aadir

"AndroidManifest.xml":
252

la

Actividad

con

la

Accin

correspondiente

en

el

fichero

Intents

<activity android:name=".NavegadorActivity"
android:label="@string/navegadorAct">
<!-- Filtro que declara el nuevo navegador en el sistema operativo -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http"/>
</intent-filter>
</activity>

En este caso, la accin es visualizar (VIEW) el protocolo HTTP (esquema del dato).
Hemos usado la etiqueta <intent-filter> para definir la nueva accin. Por la nomenclatura de
Android, es muy importante definir el punto "." en el nombre de la actividad:
android:name=".NavegadorActivity".
Lo primero que vamos a hacer es definir el fichero XML que incluye el diseo (layout)
de la actividad secundaria (Navegador de Internet). Puedes encontrar este diseo en el fichero
res/layout/navegador.xml del proyecto Android.

Despus, creamos el cdigo fuente Java para la nueva actividad:

// Clase sencilla que simula un navegador cargando una direccin de Internet


public class NavegadorActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.navegador);
// Usamos una etiqueta para mostrar la pgina descargada
TextView textoResultado = (TextView) findViewById(R.id.textView);
// Obtenemos el intent que invoca esta actividad
Intent intent = getIntent();
// Obtenemos el contenido de los datos del intent
Uri datos = intent.getData();
try {
// Formamos la URL que debemos cargar

253

URL url = new URL(datos.getScheme(), datos.getHost(),


datos.getPath());
// Leemos los datos de la pgina web y los vamos cargando en la
etiqueta
BufferedReader rd = new BufferedReader(new InputStreamReader(
url.openStream()));
String line = "";
while ((line = rd.readLine()) != null) {
textoResultado.append(line);
}

} catch (Exception e) {
e.printStackTrace();
}
}
}

Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Intencin implcita) de la Unidad 5. Estudia
el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el
que hemos definido una nueva Accin mediante Filtros de Intenciones.

Si ejecutamos la aplicacin, veremos que al hacer clic en el botn "Iniciar el


navegador" de la pantalla podemos seleccionar el navegador con el que queremos abrir la
pgina Web. Si hacemos clic en "Navegador curso Mentor", se inicia la nueva Actividad que
hemos definido anteriormente:

254

Intents

5.2.5

<b Detectar Acciones de Intents


A veces, es interesante detectar en nuestras aplicaciones de Android si podemos usar

una determinada Accin en un Intent. Por ejemplo, puede ocurrir que ampliemos la
funcionalidad de una aplicacin usando una actividad de otra aplicacin opcional que el
usuario puede haber instalado.
Esto se puede hacer usando la clase PackageManager de Android y el mtodo
queryIntentActivities(), para consultar si algn componente instalado en el telfono responde
a esa accin.
El siguiente cdigo comprueba si una Accin existe. As, es muy fcil cambiar el
comportamiento de una aplicacin, como mostrar u ocultar opciones de la misma.

public boolean existeAccion(Context context, String accion) {


final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(accion);
List<ResolveInfo> resolveInfo =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfo.size() > 0) {
return true;
}
return false;
}

255

5.3

PERMISOS Y SEGURIDAD EN ANDROID

Android es un sistema operativo que separa privilegios entre aplicaciones que se


ejecutan simultneamente usando identidades diferentes del sistema, es decir, con un ID de
usuario de grupo de Linux diferente (cada aplicacin se ejecuta con un usuario distinto).
Determinadas partes del sistema operativo tambin se separan usando diferentes identidades.
Linux asla as las aplicaciones entre s y del sistema operativo.
Adems, se proporcionan caractersticas adicionales de seguridad ms especficas a
travs del mecanismo de "permiso" que impone restricciones a las operaciones concretas que
un proceso en particular puede realizar.
Nota: por coherencia, en este apartado tratamos tambin la seguridad de Content Providers,
Servicios y Receptores de mensajes de difusin (Broadcast Receivers), que estudiaremos en la
Unidad 7. Puedes buscar informacin en esta Unidad para conocer los conceptos bsicos.

5.3.1

Arquitectura de seguridad de Android


La filosofa del diseo de la arquitectura de seguridad de Android consiste en

que, por defecto, una aplicacin no tiene permisos para realizar cualquier operacin que
pueda afectar negativamente a otras aplicaciones, al sistema operativo o al usuario. Esto
incluye la lectura o escritura de datos privados del usuario (como los contactos o correos
electrnicos), leer o escribir archivos de otra aplicacin, acceder a Internet, etctera.
Debido a que Android separa la ejecucin de las aplicaciones en cajas de arena (del
ingls sandbox), las aplicaciones deben compartir recursos y datos de manera explcita.
Un sandbox es un sistema de aislamiento de procesos, mediante el cual se pueden
ejecutar distintos programas con seguridad y de manera separada.
Para compartir recursos entre aplicaciones, stas deben declarar al sistema Android
los permisos adicionales que necesitan para funcionar correctamente y ampliar su
funcionalidad bsica. Las aplicaciones declaran los permisos que necesitan y el sistema
Android solicita al usuario su consentimiento cuando instala la aplicacin. Android no tiene
ningn mecanismo para dar permisos de forma dinmica (en tiempo de ejecucin).

5.3.2

Firma de aplicacin
Todas las aplicaciones Android (archivos .apk) deben estar firmadas con un certificado

que identifica al autor de la aplicacin. El propsito de los certificados de Android es distinguir


a los autores de las aplicaciones. Esto permite al sistema conceder o denegar solicitudes de
acceso para compartir la identidad de Linux que tenga otra aplicacin. Es decir, si el
desarrollador es el mismo, es posible ejecutar ambas aplicaciones con el mismo usuario de
Linux y que compartan el mismo sandbox.
256

Intents

5.3.3

ID de usuario y Acceso a ficheros


Cuando se instala una aplicacin, Android asigna a cada paquete un identificador

nico de usuario de Linux. Esta identidad no cambia mientras el paquete est instalado en el
dispositivo. En un dispositivo diferente el mismo paquete puede tener un ID de usuario
distinto; as pues, lo importante es que cada paquete siempre tiene otro ID de usuario en un
dispositivo cualquiera.
Debido a que la seguridad en Android se hace a nivel de proceso, el cdigo de dos
paquetes cualquiera no se puede ejecutar en el mismo proceso por defecto, ya que funcionan
con diferentes usuarios de Linux.
Sin

embargo,

se

puede

utilizar

el

atributo

sharedUserId

del

fichero

AndroidManifest.xml para asignar a cada paquete el mismo ID de usuario. De esta manera,


los dos paquetes se tratan como si fueran la misma aplicacin con el mismo ID de usuario y
los permisos de archivo. Es importante tener en cuenta que, para poder hacer esto, las dos
aplicaciones deben estar firmadas con el mismo certificado de autor.
A los datos almacenados por una aplicacin tambin se les asigna el ID de usuario y
normalmente otras aplicaciones no pueden acceder a ellos. Cuando se crea un nuevo archivo
con funciones del estilo getSharedPreferences(), openFileOutput(), etctera, se puede usar
los parmetros MODE_WORLD_READABLE y MODE_WORLD_WRITEABLE para permitir
que cualquier otra aplicacin lea o escriba en el archivo. Aunque se establezcan estos
indicadores, el archivo sigue siendo propiedad de la aplicacin original, si bien se permite su
lectura y escritura globalmente, de manera que cualquier otra aplicacin puede acceder a esta
informacin almacenada.
Por seguridad, no es recomendable usar la comparticin de informacin de esta
manera; es mejor usar Proveedores de contenidos (Content Provider) para ello.

5.3.4

Permisos de aplicaciones
Una aplicacin bsica de Android no tiene permisos asociados, por lo que no puede

hacer nada que afectara al usuario o a cualquier aplicacin instalada en el dispositivo. Para
hacer uso de las caractersticas protegidas del dispositivo, se debe incluir en el fichero
AndroidManifest.xml del proyecto una o ms etiquetas <uses-permission> que declaren los
permisos que necesita la aplicacin.
En alguno de los ejemplos anteriores del curso ya hemos usado esta etiqueta para
poder ampliar la funcionalidad de la aplicacin y acceder a determinados recursos del sistema
operativo.

257

Podemos escribir una o varias etiquetas <uses-permission> en el archivo


AndroidManifest.xml. El elemento <uses-permission> requiere que definamos el atributo
android:name, dentro del cual indicamos el nombre del permiso que requiere la aplicacin.
Por ejemplo:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.miapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
...
</manifest>

En este caso, estamos manifestando que esta aplicacin necesita poder recibir
mensajes cortos SMS para funcionar.
Como hemos comentado, los permisos se asignan en el momento en que se instala la
aplicacin en el dispositivo. Para ello, se pide al usuario su consentimiento para que la
aplicacin pueda acceder a los recursos solicitados.
Por lo tanto, a la hora de programar una aplicacin Android, es importante seleccionar
slo los permisos que realmente necesita esa aplicacin y justificar la peticin al usuario. Ten
en cuenta que cuando programes y pruebes aplicaciones en el emulador de tu ordenador, este
mensaje no aparece; solamente aparece al instalar las aplicaciones en un dispositivo real.
La pantalla que informa de los permisos tiene este aspecto:

El programador puede definir sus propios permisos internos de aplicacin para que
otra aplicacin los utilice. Ms adelante veremos cmo se hace.
Todos los permisos del sistema comienzan con el texto android.permission y se
pueden ver en la documentacin oficial de Android para la clase Manifest.permission. Como
258

Intents

hemos dicho, las aplicaciones de terceros pueden tener sus propios permisos. De forma
general, resumimos algunos de los permisos ms utilizados:

ACCESS_WIFI_STATE: permite a la aplicacin acceder a la informacin de las


conexiones WI-FI.

INTERNET: permite a la aplicacin acceder a Internet.

READ_CALENDAR, READ_CONTACTS: todos los permisos con el prefijo


READ_ permiten a la aplicacin leer informacin de un Content provider de
Android. En este caso, estos permisos otorgan acceso de lectura al calendario
y a los contactos del telfono.

WRITE_CALENDAR, WRITE_CONTACTS: todos los permisos con el prefijo


WRITE_

permiten a la aplicacin modificar informacin con un Content

provider de Android. En este caso, estos permisos otorgan acceso de


escritura al calendario y a los contactos del telfono.
Es posible que una aplicacin no reciba la autorizacin para hacer algo porque nos
hayamos olvidado de declarar el permiso necesario en el fichero AndroidManifest. En este
caso podemos usar la excepcin de tipo SecurityException, que indica el permiso que falta.
Existe otra forma de comprobar los permisos de nuestra aplicacin en tiempo de
ejecucin: podemos utilizar el mtodo Context.checkPermission(permiso, paquete) de la
clase PackageManager para comprobar si una aplicacin tiene concedido un permiso en
concreto. Este mtodo devuelve el valor PERMISSION_GRANTED o PERMISSION_DENIED
para indicar si la aplicacin tiene el permiso concedido o denegado respectivamente. En el
Ejemplo 3 de esta Unidad hemos usado este mtodo.

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Permisos de aplicaciones) de la Unidad 5.


Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior,
en el que hemos comprobado si la aplicacin tiene los permisos adecuados.

Si nos olvidramos de declarar los permisos necesarios de una aplicacin en el fichero


AndroidManifest y no controlamos en tiempo de ejecucin los permisos que tiene asignados,
al ejecutar esta aplicacin pulsando en su botn "Vibracin simple", por ejemplo, veramos el
siguiente mensaje y la aplicacin finalizara:

259

Para que funcione bien esta aplicacin, debemos aadir la siguiente etiqueta en el
fichero AndroidManifest:
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>

Este permiso posibilita a la aplicacin que el telfono vibre.


Para comprobar que la aplicacin tiene asignado este permiso, hemos escrito las
siguientes sentencias en la Actividad:

// Cargamos el objeto de gestor de paquetes


PackageManager p = this.getPackageManager();
// Buscamos si el paquete de este ejemplo tiene el permiso para vibrar el
telfono
if (p.checkPermission("android.permission.VIBRATE",
"es.mentor.unidad5.eje3.permisos")==PackageManager.PERMISSION_DENIED) {
...

As, si borramos la etiqueta que solicita el permiso de vibracin en el fichero


AndroidManifest.xml, veremos que la aplicacin muestra un error y no permite al usuario
utilizarla:

260

Intents

En el caso de que la aplicacin tenga los permisos adecuados, el usuario puede


utilizarla:

5.3.5

<Autoproteccin de aplicaciones Android


Otro aspecto que hay que conocer de los permisos de Android tiene que ver con la

forma en que podemos proteger el acceso de otras aplicaciones a nuestras aplicaciones.


Existen dos formas complementarias de enfocar la seguridad en aplicaciones Android:
1. Seguridad externa: la aplicacin est formada con Actividades e indicamos
los permisos necesarios para utilizar recursos de otras aplicaciones.
2. Seguridad interna (autoproteccin): la aplicacin utiliza Content Providers,
Servicios o Receptores de mensajes de difusin (Broadcast Receivers). En
este caso hay que controlar qu aplicaciones pueden acceder a la informacin
interna y cmo acceden a ella.
El primer procedimiento ya lo hemos visto anteriormente.
En el segundo procedimiento de autoproteccin de nuestras aplicaciones hay que
definir, igualmente, los permisos dentro del archivo AndroidManifest.xml.

261

En este caso, en lugar de escribir la etiqueta <uses-permission>, utilizamos la


etiqueta <permission> para declarar un permiso al sistema. De igual forma, podemos definir
uno o varios permisos.
La declaracin de un permiso es un poco ms compleja que usar un permiso. Para
ello, debemos proporcionar tres datos:

Nombre simblico del permiso: el nombre del permiso no puede coincidir


con el de otra aplicacin. Es recomendable utilizar nombres al estilo de las
clases Java de la aplicacin. Por ejemplo: es.mentor.ejemplo.VER_LISTADO

Etiqueta del permiso: nombre corto que ve el usuario.

Descripcin del permiso: texto ms extenso con la descripcin del permiso.

Ejemplo de permiso:
<permission
android:name="es.mentor.ejemplo.VER_LISTADO"
android:description="Permite ver el listado de ..."
android:label="Ver listado contactos" />

Esta definicin de permiso slo informa al sistema operativo de un posible permiso


que pueden usar otras aplicaciones; posteriormente, la aplicacin debe decidir cmo gestionar
estos permisos.

5.3.6

<Asignar permisos de componentes internos de la aplicacin


Dentro

de

las

actividades

los

servicios

podemos

definir

el

atributo

android:permission, que indica el nombre del permiso necesario para acceder a ese elemento
de la aplicacin. Fjate en este ejemplo:
<activity android:name=".nombreActividad"
android:label="@string/app_name"
android:permission="es.mentor.ejemplo.VER_LISTADO">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

De esta forma, nicamente las aplicaciones que hayan solicitado el permiso indicado
podrn acceder al elemento de forma segura. En este contexto, el acceso significa lo
siguiente:
262

Intents

Las actividades no pueden ejecutarse sin el permiso.

Los servicios no pueden arrancarse, detenerse o vincularse a una actividad sin


el permiso.

Los Receptores de mensajes de difusin ignorarn los mensajes enviados o


recibidos a menos que el remitente tenga el permiso.

Los Proveedores de contenidos (Content providers) ofrecen dos atributos


diferentes: readPermission y writePermission. Fjate en este ejemplo:

<provider android:name=".nombreProveedor"
android:authorities="es.mentor.ejemplo.proveedor"
android:readPermission="es.mentor.ejemplo.VER_LISTADO"
android:writePermission="es.mentor.ejemplo.ESCRIBIR_LISTADO" />
</provider>

En el ejemplo anterior, el atributo android:readPermission controla el acceso para


hacer consultas al Content provider y android:writePermission controla el acceso para
actualizar o borrar su informacin.

5.3.7

<Cmo obtienes permisos estas aplicaciones


Existen dos formas, en el cdigo fuente Java, de comprobar que las aplicaciones

externas estn solicitando los permisos necesarios para iniciar un componente de nuestra
aplicacin:

Los

servicios

pueden

checkCallingPermission().

verificar
Este

los

permisos
mtodo

usando
devuelve

el

mtodo
tambin

PERMISSION_GRANTED o PERMISSION_DENIED dependiendo de si la


aplicacin tiene el permiso o no. Como parmetro se indica el nombre del
permiso que se quiere comprobar.

Tambin cuando invocamos el mtodo sendBroadcast() para enviar un


mensaje de difusin (Broadcast) podemos incluir un permiso. As, slo los
receptores de mensajes de difusin que tengan este permiso podrn recibir el
mensaje enviado. Por ejemplo, Android incluye el permiso RECEIVE_SMS
para saber cundo se ha recibido en el telfono un mensaje SMS nuevo.

263

5.3.8

<Notas sobre seguridad en Android


Al compilar una aplicacin no existe un mecanismo que detecte automticamente los

permisos que necesita sta. Es decir, si no asignamos bien los permisos en el fichero
AndroidManifest, veremos los errores en tiempo de ejecucin. Por esto, debemos prestar
especial cuidado a la hora de declarar los permisos correctos y probar la aplicacin en el
emulador de Android.
Adems,

si

definimos

en

la

aplicacin

permisos

internos

determinados

componentes, es importante documentar bien estos permisos para que otros desarrolladores
puedan aprovechar las capacidades de nuestra aplicacin.

5.4

5.4.1

Tab.Layout

<Pantallas con pestaas con Tab Layout


En la Unidad 1 hemos visto cmo disear la interfaz de usuario de una aplicacin

Android mediante el uso de diversos componentes de tipo Layout, como los lineales, los
absolutos, los relativos, etctera.
Los Layouts son elementos organizativos bsicos de la interfaz, pero, teniendo en
cuenta el poco espacio disponible en las pantallas de los telfono o, simplemente, por
cuestiones de organizacin, a veces es interesante dividir la distribucin de los componentes
en varias pantallas.
Una de las formas clsicas de hacerlo en programacin es mediante la distribucin de
los componentes en pestaas (en ingls tabs).
Android tambin permite disear este tipo de interfaces de usuario, si bien lo hace de
una forma caracterstica, ya que la implementacin depende de varios elementos que deben
estar distribuidos y estructurados de una forma determinada.
Adicionalmente, no es suficiente con definir la interfaz en el fichero XML de diseo que
hemos utilizado en otros ejemplos, sino que tambin es necesario completarlo con algunas
sentencias de cdigo.
A continuacin, en el Ejemplo 4 de esta Unidad, vamos a ver paso a paso cmo
implementar este tipo de interfaces.
El elemento principal de Android de un conjunto de pestaas es el componente
TabHost. ste es el contenedor principal del conjunto de pestaas y debe tener
obligatoriamente como id el valor @android:id/tabhost.

264

Intents

Dentro de ste vamos a incluir un LinearLayout, que se usa para distribuir


verticalmente las secciones principales del TabHost, con las pestaas en la parte superior y la
seccin con el contenido de cada pestaa en la parte inferior.
La parte de pestaas se representa mediante un elemento TabWidget, que debe tener
el valor id @android:id/tabs.
La parte inferior que alberga el contenido de las pestaas se define con el elemento
FrameLayout y con el id obligatorio @android:id/tabcontent.
Por ltimo, dentro del FrameLayout incluimos el contenido de cada pestaa.
Normalmente, cada contenido se define dentro de su propio layout principal (en este ejemplo
hemos utilizado un LinearLayout), con un id nico que permita, posteriormente, hacer
referencia a l fcilmente. En el ejemplo hemos utilizado los identificadores pest1 y pest2.
A continuacin, se puede ver grficamente la estructura descrita anteriormente:

Si abrimos el fichero XML del layout, veremos el siguiente cdigo que corresponde a
esta estructura:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent" android:layout_width="match_parent">

<TabHost android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">

265

<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TabWidget android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@android:id/tabs" />

<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@android:id/tabcontent" >

<LinearLayout android:id="@+id/pest1"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView android:id="@+id/textView1"
android:text="Contenido de la Pestaa 1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

<LinearLayout android:id="@+id/pest2"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView android:id="@+id/textView2"
android:text="Contenido de la Pestaa 2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</FrameLayout>
</LinearLayout>

266

Intents

</TabHost>
</LinearLayout>

Como puedes observar en el cdigo anterior, por simplificacin, en el contenido de las


pestaas nicamente hemos aadido la etiqueta de texto Contenido de la Pestaa N
Pestaa.
Con el diseo anterior nicamente est montada toda la estructura de componentes
necesarios para la nueva pantalla con pestaas. Sin embargo, como ya indicamos al principio
de este apartado, esto no completa la funcionalidad.
Es necesario asociar cada pestaa con su contenido de forma que el componente de
pestaas funcione correctamente al cambiar de pestaa. Esto hay que hacerlo con sentencias
Java en la actividad principal.
Como viene siendo habitual, lo primero que hacemos es obtener una referencia al
componente principal TabHost y lo inicializamos preparndolo para su configuracin
invocando su mtodo setup().
Despus, creamos un objeto de tipo TabSpec para cada una de las pestaas que
queramos aadir mediante el mtodo newTabSpec(), al que pasamos como parmetro una
etiqueta identificativa de la pestaa; en el ejemplo usamos mipestania1 y mipestania2.
Adems, tambin asignamos el Layout del contenido correspondiente a la pestaa en
particular con el mtodo setContent().
Finalmente, indicamos el texto y el icono que se muestra en la pestaa mediante el
mtodo setIndicator(texto, icono).
Para acabar, aadimos la nueva pestaa al componente TabHost mediante el mtodo
addTab().
Veamos el cdigo completo:
// Obtenemos la referencia al componente TabHost
TabHost tabs=(TabHost)findViewById(android.R.id.tabhost);
// Preparamos su configuracin
tabs.setup();
// Preparamos un objeto con referencia a la pestaa 1
TabHost.TabSpec pestania=tabs.newTabSpec("mipestania1");
//Establecemos el contenido de la pestaa 1
pestania.setContent(R.id.pest1);
// Definimos la pestaa 1 en el TabHost

267

pestania.setIndicator("Pestaa 1",
res.getDrawable(android.R.drawable.ic_menu_agenda));
//Aadimos la pestaa 1 al TabHost
tabs.addTab(pestania);
// Preparamos un objeto con referencia a la pestaa 2
pestania=tabs.newTabSpec("mipestania2");
//Establecemos el contenido de la pestaa 2
pestania.setContent(R.id.pest2);
// Definimos la pestaa 2 en el TabHost
pestania.setIndicator("Pestaa 2",
res.getDrawable(android.R.drawable.ic_menu_directions));
//Aadimos la pestaa 2 al TabHost
tabs.addTab(pestania);
// Indicamos la pestaa activa por defecto
tabs.setCurrentTab(0);

Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Pestaas) de la Unidad 5. Estudia el


cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que
hemos utilizado el componente TabHost.

Si ejecutamos la aplicacin en el emulador de Eclipse, veremos la siguiente pantalla:

268

Intents

Normalmente no se suelen usar los eventos disponibles del componente TabHost,


aunque, a modo de ejemplo, vamos a ver el ms interesante de ellos, que ocurre cuando el
usuario cambia de pestaa. El evento se denomina OnTabChanged e informa de la nueva
pestaa que ha seleccionado el usuario.
Este

evento

los

podemos

implementar

mediante

el

mtodo

setOnTabChangedListener() de la siguiente manera:

// Definimos el evento OnTabChanged (cuando el usuario ha cambiado de pestaa)


tabs.setOnTabChangedListener(new OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
Toast.makeText(getBaseContext(), "Pestaa seleccionada: " +
tabId, 1).show();
}
});

El mtodo onTabChanged() recibe como parmetro el identificador de la pestaa, no


su ID, identificador que asignamos al crear el objeto TabSpec con la pestaa correspondiente.
En el ejemplo del curso hemos detectado el cambio de pestaa y mostrado un
mensaje informativo con el identificador de la nueva pestaa seleccionada.
Al cambiar a la segunda pestaa vemos el mensaje Pestaa seleccionada:
mipestania2:

269

Las Intenciones (Intents) permiten a las aplicaciones de Android expresar la


intencin que desea ejecutar una accin sobre unos datos usando algn
componente de sta o de otra aplicacin.
Para crear una Intencin hay que usar el objeto Intent de Android.
Las Intenciones son mensajes asncronos que se usan para realizar acciones e
intercambiar datos, tanto en la peticin como en la respuesta, entre componentes
de las aplicaciones.
Las intenciones se invocan de dos formas:
o Explcita: invocando la clase Java del componente que queremos
ejecutar. Normalmente, se hace para invocar componentes de una misma
aplicacin.
o Implcita: invocando la accin y los datos sobre los que aplicar dicha
accin. Android selecciona, en tiempo de ejecucin, la actividad receptora
que cumple mejor con la accin y los datos solicitados.
Para que Android pueda iniciar un componente de una aplicacin, debe conocer
que existe; para ello, se declaran los componentes de una aplicacin en el fichero
AndroidManifest.xml.
La forma en que Android identifica qu componentes pueden responder a una
Accin es buscndola en los filtros de intencin (intent filters) que se declaran en
el archivo "AndroidManifest" de todas las aplicaciones.
Es posible emplear intenciones implcitas para proporcionar funcionalidad
(acciones) de nuevos componentes de aplicaciones desde los mens de
opciones de aplicaciones compiladas y antes de que existiera la accin en el
sistema.
Android es un sistema operativo que separa privilegios entre aplicaciones que
se ejecutan simultneamente.
Una caja de arena, en ingls sandbox, es un sistema de aislamiento de
procesos, mediante el cual se pueden ejecutar distintos programas con seguridad
y de manera separada.
270

Intents

Para compartir recursos entre aplicaciones, stas deben declarar al sistema


Android los permisos adicionales que necesitan para funcionar correctamente y
ampliar su funcionalidad bsica.
Es posible compartir el mismo sandbox entre dos aplicaciones siempre y cuando
estn firmadas con el mismo certificado del autor.
Hay dos formas complementarias de enfocar la seguridad en aplicaciones
Android:
1. Seguridad externa:
indicamos

los

la aplicacin est formada con Actividades e

permisos

necesarios

para

utilizar

recursos

de

otras

aplicaciones.
2. Seguridad interna (autoproteccin): la aplicacin utiliza Content
Providers, Servicios o Receptores de mensajes de difusin (Broadcast
Receivers). En este caso hay que controlar qu aplicaciones pueden acceder
a la informacin interna y cmo acceden a ella.
No existe un mecanismo que detecte automticamente al compilar los
permisos que necesita una aplicacin. Por esto, debemos prestar especial
cuidado a la hora de declarar los permisos correctos y probar la aplicacin en el
emulador de Android.
Android tambin permite disear interfaces de usuario con pestaas mediante la
clase TabHost.

271

BASES DE DATOS Y XML

NDICE
6.1 BASES DE DATOS .................................................................. 275
6.1.1 Introduccin ...........................................................................275
6.1.2 Teora sobre Bases de Datos ..............................................275
6.1.3 Ventajas de las bases de datos ..........................................279
6.1.4 Bases de datos relacionales................................................281
6.1.5 Diseo de bases de datos ...................................................283
6.2 SQLite en Android .................................................................. 284
6.2.1 Gestin de la informacin en Android ........................... 284
6.2.2 Gestin de la Base de Datos SQLite en Android........... 285
6.2.3 Creacin de Bases de datos SQLite.............................. 286
6.2.4 Modificacin de la informacin de BD SQLite............... 292
6.2.4.1 Mtodo insert() .....................................................293
6.2.4.2 Mtodo update() y mtodo delete() ..................294
6.2.5 Uso de parmetros en los mtodos SQLite................... 294
6.3 Consultas SQLite en Android ............................................... 295
6.3.1 Seleccin y recuperacin de Consultas de BD SQLite ..295
6.3.2 Ejemplo prctico de BD SQLite con Android ................ 298
6.3.3 Acceso y creacin de la Base de datos......................... 299
6.3.4 Recursos de diseo XML ............................................... 303
6.3.5 Actividades..................................................................... 304
6.3.6 Fichero Androidmanifest.xml ......................................... 310
6.4 GESTIN DE FICHEROS XML .............................................. 311
6.4.1 SAX es el modelo clsico en Android ............................ 316
6.4.2 SAX simplificado en Android.......................................... 323
6.4.3 DOM en Android ............................................................ 327
6.4.4 StAX en Android............................................................. 331

Introduccin al entorno Android

6.1

6.1.1

BASES DE DATOS

Introduccin
En esta Unidad vamos a repasar, como prembulo , la teora general sobre bases de

datos.
Despus, explicaremos cmo gestionar bases de datos SQLite en Android dentro de
las aplicaciones.
Finalmente, detallaremos el uso de ficheros XML en Android.

6.1.2

Teora sobre Bases de Datos


En

este

apartado

vamos

explicar

brevemente

algunos

conceptos

fundamentales sobre las bases de datos. Suponemos que el alumno o alumna ya tiene
conocimientos suficientes sobre las mismas y, por tanto, abordaremos slo algunos
conocimientos ms relacionados con la forma en que Android accede a las bases de datos y
trata su informacin.
El trmino base de datos es informtico, pero puede aplicarse tambin a la forma
como se almacena, ordena y utiliza manualmente la informacin. Por ejemplo, ya hemos visto
en la Unidad 4 cmo leer y almacenar informacin en ficheros de preferencias o archivos en la
memoria del dispositivo Android. La informacin de estos ficheros puede ser considerada una
base de datos en un sentido amplio. Es decir, se almacena y se consulta la informacin
cuando es necesario.
Sin embargo, en el sentido informtico, la palabra base de datos se refiere a una
coleccin, conjunto o depsito de datos, almacenados en un soporte magntico o de otro
tipo, accesibles por mltiples usuarios de forma rpida y eficaz mediante el ordenador a travs
de una o de varias aplicaciones informticas independientes de los datos. stos se relacionan
entre s y estn organizados de tal manera que es fcil introducirlos, actualizarlos, recuperarlos
o llevar a cabo con ellos otras operaciones de gestin.
En el caso de Android, las bases de datos son privadas y nicamente una
aplicacin puede acceder a ellas para leer y escribir datos. Cuando una aplicacin desea
consultar o modificar la informacin de la base de datos de otra aplicacin, Android dispone
de los Content Providers que permiten a otras aplicaciones hacer las peticiones necesarias a
la aplicacin que alberga la base de datos. sta devuelve a la aplicacin la informacin
solicitada con los resultados de esas operaciones.
Android usa SQLite como motor de base de datos relacional. En el siguiente apartado
veremos sus caractersticas. La informacin que mostramos a continuacin est basada en la
versin 3 de SQLite.
275

Generalmente, en las bases de datos relacionales, de las que hablaremos despus, la


informacin est almacenada y organizada en ficheros formados por filas y columnas,
como puede verse en el ejemplo siguiente, en el que se presentan algunos datos de cinco
libros de una biblioteca:
Columnas
Ttulo

Autor

Editorial

Antonio
El invierno en Lisboa Muoz
Molina

Seix Barral

Tener o ser?

Fondo
de
Econmica

Erich Fromm

Cultura

Filas
Crnica
de
una Gabriel Garca
Bruguera
muerte anunciada
Mrquez

El lobo estepario

Hermann
Hesse

Anaya Editores

La vida est en otra


Milan Kundera Seix Barral
parte

Cada fila contiene el ttulo, el autor y la editorial de un libro y se relaciona con las
dems filas gracias a que incluye el mismo tipo de informacin (datos de los libros) y en todas
ellas la informacin est organizada de la misma forma: la primera columna contiene el ttulo
del libro, la segunda, el autor y la tercera, la editorial.
As pues, una base de datos contiene un conjunto de ficheros cuya informacin est
organizada de tal forma que puede ser tratada informticamente con rapidez y eficacia. La
informacin de una base de datos puede almacenarse en un solo fichero o en varios.
Los ficheros de una base de datos estn grabados en el servidor. Tienen un nombre
(por ejemplo, flores, ros, libros, coches, amigos, artculos, clientes, ventas, facturas, etctera).
Su denominacin debe seguir las normas establecidas para que el nombre de un fichero sea
correcto. Como puede verse, hemos prescindido de las tildes en los identificadores que las
llevan ortogrficamente.
El tipo o extensin de estos ficheros de base de datos puede ser muy variado, segn
el tipo de base de datos utilizado: en dBase es dbf (Data Base File, Fichero de Base de Datos),
en Access mdb, en Interbase db o dbf, en MySQL myd, etctera.
En el caso de SQLite en Android, el archivo de la base de datos suele tener la
extensin .db, si bien, puede almacenarse en el directorio especfico de la aplicacin con

276

Introduccin al entorno Android

cualquier extensin e, incluso, sin ella. En otros sistemas operativos, los archivos de una base
de datos de tipo SQLite suelen tener la extensin .sqlite.
Las filas de un archivo de base de datos se denominan registros y las columnas,
campos (fields, en ingls).
As pues, un fichero de base de datos est integrado por registros, que son cada
uno de sus elementos o componentes (flor, ro, libro, coche, amigo, artculo, cliente, venta o
factura). Todos los registros contienen un conjunto de campos en los que se almacena su
informacin; este conjunto define la estructura del fichero que integra una base de datos.
En la representacin grfica siguiente puede observarse, en forma de tabla, la
estructura de un fichero que contiene una base de datos con informacin sobre personas:
Campos

Nombre Sueldo

Fecha_nac

Observacion Foto

1
2
3
4
Registros 5
6
7
8

En las filas aparecen hasta once registros, cada uno de los cuales, en este caso,
contiene los cinco campos siguientes: Nombre, Sueldo, Fecha_nac, Observacion y Foto.
En el ejemplo anterior slo se han incluido once registros y cinco campos, pero de
hecho en las bases de datos que vamos a usar el nmero de registros es ilimitado (depende
de la capacidad del soporte) y el de campos es muy amplio, segn el tipo de base de datos
usada. Todos los registros tienen los mismos campos.
Si comparamos un fichero de base de datos con los archivadores de una biblioteca,
podemos decir que stos integran la base de datos. Cada archivador es como un fichero de la
base de datos, las fichas que hay en su interior son los registros y los apartados de cada
ficha (ttulo, autor, editorial, etctera) son los campos.

277

Cada campo de un registro tiene un nombre, un tipo, una longitud o ancho, un


nmero de decimales si es de tipo numrico o de coma flotante y un ndice opcional.
Segn el tipo de base de datos que se est utilizando, el identificador del campo, la clase de
tipos y la longitud de los mismos pueden ser diferentes.
Vamos a centrarnos en los tipos de campos que define SQLite. Este tipo de base de
datos no define todos los tipos de campos tpicos en bases de datos relacionales. nicamente
define unos tipos de campos bsicos y luego los reutiliza para especificar otros tipos de
campos.
El nombre de cada campo puede ser muy largo, si bien recomendamos que en el
orden prctico sea lo ms breve posible y tenga algn significado. Debe atenerse a las reglas
de todos los identificadores ya comentadas anteriormente.
Hay estos tipos de campos bsicos en SQLite:
1. Campo de tipo Carcter. Es el ms comn (letras, dgitos, signos, etctera), y
contiene informacin que es tratada como una cadena de caracteres. Se asigna este tipo a un
campo cuando no se realizan operaciones aritmticas con sus datos, ni contiene una fecha, ni
es un texto mayor de 255 caracteres. Por ejemplo, se asigna este tipo al campo cuyo
contenido va a ser el nombre de una persona, sus apellidos, domicilio, localidad, provincia,
etctera. Admite ndice. En SQLite hay un tipo nico de campo para almacenar datos de esta
clase: TEXT que tiene una longitud mxima de 255 caracteres.
Tambin podemos usar los siguientes tipos de campos de texto en SQLite:
CHARACTER(20): campo de texto con 20 caracteres de longitud.
VARCHAR(255): campo de texto de longitud variable hasta 255 caracteres.
VARYING CHARACTER(255): similar al anterior.
NCHAR(x): campo de texto con x caracteres de longitud.
NATIVE CHARACTER(70): campo de texto con 70 caracteres de longitud.
NVARCHAR(100): campo de texto de longitud variable hasta 100 caracteres.
CLOB: campo similar a TEXT.
Todos estos tipos de campo de texto se pueden definir al crear una tabla, si bien,
internamente, SQLite los traduce por afinidad al tipo TEXT inicial.
2. Campo de tipo Numrico. Se utiliza para escribir nmeros, incluidos los signos
positivo y negativo. Se asigna este tipo a un campo cuando se realizan operaciones
aritmticas con nmeros enteros o reales, como sumar, restar, multiplicar, dividir, etctera.
Admite ndice. SQLite admite estos valores para determinar los campos de este tipo:
INTEGER y REAL. Como puede verse, en realidad los valores posibles se refieren a si es un
campo de nmero entero o decimal.
Podemos usar los siguientes tipos de campos de tipo entero en SQLite:

278

Introduccin al entorno Android

INT
TINYINT
SMALLINT
MEDIUMINT
BIGINT
UNSIGNED BIG INT
INT2
INT8
Todos estos tipos de campo de nmero entero se pueden definir al crear una tabla, si
bien, internamente, SQLite los traduce por afinidad al tipo INTEGER anterior.
En el caso del tipo de campo numrico con decimales, podemos usar los siguientes
tipos de campos:
DOUBLE
DOUBLE PRECISION
FLOAT
Todos estos tipos de campo de nmero con decimales se pueden definir al crear una
tabla, si bien, internamente, SQLite los traduce por afinidad al tipo REAL anterior.
3. Campo de tipo Fecha y Lgico. Puede contener fechas y tiempos (horas, minutos,
segundos) o almacenar valores lgicos (true / false). Admite ndice. SQLite define el tipo de
campo interno NUMERIC para almacenar otros tipos de campos necesarios en las
aplicaciones en una tabla, tales como los campos lgicos o de fecha, as como los que
establecen los decimales exactos en un campo numrico. Podemos usar los siguientes tipos
de campos en SQLite:
DECIMAL(10,5)
BOOLEAN
DATE
DATETIME
Todos estos tipos de campo se pueden definir al crear una tabla, si bien, internamente,
SQLite los traduce por afinidad al tipo NUMERIC anterior.
4. Campo de tipo Memo. Es un campo de longitud variable que admite gran cantidad
de texto o datos binarios segn nuestras necesidades. Para cada registro tendr una longitud
distinta, segn la cantidad de datos que se introduzcan en este campo. No admite ndice.
SQLite admite nicamente BLOB.

6.1.3

Ventajas de las bases de datos


Hemos dicho que los archivadores de una biblioteca o de una agenda pueden

considerarse, en cierta forma, bases de datos, pues en ellos se almacena informacin en un


279

determinado orden y es posible buscar esta informacin, consultarla, modificarla o eliminarla


con facilidad.
Sin embargo, todas estas operaciones suelen llevar mucho tiempo y, en ocasiones, no
se efectan tan fcilmente como desearamos. Adems, ocupan bastante espacio si la
informacin es abundante. Incluso, en ocasiones, algunas operaciones fundamentales son
imposibles de realizar manualmente.
Por ejemplo, si tenemos 1.000 fichas bibliogrficas ordenadas por autor y necesitamos
ordenarlas por ttulo, la operacin ha de realizarse manualmente, mirando una a una cada
ficha, lo cual puede hacerse muy largo y pesado. Podamos haber escrito dos ejemplares de
cada ficha, uno para el archivo por autores y otro para el de ttulos, pero esto hubiera llevado
el doble de tiempo, de trabajo y stas ocuparan el doble de espacio.
Supongamos ahora que necesitamos seleccionar todas las fichas en las que aparece
la misma editorial. De nuevo la tarea puede parecernos pesada y larga, y lo es. No digamos si
se cambia la situacin de los libros en los armarios de la biblioteca. Tambin ser necesario
modificar la signatura en las fichas.
Hemos puesto este ejemplo para explicar los graves problemas que se derivan
de la gestin manual de la informacin. Las dificultades aumentan a medida que crece el
volumen de informacin que debe manejarse y segn sean los criterios de ordenacin y
seleccin.
En una base de datos informtica, en cambio, al gestionarse la informacin
automticamente, muchos de los problemas anteriormente mencionados desaparecen.
En primer lugar, la rapidez de las operaciones fundamentales (introduccin de
datos, ordenacin por diferentes campos, consultas, bsquedas, elaboracin de informes,
actualizacin y modificacin de los datos, etctera) aumenta de una forma muy destacada.
En segundo lugar, el espacio que ocupa una base de datos es mucho menor que el
de cualquier otra forma de archivo manual. En un disco flexible de 3,5 pulgadas puede
almacenarse casi un milln y medio de caracteres. En los discos duros de los actuales
servidores el volumen de informacin puede ser prcticamente ilimitado.
En tercer lugar, las operaciones fundamentales de gestin de la informacin son
automticas, lo cual hace que sean menos pesadas y tediosas si son llevadas a cabo por el
ordenador. As pues, el trabajo se humaniza y el tiempo libre de las personas que manejan la
informacin es mayor.
Finalmente, la seguridad de los datos informatizados tambin es mayor que la
contenida en archivos de tipo manual, pues el ordenador nos permite hacer rpidamente
cuantas copias queramos de esa informacin en diferentes soportes.
Desde la aparicin de los ordenadores, stos se han dedicado al almacenamiento y
organizacin de grandes volmenes de datos. Igualmente, se han aplicado a la evaluacin de
280

Introduccin al entorno Android

las diversas soluciones propuestas para resolver los problemas de estructuracin y acceso a
dicha informacin.

6.1.4

Bases de datos relacionales


Se ha descubierto que la mejor forma de resolver estos problemas es organizar la

informacin de forma relacional. De aqu ha surgido el concepto de bases de datos


relacionales (RDBMS, Relation DataBase Management System).
El fundamento terico de las bases de datos relacionales es complejo, ya que se
basa en el concepto matemtico de relacin entre los elementos de un conjunto. Sus
caractersticas y propiedades formales requieren ciertos conocimientos de la teora de
conjuntos. Sin embargo, en la prctica, el concepto de relacin es muy sencillo de utilizar
porque en sta la organizacin de los datos es muy clara e intuitiva.
En otros tipos de organizacin de la informacin, como las bases de datos
jerrquicas o las bases de datos en red, anteriores a las relacionales, aparecan distintas
categoras de datos y estructuras muy complejas y poco flexibles que dificultaban la
posibilidad de relacionar stos con eficacia y rapidez. En cambio, en las bases de datos
relacionales la informacin se organiza en ficheros que tienen estructura tabular o en
forma de tabla, en la que todos los datos tienen la misma categora. Cada tabla tambin
recibe el nombre de relacin.
Por ejemplo, en el grfico siguiente puede observarse una tabla que contiene
diversos datos de personas:
Cabecera

Nombre

Direccin

Edad

Sexo

Profesin

Len Garca

C/ Zurita, 25

25

Admtvo.

Mara Prez

C/ Flores, 15

30

Abogada

C/ Ro Sil, 11

50

Dependiente

Filas
(Registros)
3

Jos
Rodrguez

Juana de Dios

Avda. Canarias, 50

70

Jubilada

Begoa Lpez

Pza. Segovia, s/n

15

Estudiante

Columnas (Campos)

Como se ve, una tabla consta de filas y de columnas; en cada columna, denominada
campo en la base de datos, hay un dato: Nombre, Direccin, Edad, etctera; cada fila es un
registro que contiene todos los datos de los elementos de la base. Cada tabla tiene un
281

registro especial, denominado cabecera, que contiene los nombres de los campos y sus
atributos (tipo y longitud).
Generalmente, una base de datos no consta de una sola tabla, sino de varias.
Grficamente puede representarse as:

Estas tablas no son independientes unas de otras, sino que tienen al menos un
campo comn con las otras a travs del cual se puede acceder a la informacin que
contienen todas en conjunto.
Por ejemplo, la base de datos de una biblioteca puede estar integrada por una tabla de
libros, otra de lectores, otra de prstamos y otra de editoriales. El fichero de libros puede
contener la informacin completa de cada volumen: ttulo, autor, editorial, ao de edicin,
precio, nmero de pginas, cdigo de materia, nmero de registro, etctera.
El fichero de editoriales contendr los datos de cada entidad editora: nombre,
direccin, telfono, plazo de entrega, descuentos, etctera.
El fichero de lectores estar integrado por los datos personales y profesionales de
stos: nombre, DNI, direccin, telfono, profesin, centro de trabajo, nmero de carn,
etctera.
El fichero de prstamos contendr datos de este tipo: nmero de registro del libro
prestado, nmero de carn del lector, fecha del prstamo, plazo, etctera.
Como puede verse, la informacin no debe repetirse en todos los ficheros, pero s
debe poder relacionarse. Por ejemplo, los ficheros de libros y editoriales, tienen en comn el
campo EDITORIAL. Los ficheros de libros y prstamos tienen en comn, al menos, el
NMERO DE REGISTRO del libro prestado, gracias a lo cual desde uno se puede acceder a
los datos del otro. Los ficheros de lectores y prstamos tienen en comn el campo CARN,
etctera.
282

Introduccin al entorno Android

Son bases de datos relacionales Microsoft Access, Oracle, SQL Server, MySQL,
SQLite y otras.

6.1.5

Diseo de bases de datos


El diseo de bases de datos puede presentar distinto tipo de dificultad dependiendo

de la complejidad e interrelacin de los datos que se quiera gestionar.


Imaginemos que una compaa area quiere gestionar toda la informacin contenida
en una base de datos relativa a los aviones y su mantenimiento, a los vuelos, viajes, destinos,
clientes, personal de la empresa, agencias de viajes, billetes, asistencia, etctera. Es evidente
que, en este caso, la complejidad es enorme y que para realizar el diseo de esta base se
requiere la colaboracin de tcnicos especialistas que faciliten la tarea.
Sin embargo, en la mayora de las ocasiones el diseo de una base de datos se
resuelve con uno, dos o tres ficheros como mximo. En este caso no es necesario profundizar
en aspectos complejos de tcnicas de diseo, sino que basta aplicar el sentido comn para
organizar los ficheros de la base de datos de forma coherente.
Deben crearse tantos ficheros como categoras o grupos de elementos distintos
haya que organizar. Por ejemplo, en una tienda que vende al por menor bastara con crear un
fichero de artculos y otro de proveedores, y a lo sumo otros tres: de pedidos, de ventas y de
clientes.
Antes de ponerse a crear una base de datos con el ordenador, es preciso
disearla previamente sobre el papel. La planificacin es fundamental en este caso para
evitar errores graves: falta de datos necesarios, repeticin innecesaria de algunos,
equivocacin del tipo de campo o falta de precisin en su longitud. Aunque es posible
modificar la estructura de una base de datos, una vez creada, se puede perder mucho tiempo
e incluso datos en esta operacin.
Disear una base de datos consiste en determinar los datos que van a introducirse en
ella, la forma como se van a organizar y el tipo de esos datos. Adems, se debe precisar la
forma como se van a solicitar y las clases de operaciones que hay que realizar con los
mismos: aritmticas, lgicas, de fechas, de carcter, etctera. Tambin conviene conocer los
resultados concretos

que

se

espera

obtener: consultas, informes, actualizaciones,

documentos, etctera.
A continuacin, se resumen las operaciones que deben llevarse a cabo al disear una
base de datos:
1. Atendiendo a la informacin que contiene es preciso:
Identificar los diferentes elementos informativos (artculos, clientes, ventas,
facturas, etctera) que forman parte de la base de datos.
283

Determinar los datos que debe contener cada uno de esos elementos.
Precisar el grado de necesidad y de utilizacin de cada dato.
Concretar las operaciones que se van a realizar con los datos: aritmticas, lgicas,
de salida slo por la pantalla, de salida tambin por la impresora, etctera.
Seleccionar el dato o datos esenciales que deben ser el campo clave por el que se
ordenarn las unidades o elementos mencionados.
Fijar los datos comunes a los diferentes ficheros de la base de datos que van a
permitir relacionar la informacin distribuida entre ellos.
2. Atendiendo a la estructura de la base de datos
Distribuir la informacin en ficheros segn los diferentes grupos que se hayan
hecho (artculos, clientes, etctera) y dar un nombre a cada fichero.
Determinar el nombre de cada campo de los registros de cada fichero. Este
nombre ha de ser claro y debe significar algo para que pueda recordarse fcilmente.
Decidir qu tipo conviene asignar a cada campo segn la clase de operaciones
que vayamos a realizar con sus datos.
Asignar a cada campo una longitud apropiada para tener los datos fundamentales
sin despilfarro de memoria interna ni de espacio en el disco duro o soporte
empleado.
Establecer un orden lgico y prctico agrupando los campos segn un criterio
concreto: clase e importancia de los datos, frecuencia de utilizacin, proximidad,
parecido, etctera.
Decidir cul o cules van a ser los campos clave permanentes y situarlos al
principio de la estructura.
No incluir campos que puedan ser el resultado de diversas operaciones de
tratamiento posterior.
Fijar los campos comunes a todos los ficheros para poder relacionarlos con otros
de la misma aplicacin.

6.2

6.2.1

SQLite en Android

Gestin de la informacin en Android


Como ya hemos estudiado, en Android existen tres formas de almacenar informacin

para usarla en las aplicaciones:

284

Preferencias de la aplicacin

Introduccin al entorno Android

Ficheros locales en el sistema de archivos del sistema operativo

Base de datos SQLite

En la Unidad 4 hemos tratado las dos primeras formas y en esta Unidad 6 veremos las
bases de datos.

6.2.2

Gestin de la Base de Datos SQLite en Android


SQLite es un motor de bases de datos relacional muy popular por sus caractersticas,

que son muy especiales, como las siguientes:

No necesita un servidor, ya que la librera se enlaza directamente en la


aplicacin al compilarla.

Ocupa muy poco tamao: slo unos 275 KB.

Precisa de poca o nula configuracin.

Es posible hacer transacciones.

Es de cdigo libre.

Android incorpora todas las herramientas necesarias para la creacin y gestin de


bases de datos SQLite mediante una API completa. Despus iremos viendo otros comandos
para realizar consultas ms complejas.
Usar bases de datos Android puede hacer ms lentas las aplicaciones debido a que es
necesario escribir y leer informacin de la memoria fsica del dispositivo.
Por lo tanto, es recomendable realizar esta operaciones de forma Asncrona, tal como hemos
estudiado en la Unidad 3 (Hilos). En los ejemplos de esta Unidad no vamos a incluir hilos, para
mostrar nicamente las sentencias de SQLite.

285

6.2.3

Creacin de Bases de datos SQLite


La forma usual en Android de crear, modificar y conectar con una base de datos

SQLite consiste en usar la clase Java SQLiteOpenHelper. En realidad, debemos definir una
clase propia que derive de ella y personalizarla para adaptarnos a las necesidades concretas
de la aplicacin.
La clase SQLiteOpenHelper define un nico constructor que, normalmente, no es
necesario reescribir y los dos mtodos abstractos onCreate() y onUpgrade() que tendremos
que implementar con el cdigo Java necesario para crear la base de datos acorde con las
necesidades de la aplicacin. Tambin debemos hacerlo para modificar su estructura si hace
falta cambiar los campos que definen alguna tabla al actualizar la versin de la aplicacin.
En el Ejemplo 1 de esta Unidad, vamos a crear una base de datos muy sencilla
llamada BDBiblioteca.db, con una nica tabla interna llamada Ejemplares que albergar
nicamente cinco campos:

_id: id registro de tipo INTEGER, ndice PRIMARIO y autoincremental

ttulo: de tipo TEXT

autor: de tipo TEXT

ao: de tipo INTEGER

prestado: BOOLEAN

En el caso de Android es obligatorio definir un ndice primario en la tabla con el identificador


_id para poder extraer, de manera sencilla, la informacin de los registros de una consulta
SQL mediante cursores usando la clase Cursor de Android.

Para

esto,

vamos

crear

la

clase

BibliotecaSQLiteHelper

derivada

de

SQLiteOpenHelper, donde reescribimos los mtodos onCreate() y onUpgrade() para


adaptarlos a la estructura de campos anterior:

// La clase se debe heredar de SQLiteOpenHelper


public class BibliotecaSQLiteHelper extends SQLiteOpenHelper {
//Sentencia SQL para crear la tabla Ejemplares
static String createBDSQL = "CREATE TABLE Ejemplares (_id integer primary
key autoincrement, titulo TEXT, autor TEXT, anio TEXT,
prestado BOOLEAN)";

// Definimos el constructor indicando el contexto de la aplicacin,


// el nombre de la base de datos y la versin de la BD

286

Introduccin al entorno Android

public BibliotecaSQLiteHelper(Context contexto, String nombre,


CursorFactory factory, int version) {
super(contexto, nombre, factory, version);
}

@Override
// Si la BD no existe, Android llama a este mtodo
public void onCreate(SQLiteDatabase db) {
//Se ejecuta la sentencia SQL de creacin de la tabla
db.execSQL(createBDSQL);
}

@Override
public void onUpgrade(SQLiteDatabase db, int versionAnterior, int
versionNueva) {
/* NOTA: para simplificar este ejemplo eliminamos directamente la tabla
* anterior y la creamos de nuevo.
* Sin embargo, lo normal sera migrar los datos de la tabla antigua
* a la nueva estructura de campos, por lo que las sentencias podran
* ser del estilo ALTER TABLE.
*/
//Se elimina la versin anterior de la tabla
db.execSQL("DROP TABLE IF EXISTS Ejemplares");

//Se crea la nueva versin de la tabla


db.execSQL(createBDSQL);
}
}

En el cdigo fuente anterior se define la variable esttica (en Java se definen as las
constantes) createBDSQL, donde se establece la orden SQL para crear la tabla llamada
Ejemplares con los campos alfanumricos descritos anteriormente.
ATENCIN: en este curso no se describe la sintaxis del lenguaje SQL, pues se considera que
el alumno o alumna conoce cmo usar una base de datos relacional.

287

El mtodo onCreate() se ejecuta automticamente cuando es necesario crear la base


de datos, es decir, cuando an no existe y se instala la aplicacin por primera vez. Por lo
tanto, en este mtodo debemos crear todas las tablas necesarias y aadir, si fuera necesario,
los registros iniciales.
Para la creacin de la tabla hemos aplicado la sentencia SQL ya definida en la
constante y la ejecutamos en la base de datos utilizando el mtodo ms sencillo disponible en
la API SQLite de Android execSQL(SQL). Este mtodo ejecuta directamente la orden SQL
incluida como parmetro.
Por otra parte, el mtodo onUpgrade() se ejecuta automticamente cuando sea
necesario actualizar la estructura de la base de datos al cambiar la versin de la aplicacin que
la alberga.
Por ejemplo, desarrollamos la versin 1 de la aplicacin que utiliza una tabla con los
campos descritos en el ejemplo anterior. Ms adelante, ampliamos la funcionalidad de la
aplicacin desarrollando la versin 2, que incluye en la tabla el campo "Editorial". Si un usuario
tiene instalada la versin 1 de la aplicacin en su dispositivo Android, la primera vez que
ejecute la versin 2 de la aplicacin hay que modificar la estructura de la tabla, para aadir el
nuevo campo; en este caso, Android ejecutar automticamente el mtodo onUpgrade().
Este mtodo recibe como parmetros la versin actual de la base de datos en el
sistema y la nueva versin a la que se quiere convertir. En funcin de esta informacin
debemos realizar unas acciones u otras. Por ejemplo, modificar la tabla con la orden "ALTER
TABLE" para aadir el nuevo campo.
Una vez que hemos implementado la clase SQLiteOpenHelper, podemos abrir
fcilmente la base de datos desde la aplicacin Android.
Lo primero que hacemos es crear un objeto de la clase BibliotecaSQLiteHelper al
que pasamos el contexto de la aplicacin (en el ejemplo pasamos la referencia a la actividad
principal), el nombre de la base de datos, un objeto CursorFactory (ms adelante veremos
cmo funcionan los cursores, en ese caso pasamos el valor null) y, por ltimo, la versin de la
base de datos de la aplicacin. Al crear este objeto pueden ocurrir varias cosas:

Si ya existe la base de datos y su versin actual coincide con la solicitada, se


conecta con ella.

Si la base de datos existe, pero su versin actual es anterior a la solicitada, se


invocar automticamente al mtodo onUpgrade(), para convertir la base de
datos a la nueva versin y conectarla con la base de datos convertida.

Si la base de datos no existe, se llamar automticamente al mtodo


onCreate() para crearla y se conectar con la base de datos creada.

288

Introduccin al entorno Android

Una vez obtenida una referencia al objeto BibliotecaSQLiteHelper, podemos invocar


el mtodo getReadableDatabase() para realizar consultas a la base de datos o el mtodo
getWritableDatabase() para llevar a cabo modificaciones, .
Despus, utilizando el mtodo execSQL(), podemos ejecutar rdenes SQL para
consultar o modificar registros. En el Ejemplo 1 se insertan cinco registros de prueba.
Por ltimo, cerramos la conexin con la base de datos llamando al mtodo close().
A continuacin, vemos el aspecto que tiene el cdigo fuente de la actividad principal:

//Abrimos la base de datos 'DBBiblioteca.db'


BibliotecaSQLiteHelper bibliodbh =
new BibliotecaSQLiteHelper(this, "DBBiblioteca.db", null, 1);
// Modo escritura
SQLiteDatabase db = bibliodbh.getWritableDatabase();
resultado.append("- La base de datos DBBiblioteca se ha abierto
correctamente.");
//Si se ha abierto correctamente la base de datos, entonces cargamos algunos
registros...
if(db != null)
{
// Hemos definido los datos en un fichero de recursos de la aplicacin
Resources res = getResources();
String titulos[] = res.getStringArray(R.array.titulos);
String autores[] = res.getStringArray(R.array.autores);
String anios[] = res.getStringArray(R.array.anios);
String prestados[] = res.getStringArray(R.array.prestados);
//Insertamos 5 libros de ejemplo
for(int i=0; i<5; i++)
{
String SQLStr="INSERT INTO Ejemplares (titulo, autor, anio,
prestado) " + "VALUES ('" + titulos[i] +"', '" +
autores[i] +"', " + anios[i] +", '" + prestados[i] +"')";
resultado.append("\n- SQL ejecutada: "+SQLStr);
//Insertamos los datos en la tabla Ejemplares
db.execSQL(SQLStr);
}

289

//Cerramos la base de datos


db.close();
resultado.append("\n- La base de datos DBBiblioteca se ha cerrado.");
}

Android almacena los archivos de la base de datos en la memoria interna del


dispositivo, en un directorio determinado, siempre el mismo, sin que el programador pueda
cambiarlo. Es el siguiente:
/data/data/paquete_java/databases/nombre_del_fichero
En el ejemplo anterior se almacena en:
/data/data/es.mentor.unidad6.eje1.crearbd/databases/DBBiblioteca.db
Si ejecutas el Ejemplo 1 de esta Unidad, puedes comprobar en el DDMS cmo se
crea el fichero correctamente en el directorio indicado. Para acceder a esta herramienta en
Eclipse hay que hacer Clic en la opcin del men principal: Window -> Open Perspective ->
DDMS:

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Crear base de datos SQLite) de la
Unidad 6. Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del
programa anterior, en el que hemos utilizado los mtodos de la base de datos SQLite.

Hemos visto que el fichero de la base de datos del Ejemplo 1 se ha creado en la ruta
correcta. Para comprobar que la tabla se ha creada correctamente y hemos insertado los
registros en la misma, podemos usar dos mtodos:
290

Introduccin al entorno Android

1. Transferir la base de datos a nuestro PC y consultarla con cualquier


administrador de bases de datos SQLite. Esto resulta un poco incmodo si
slo necesitamos hacer una pequea consulta, pero, a veces, es
imprescindible hacerlo para depurar errores en consultas SQL complejas.
2. Usar la consola de comandos del emulador de Android y recurrir a comandos
SQL para acceder y consultar la base de datos SQLite.
El primer mtodo es simple. Podemos transferir el fichero de la base de datos a
nuestro PC utilizando el botn de descarga situado en la esquina superior derecha del
explorador de archivos (enmarcado en rojo en la imagen anterior). Al lado de este botn hay
otro botn para hacer la operacin contraria, es decir, copiar un fichero local al sistema de
archivos del emulador. Adems, hay otro botn para eliminar ficheros del emulador.
NOTA: a veces, al desarrollar una aplicacin Android con bases de datos, el programador
debe eliminar a mano un fichero porque la estructura creada no es correcta y Android no
elimina el fichero automticamente cada vez que cargamos la aplicacin en el emulador de
Eclipse.
Una vez hemos descargado el fichero a nuestro PC, podemos utilizar cualquier
administrador de SQLite para abrir y consultar la base de datos.
En el segundo mtodo accedemos de forma remota al emulador a travs de su
consola de comandos (shell). Vamos a ver cmo hacerlo.
Ten en cuenta que para que este mtodo funcione debemos haber incluido bien el
PATH del SDK de Android en el sistema operativo del PC donde trabajemos. En caso de duda,
conviene repasar el documento de Instalacin de Android de este curso.
Con el emulador de Android ejecutndose, abrimos una consola de Windows (o del
sistema operativo correspondiente) y utilizamos la utilidad adb.exe (Android Debug Bridge)
situada en la carpeta platform-tools del SDK de Android.
En primer lugar, consultamos todos los identificadores de los emuladores en ejecucin
mediante el comando "adb devices". Este comando debe devolver una nica instancia con el
emulador abierto, que en el ejemplo se denomina emulator-5554.
Tras obtener este identificador del emulador activo vamos a acceder a su shell
mediante el comando adb -s <identificador-del-emulador> shell.
Una vez conectados a la consola del emulador, podemos acceder a la base de datos
utilizando el comando sqlite3 e indicando la ruta del fichero de la base de datos; en el caso
del ejemplo debemos escribir
sqlite3 /data/data/es.mentor.unidad6.eje1.crearbd/databases/DBBiblioteca.db.
A continuacin, debe aparecer el prompt de SQLite sqlite>, que nos indica que ya
podemos escribir consultas SQL sobre la base de datos.

291

Vamos a comprobar que se han insertado bien los cinco registros del ejemplo en la
tabla Ejemplares. Para ello, escribimos la siguiente orden: SELECT * FROM Ejemplares;.
Si la orden est escrita correctamente, veremos el resultado en la pantalla; si no, se
mostrar el error correspondiente. En la imagen siguiente se muestra el resultado de todos los
comandos:

Para salir del cliente SQLite debemos escribir el comando ".exit" (fjate que lleva un
punto delante) y para abandonar la shell del emulador debemos escribir el comando "exit".

6.2.4

Modificacin de la informacin de BD SQLite


La librera de SQLite incluida en Android proporciona dos formas para llevar a cabo

operaciones sobre una base de datos que no devuelven resultados. Por ejemplo, aadir,
actualizar y eliminar registros de una tabla; tambin se puede crear tablas, ndices de
bsqueda, etctera.
La primera forma. que ya la hemos visto anteriormente, consiste en usar el mtodo
execSQL() de la clase SQLiteDatabase. Este mtodo permite ejecutar cualquier orden SQL
sobre la base de datos siempre que no sea necesario obtener los resultados de la orden. Ya
hemos utilizado este mtodo indicando como parmetro la cadena de texto de la orden SQL.
Aunque ya hemos visto en el Ejemplo 1 cmo se usa este mtodo, a continuacin,
mostramos algunos ejemplos ms:

292

Introduccin al entorno Android

//Insertar un registro
db.execSQL("INSERT INTO Ejemplares (titulo, autor, anio, prestado)
VALUES ('Ttulo', 'Autor', 2001, 'false'));
//Eliminar un registro
db.execSQL("DELETE FROM Ejemplares WHERE _id=1");
//Actualizar un registro
db.execSQL("UPDATE Ejemplares SET autor='Nombre' WHERE _id=1");

La segunda forma disponible en Android consiste en utilizar los mtodos insert(),


update() y delete() proporcionados tambin por la clase SQLiteDatabase. Estos mtodos
permiten aadir, actualizar y eliminar registros de la tabla mediante parmetros, el valor del
campo y las condiciones en que debe aplicarse la operacin. Veamos un ejemplo de cada uno
de ellos:
6.2.4.1

Mtodo insert()

Este mtodo se usa para aadir nuevos registros en una tabla de la base de datos. Al
invocar insert (String table, String nullColumnHack, ContentValues values), es necesario
definir tres parmetros:

table: nombre de la tabla en la que insertamos un registro.

nullColumnHack: slo es necesario en casos muy puntuales, por ejemplo, al


insertar registros completamente vacos. Normalmente debemos indicar el
valor null en este segundo parmetro.

values: valores del registro que se inserta.

Los valores que queremos insertar los pasamos como elementos de una coleccin de
tipo ContentValues. Esta coleccin es del tipo duplos de clave-valor, donde la clave es el
nombre del campo de la tabla y el valor es el dato que debemos insertar en dicho campo.
Veamos un ejemplo sencillo:
//Creamos el registro a partir del objeto ContentValues
ContentValues nuevoRegistro = new ContentValues();
nuevoRegistro.put("titulo", "Ttulo de la obra");
nuevoRegistro.put("autor","Nombre del autor");
...
//Insertamos el registro en la tabla de la base de datos
db.insert("Ejemplares", null, nuevoRegistro);

293

Este mtodo devuelve el campo ID del nuevo registro insertado o el valor -1 si ocurre
algn error durante la operacin.

6.2.4.2

Mtodo update() y mtodo delete()

Estos mtodos se usan para actualizar o borrar registros de una tabla. Los mtodos
update (String table, ContentValues values, String whereClause, String[] whereArgs) y
delete(String table, String whereClause, String[] whereArgs) se invocan de manera
parecida a insert(). En estos mtodos hay que usar el parmetro adicional whereArgs para
indicar la condicin WHERE de la orden SQL.
Por ejemplo, para actualizar el autor del usuario de id 1 escribimos lo siguiente:
//Establecemos los campos-valores que actualizamos
ContentValues valores = new ContentValues();
valores.put("autor","Otro autor");
//Actualizamos el registro de la tabla
db.update("Ejemplares", valores, "_id=1");

En el tercer parmetro del mtodo update() indicamos la condicin tal como haramos
en la clusula WHERE en una orden UPDATE de SQL.
El mtodo delete() se aplica de igual forma. Por ejemplo, para eliminar el registro 2
escribimos lo siguiente:

//Eliminamos el registro del _id 2


db.delete("Ejemplares", "_id=2");

De nuevo, indicamos como primer parmetro el nombre de la tabla y como segundo la


condicin WHERE. Si queremos vaciar toda la tabla, podemos indicar null en este segundo
parmetro.

6.2.5

Uso de parmetros en los mtodos SQLite


En el caso de los mtodos execSQL(), update() y delete() de SQLiteDatabase

podemos utilizar argumentos como condiciones de la sentencia SQL. De esta manera,


podemos prescindir de SQL formadas con cadenas de texto muy largas y as evitamos errores
de codificacin.
294

Introduccin al entorno Android

Estos argumentos son piezas variables de la sentencia SQL, en forma de matriz, que
evitan tener que construir una sentencia SQL concatenando cadenas de texto y variables para
formar la orden final SQL.
Estos argumentos SQL se indican con el smbolo ? y los valores de dichos
argumentos deben pasarse en la matriz en el mismo orden que aparecen en la sentencia SQL.
Fjate en el siguiente ejemplo:
//Elimina un registro con execSQL() utilizando argumentos
String[] args = new String[]{"Nombre de autor"};
db.execSQL("DELETE FROM Ejemplares WHERE autor=?", args);
//Actualiza dos registros con update() utilizando argumentos
ContentValues valores = new ContentValues();
valores.put("Ttulo 1","Ttulo 2");
String[] args = new String[]{1, "2"};
db.update("Ejemplares", valores, "_id=? OR _id=?", args);

6.3

6.3.1

Consultas SQLite en Android

Seleccin y recuperacin de Consultas de BD SQLite


A continuacin, vamos a describir la manera de hacer consultas a una base de datos

SQLite desde Android y de extraer la informacin de datos del resultado.


Existen dos formas de buscar y recuperar registros de una base de datos SQLite. La
primera de ellas consiste en utilizar directamente un comando de consulta SQL; la segunda
forma consiste en utilizar un mtodo especfico con parmetros de consulta a la base de
datos.
La primera forma se basa en la utilizacin del mtodo rawQuery() de la clase
SQLiteDatabase, que ya hemos estudiado en el apartado anterior. En este indicamos
directamente como parmetro el comando SQL que queremos usar en la consulta sealando
los campos seleccionados y los criterios de seleccin.
El resultado de la consulta lo obtenemos en forma de Cursor. La clase Cursor
permite acceder en modo lectura/escritura a los resultados devueltos por una consulta a la
base de datos. Esta clase Cursor se puede usar con varios hilos (subprocesos) para obtener
asncronamente informacin de una BD.
Fjate en el siguiente ejemplo:
Cursor c = db.rawQuery(" SELECT autor,titulo FROM Ejemplares WHERE _id=1");

295

Tal y como hemos visto anteriormente en algunos mtodos de modificacin de datos,


tambin es posible incluir en este mtodo una lista de argumentos variables que indicamos en
la orden SQL con el smbolo ?; por ejemplo, as:
String[] args = new String[] {"1"};
Cursor c =
db.rawQuery(" SELECT autor,titulo FROM Ejemplares WHERE _id=? ", args);

La segunda forma de obtencin de datos se basa en utilizar el mtodo query() de la


clase SQLiteDatabase. Este mtodo query (String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having, String orderBy, String limit) se
invoca con varios parmetros:

table: nombre de la tabla consultada

columns: matriz con los nombres de los campos seleccionados

selection: clusula WHERE del lenguaje SQL

selectionArgs: matriz con los argumentos variables incluidos en el WHERE o


null si no se indican.

groupBy: clusula GROUP BY si existe; si no, escribimos null.

having: clusula HAVING si existe; si no, escribimos null.

orderBy: clusula ORDER BY si existe; si no, escribimos null.

limit: nmero mximo de registros devueltos por la consulta.

Veamos el mismo ejemplo anterior utilizando el mtodo query():

String[] campos = new String[] {"autor", "titulo"};


String[] args = new String[] {"1"};

Cursor c = db.query("Ejemplares", campos, "_id=?", args, null, null, null);

Tanto en la primera forma como en la segundo, ambos mtodos devuelven como


resultado de su ejecucin un objeto de tipo Cursor, que debemos recorrer para procesar los
registros obtenidos.
La clase Cursor dispone de varios mtodos para recorrer y manipular los registros
devueltos por la consulta. Entre ellos podemos destacar dos de los dedicados a recorrer el
cursor de forma secuencial y en orden natural:

moveToFirst(): mueve el puntero del cursor al primer registro devuelto. Si no


hay ningn primer registro este mtodo devuelve false.

296

Introduccin al entorno Android

moveToNext(): mueve el puntero del cursor al siguiente registro devuelto. Si


no hay ningn registro despus, este mtodo devuelve false.

moveToPrevious(): mueve el cursor al registro anterior. Si no hay ningn


registro anterior, este mtodo devuelve false.

getCount(): devuelve el nmero de registros devueltos por la consulta.

getColumnIndexOrThrow(String columna): devuelve el ndice de la columna


dada o lanza la excepin IllegalArgumentException si no existe la columna.

getColumnName(int indice): devuelve el nombre de la columna indicada en el


ndice dado.

getColumnNames(): devuelve una matriz con los nombres de las columnas


seleccionadas.

moveToPosition(int posicion): mueve el cursor al registro que hay en esa


posicin. Si no hay ningn registro en esa posicin, este mtodo devuelve
false.

getPosition(): devuelve la posicin actual del cursor.

Una vez colocado el cursor en el registro que queremos leer, podemos utilizar
cualquiera de los mtodos getXXX(ndice_columna) existentes para cada tipo de dato y as
recuperar el dato de cada campo de ese registro.
Por ejemplo, si queremos recuperar la segunda columna del registro actual y sta
contiene un campo alfanumrico, usamos la sentencia getString(1).
La primera columna de la consulta tiene el ndice 0, la segunda columna tiene ndice 1 y as
sucesivamente.
En el caso de que la columna contenga un dato de tipo real, ejecutaramos la
sentencia getDouble(1).
Teniendo todo esto en cuenta, veamos, a continuacin, cmo recorrer todos los
registros devueltos por la consulta del ejemplo anterior usando un cursor:
String[] campos = new String[] {"autor", "titulo"};
String[] args = new String[] {"1"};

Cursor c = db.query("Ejemplares", campos, "_id=?", args, null, null, null);

//Comprobamos que existe, al menos, un registro


if (c.moveToFirst()) {

297

//Recorremos el cursor mientras haya registros sin leer


do {
String autor = c.getString(0);
String titulo = c.getString(1);
} while(c.moveToNext());
}

Adems de los mtodos comentados de la clase Cursor, existen muchos ms


mtodos que pueden ser muy tiles. El alumno o la alumna puede consultar la lista completa
en la documentacin oficial de la clase Cursor.
Como hemos comentado ya en esta Unidad, las bases de datos SQLite de una
aplicacin son siempre privadas e internas a esta aplicacin. Para que el resto de aplicaciones
pueda acceder a la informacin de la BD, Android define los Content Provider. En la Unidad
7 tratamos este tema en profundidad.
Adems, se pueden tratar datos dinmicos de una base de datos usando la clase de
Android SQLiteQueryBuilder. Esta clase es similar a la interfaz de un proveedor de
contenidos, por lo que suele utilizarse conjuntamente con los Content Providers.

NOTA: Existen funcionalidades ms avanzadas de gestin de BD con Android, como la


utilizacin de transacciones, pero no vamos a tratarlas en este curso por considerarse
programacin avanzada.

6.3.2

Ejemplo prctico de BD SQLite con Android


A continuacin, vamos a mostrar mediante un ejemplo completo cmo se usan todos

los mtodos de acceso a base de datos que hemos estudiado hasta ahora.

Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Notas) de la Unidad 6. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que hemos
utilizado mtodos de la base de datos SQLite.

Si ejecutas la aplicacin, vers que tiene el siguiente aspecto:

298

Introduccin al entorno Android

Se trata de una aplicacin donde un usuario puede gestiona notas sencillas por
categoras. Estas notas se almacenan en la base de datos "bdnotas.db" en la tabla "notas"
que tiene la siguiente estructura:

_id: ndice de registro de tipo entero con ndice primario autoincremental

categoria: texto no nulo

titulo: texto no nulo

descripcion: texto no nulo

La aplicacin est formada por dos actividades: la primera muestra todas las notas en
un listado y la segunda permite editarlas o dar de alta una nueva. Ambas actividades se
interconectan con Intents invocados de manera explcita.
Para mostrar el listado con las notas en la actividad principal hemos heredado la clase
ListActivity. Como ya hemos visto anteriormente en el curso, esta clase define un ListView
interno. Podemos conectarlo con la clase Cursor, que devuelve los resultados de las consultas
a la BD, usando la clase SimpleCursorAdapter de Android.
Veamos cmo hacerlo en la prctica:

6.3.3

Acceso y creacin de la Base de datos


Como ya hemos visto anteriormente, para acceder y crear la base de datos de la

aplicacin es necesario crear una clase heredada de SQLiteOpenHelper. En este ejemplo


hemos definido la clase NotasBDHelper:
299

public class NotasBDHelper extends SQLiteOpenHelper {


// Definimos el nombre y la versin de la BD
private static final String BD_NOMBRE = "bdnotas.db";
private static final int BD_VERSION = 1;

// SQL que crea la base de datos


// Es muy importante usar el campo _id
private static final String BD_CREAR = "create table notas (_id integer
primary key autoincrement, " + "categoria text not null, titulo
text not null, descripcion text not null);";

// Contructor de la clase
public NotasBDHelper(Context context)

super(context, BD_NOMBRE, null, BD_VERSION);


}
// Mtodo invocado por Android si no existe la BD
@Override
public void onCreate(SQLiteDatabase database) {
// Creamos la estructura de la BD
database.execSQL(BD_CREAR);
}
// Mtodo invocado por Android si hay un cambio de versin de la BD
@Override
public void onUpgrade(SQLiteDatabase database, int oldVersion,
int newVersion) {
// Eliminamos la BD y la volvemos a crear otra vez
database.execSQL("DROP TABLE IF EXISTS notas");
onCreate(database);
}
}

Basada en esta clase anterior vamos a definir la nueva clase NotasBDAdapter, que es
la encargada de hacer las consultas a la base de datos, borrar y actualizar registros de sta.
300

Introduccin al entorno Android

Dentro de esta clase hemos definido el mtodo abrir(), que se conecta a la base de
datos utilizando la clase NotasBDHelper.
Para actualizar y dar de alta registros hemos usado un argumento del tipo
ContentValues, que hemos estudiado en el apartado anterior.

class NotasBDAdapter {
// Campos de la BD
public static final String CAMPO_ID = "_id";
public static final String CAMPO_CATEGORIA = "categoria";
public static final String CAMPO_TITULO = "titulo";
public static final String CAMPO_DESCRIPCION = "descripcion";
private static final String TABLA_BD = "notas";
private Context contexto;
private SQLiteDatabase basedatos;
private NotasBDHelper bdHelper;

public NotasBDAdapter(Context context) {


this.contexto = context;
}
// Mtodo que abre la BD
public NotasBDAdapter abrir() throws SQLException {
// Abrimos la base de datos en modo escritura
bdHelper = new NotasBDHelper(contexto);
basedatos = bdHelper.getWritableDatabase();
return this;
}
// Mtodo que cierra la BD
public void cerrar() {
bdHelper.close();
}
// Mtodo que crear una nota. Devuelve el id del registro nuevo si se ha
// dado de alta correctamente o -1 si no.
public long crearNota(String categoria, String titulo, String descripcion)
{
// Usamos un argumento variable para aadir el registro

301

ContentValues initialValues = crearContentValues(categoria, titulo,


descripcion);
// Usamos la funcin insert del SQLiteDatabase
return basedatos.insert(TABLA_BD, null, initialValues);
}
// Mtodo que actualiza una nota
public boolean actualizarNota(long id, String categoria, String titulo,
String descripcion) {
// Usamos un argumento variable para modificar el registro
ContentValues updateValues = crearContentValues(categoria, titulo,
descripcion);
// Usamos la funcin update del SQLiteDatabase
return basedatos.update(TABLA_BD, updateValues, CAMPO_ID + "=" + id,
null) > 0;
}
// Mtodo que borra una nota
public boolean borraNota(long id) {
// Usamos la funcin delete del SQLiteDatabase
return basedatos.delete(TABLA_BD, CAMPO_ID + "=" + id, null) > 0;
}
// Devuelve un Cursor con la consulta a todos los registros de la BD
public Cursor obtenerNotas() {
return basedatos.query(TABLA_BD, new String[] { CAMPO_ID,
CAMPO_CATEGORIA, CAMPO_TITULO, CAMPO_DESCRIPCION },
null, null, null, null, null);
}
// Devuelve la Nota del id
public Cursor getNota(long id) throws SQLException {
Cursor mCursor = basedatos.query(true, TABLA_BD, new String[] {
CAMPO_ID, CAMPO_CATEGORIA, CAMPO_TITULO, CAMPO_DESCRIPCION },
CAMPO_ID + "=" + id, null, null, null, null, null);
// Nos movemos al primer registro de la consulta
if (mCursor != null) {
mCursor.moveToFirst();

302

Introduccin al entorno Android

}
return mCursor;
}
// Mtodo que crea un objeto ContentValues con los parmetros indicados
private ContentValues crearContentValues(String categoria, String titulo,
String descripcion) {
ContentValues values = new ContentValues();
values.put(CAMPO_CATEGORIA, categoria);
values.put(CAMPO_TITULO, titulo);
values.put(CAMPO_DESCRIPCION, descripcion);

return values;
}
}

6.3.4

Recursos de diseo XML


A continuacin, indicamos los ficheros XML de Layout que componen el diseo de la

interfaz del usuario:

res/menu/menu_listado.xml: define el diseo del men principal de la


aplicacin.

res/layout/main.xml: define el diseo de la pantalla de la actividad principal


NotasActivity.

res/layout/editar_nota.xml: define el diseo de la actividad secundaria


GestionarNota, que sirve para editar y dar de alta notas.

res/layout/fila_notas.xml: define el diseo de los elementos del ListView de


la actividad principal, es decir, el estilo de cada nota en el listado.

El alumno o alumna puede abrir estos ficheros en su ordenador y ver cmo estn
implementados los distintos diseos.
Adems, se definen los dos ficheros strings.xml y categorias.xml en la carpeta
res/values con los literales que usa la aplicacin.

303

6.3.5

Actividades
Como hemos comentado, la aplicacin est formada por dos actividades: la actividad

principal (NotasActivity) muestra un listado con todas las notas y la segunda (GestionarNota)
sirve para editarlas o dar de alta una nueva.
Veamos el contenido de la actividad principal:

public class NotasActivity extends ListActivity {


private NotasBDAdapter bdHelper;
private static final int ACTIVIDAD_NUEVA = 0;
private static final int ACTIVIDAD_EDITAR = 1;
private static final int MENU_ID = Menu.FIRST + 1;
private Cursor cursor;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Hacemos ms ancha la lnea de divisin entre elementos en el
listado
this.getListView().setDividerHeight(3);

// Creamos el adaptador que conecta con la BD


bdHelper = new NotasBDAdapter(this);
// Cargamos todos los datos
bdHelper.abrir();
cargaDatos();
// Indicamos el men contextual asociado al listado
registerForContextMenu(getListView());
}
// Creamos el men principal
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menulistado, menu);

304

Introduccin al entorno Android

return true;
}
// El usuario hace clic en una opcin del men principal
@Override
public boolean onMenuItemSelected(int id, MenuItem item) {
// Buscamos la opcin del men principal seleccionada
switch (item.getItemId()) {
case R.id.insertar:
// Creamos una actividad indicando el tipo de peticin
// "ACTIVIDAD_NUEVA" y esperamos el resultado de la misma
Intent i = new Intent(this, DetallesNota.class);
startActivityForResult(i, ACTIVIDAD_NUEVA);
// Indicamos que hemos manejado la opcin del men
return true;
}
return super.onMenuItemSelected(id, item);
}
// El usuario hace clic en una opcin del men contextual del listado
@Override
public boolean onContextItemSelected(MenuItem item) {
// Buscamos la opcin del men contextual seleccionada
switch (item.getItemId()) {
case MENU_ID:
// Obtenemos el id del elemento seleccionado
AdapterContextMenuInfo info = (AdapterContextMenuInfo)
item.getMenuInfo();
// Borramos ese registro
bdHelper.borraNota(info.id);
// Recargamos los datos
cargaDatos();
// Indicamos que hemos manejado la opcin del men
return true;
}
return super.onContextItemSelected(item);

305

}
// Cuando hacemos clic en un elemento del listado, se edita la Nota
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
// Creamos una actividad indicando el tipo de peticin
// "ACTIVIDAD_EDITAR" y esperamos el resultado de la misma
Intent i = new Intent(this, DetallesNota.class);
// Pasamos el campo _id como un dato extra
i.putExtra(NotasBDAdapter.CAMPO_ID, id);
startActivityForResult(i, ACTIVIDAD_EDITAR);
}
// Mtodo que se llama cuando una subactividad devuelve el resultado
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
// Recargamos los datos si se ha modificado algo.
// Es decir, el usuario ha hecho clic en OK
if (resultCode == Activity.RESULT_OK) cargaDatos();
}
private void cargaDatos() {
cursor = bdHelper.obtenerNotas();
// Se indica que a la Actividad principal que controle los recursos
// cursor. Es decir, si se termina la Actividad, se elimina esta
// Cursor de la memoria
startManagingCursor(cursor);

// Indicamos cmo debe pasarse el campo ttulo de (from) a (to)


// la Vista de la opcin (fila_notas.xml)
String[] from = new String[] { NotasBDAdapter.CAMPO_CATEGORIA,
NotasBDAdapter.CAMPO_TITULO };
int[] to = new int[] { R.id.fila_categoria, R.id.fila_titulo };

306

Introduccin al entorno Android

// Creamos un sencillo adaptador de tipo Matriz


// asociado al cursor
SimpleCursorAdapter notas = new SimpleCursorAdapter(this,
R.layout.fila_notas, cursor, from, to);
// Indicamos al listado el adaptador que le corresponde
setListAdapter(notas);
}

// Creamos el men contextual


@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, MENU_ID, 0, R.string.menu_borrar);
}

// Cuando se acaba la Actividad cerramos la BD


// Es muy importante hacer esto para que se escriba toda la informacin
@Override
protected void onDestroy() {
super.onDestroy();
if (bdHelper != null) {
bdHelper.cerrar();
}
}
}

A continuacin, vamos a ver el cdigo de la Actividad secundaria o subactividad:

public class GestionarNota extends Activity {


private EditText tituloText;
private EditText descripcionText;

307

private Spinner categoriaSpinner;


// Usamos esta variable para saber si estamos editando (filaId=id) o
// se trata de un registro nuevo (filaId=null)
private Long filaId;
private NotasBDAdapter bdHelper;

@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
// Creamos un adaptador u abrimos la BD
bdHelper = new NotasBDAdapter(this);
bdHelper.abrir();
// Dibujamos el UI y buscamos sus Vistas
setContentView(R.layout.editar_nota);
categoriaSpinner = (Spinner) findViewById(R.id.category);
tituloText = (EditText) findViewById(R.id.nota_editar_titulo);
descripcionText = (EditText)
findViewById(R.id.nota_editar_descripcion);
Button aceptaBoton = (Button) findViewById(R.id.nota_editar_boton);
// Variable con el ID del registro actual
filaId = null;
// Obtenemos el campo ID que se debe haber pasado en la invocacin
// de la actividad si estamos editando el registro
Bundle extras = getIntent().getExtras();
// Si extras contiene algo cargamos ese ID
if (extras != null) {
filaId = extras.getLong(NotasBDAdapter.CAMPO_ID);
}

// Cargamos el registro en los componentes de la pantalla


cargarRegistro();
// Mtodo del botn OK
aceptaBoton.setOnClickListener(new View.OnClickListener() {

308

Introduccin al entorno Android

public void onClick(View view) {


// Si pulsa este botn guardamos los datos y devolvemos OK a la
Actividad
String categoria = (String) categoriaSpinner.getSelectedItem();
String titulo = tituloText.getText().toString();
String descripcion = descripcionText.getText().toString();

// Alta de registro
if (filaId == null) {
bdHelper.crearNota(categoria, titulo, descripcion);
} else { // Modificacin de registro
bdHelper.actualizarNota(filaId, categoria, titulo,
descripcion);
}

setResult(RESULT_OK);
// Acabamos la actividad
finish();
}

});
} // end onCreate

private void cargarRegistro() {


if (filaId != null) {
Cursor nota = bdHelper.getNota(filaId);
// Volvemos a dejar que la actividad actual controle el Cursos
startManagingCursor(nota);
// Obtenemos el campo categoria
String categoria = nota.getString(
nota.getColumnIndexOrThrow(NotasBDAdapter.CAMPO_CATEGORIA));

for (int i=0; i<categoriaSpinner.getCount();i++){


// Cargamos una de la opciones del listado desplegable

309

String s = (String) categoriaSpinner.getItemAtPosition(i);


// Si coindice con la que est en la BD la seleccionamos
en el listado desplegable
if (s.equalsIgnoreCase(categoria)){
categoriaSpinner.setSelection(i);
break;
}
}

// Rellenamos las Vistas de Ttulo y Descripcin


tituloText.setText(nota.getString(
nota.getColumnIndexOrThrow(NotasBDAdapter.CAMPO_TITULO)));
descripcionText.setText(nota.getString(
nota.getColumnIndexOrThrow(NotasBDAdapter.CAMPO_DESCRIPCION)));
}
} // end cargarRegistro
}

6.3.6

Fichero Androidmanifest.xml
Para que la subactividad GestionarNota est disponible en el sistema operativo,

debemos declararla en el archivo "AndroidManifest.xml" del proyecto, incluso si la vamos a


invocar de manera explcita. Para esto, escribimos en este fichero las siguientes lneas:

<activity android:name=".GestionarNota"
android:windowSoftInputMode="stateVisible|adjustResize">
</activity>

El

atributo

android:windowSoftInputMode

subactividad con el teclado flotante de Android:

310

indica

cmo

interacciona

esta

Introduccin al entorno Android

El establecimiento de este atributo afecta a dos aspectos del teclado:

Al estado del teclado de la pantalla, es decir, si est oculto o visible cuando la


actividad est en primer plano y el usuario interacciona con ella.

Al ajuste que sufren los componentes de la ventana principal de la actividad


para que el teclado quepa en la pantalla, es decir, si se ajusta el contenido
para dejar espacio al teclado o el contenido se mantiene intacto y el tecla
"flota" sobre ste.

En este ejemplo hemos usado las opciones stateVisible y adjustResize para que el
teclado se muestre cuando el usuario acceda a un componente de introduccin de texto y
cambie las proporciones de la pantalla para hacer un "hueco" al teclado.
En la ayuda oficial de Android puedes encontrar todos los posibles valores con su
descripcin.

6.4

GESTIN DE FICHEROS XML

EXtensible Markup Language (XML) es un formato de datos que se usa comnmente


en las aplicaciones web modernas. XML utiliza etiquetas personalizadas para describir los
tipos de datos y se codifica como texto sin formato, por lo que es muy flexible y sencillo de
utilizar. Android incluye bibliotecas de clases diseadas para el procesamiento de datos en
formato XML.
Los tres modelos ms extendidos para leer y escribir ficheros de tipo XML son DOM
(Document Object Model), SAX (Simple API for XML) y StAX (Streaming API for XML):

311

DOM: vuelca el documento XML en la memoria del dispositivo en forma de


estructura de rbol, de manera que se puede acceder aleatoriamente a los
elementos de las ramas.

SAX: en este modelo, basado en eventos, la aplicacin recorre todos los


elementos del archivo XML de una sola vez. La ventaja respecto al modelo
anterior consiste en que es ms rpido y requiere menos memoria, si bien no
permite el acceso aleatorio a una de sus ramas.

StAX: es una mezcla de las dos modelos anteriores. En este caso, tambin se
lee el fichero XML de forma secuencial, pero podemos controlar la forma en
que se leen sus elementos. En el caso de SAX es obligatorio leer todos los
elementos a la vez. Este modelo es tambin mucho ms rpido que DOM,
pero algo ms lento de SAX.

Un analizador sintctico (en ingls parser) convierte el texto de entrada en otras


estructuras (comnmente rboles), que son ms tiles para el posterior anlisis y capturan la
jerarqua implcita de la entrada.
Android dispone de analizadores XML para estos tres modelos. Con cualquiera de
ellos podemos hacer las mismas tareas. Ya veremos ms adelante que, dependiendo de la
naturaleza de la aplicacin, es ms eficiente utilizar un modelo u otro.
Estas tcnicas se pueden utilizar para leer cualquier documento XML, tanto de Internet
como del sistema de archivos. En el Ejemplo 3 de esta Unidad vamos a leer datos XML de un
documento RSS de un peridico; concretamente, del canal RSS de noticias de
20minutos.es.
Puedes modificar esta direccin cambiado la variable de la Actividad principal
XMLActivity:
static String feedUrl =
"http://20minutos.feedsportal.com/c/32489/f/478284/index.rss";

Si abrimos el documento RSS de esta fuente de noticias (en ingls feed), vemos la
estructura siguiente:
<rss version="2.0">
<channel>
<title>20minutos.es</title>
<link> http://www.20minutos.es/</link>
<description> Diario de informacin general y ...</description>
<language>es-ES</language>
<pubDate> Fri, 28 Oct 2011 18:54:41 GMT</pubDate>

312

Introduccin al entorno Android

<lastBuildDate> Fri, 28 Oct 2011 18:54:41 GMT</lastBuildDate>


<item>
<title>Ttulo de la noticia 1</title>
<link>http://link_de_la_noticia_2.es</link>
<description>Descripcin de la noticia 2</description>
<pubDate>Fecha de publicacin 2</pubDate>
</item>
<item>
<title>Ttulo de la noticia 2</title>
<link>http://link_de_la_noticia_2.es</link>
<description>Descripcin de la noticia 2</description>
<pubDate>Fecha de publicacin 2</pubDate>
</item>
...
</channel>
</rss>

Como puedes observar, se compone de un elemento principal <channel>, seguido de


varios datos relativos al canal y, posteriormente, de una lista de elementos <item> para cada
noticia.
En este apartado vamos a describir cmo leer este archivo XML sirvindonos de cada
una de las tres alternativas citadas anteriormente.
Desde Eclipse puedes abrir el proyecto Ejemplo 3 (XML) de la Unidad 6. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que hemos
utilizado mtodos de lectura del formato XML.

Si ejecutas la aplicacin en el emulador de Android, vers que tiene el siguiente


aspecto:

313

Para implementar la Actividad principal, hemos usado la clase ListActivity, donde


mostraremos un listado con las noticias.
Para empezar, en primer lugar debemos definir una clase Java para almacenar los
datos ledos de una noticia. Para cargar el listado de la clase ListActivity con los titulares de
las noticias usamos una lista de objetos de este tipo. Veamos el cdigo fuente de esta clase
que hemos denominado Noticia:

// Clase que sirve para cargar en un objeto cada noticia que leamos del
fichero XML
public class Noticia {
// Dispone de las variables y mtodos tpicos de una clase sencilla
private String titulo;
private URL enlace;
private String descripcion;
private String fecha;

public URL getEnlace() {


return enlace;
}
public void setEnlace(String enlace) {
// Intentamos cargar el enlace en forma de URL.
// Si tenemos un error lanzamos una excepcin

314

Introduccin al entorno Android

try {
this.enlace = new URL(enlace);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}

public void setFecha(String fecha) {


while (!fecha.endsWith("00")){
fecha += "0";
}
this.fecha = fecha.trim();
}
public String getFecha() {
return this.fecha;
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
}

Por simplificacin, hemos tratado todos los datos como cadenas de texto.

315

6.4.1

SAX es el modelo clsico en Android

En el modelo clsico de SAX el tratamiento de un archivo XML se basa en un


analizador (parser) que lee secuencialmente el documento XML y va generando diferentes
eventos con la informacin de cada elemento ledo.
Por ejemplo, a medida que lee el documentos XML, si el analizador encuentra una
etiqueta <title> lanzar el mtodo startElement() del parser de inicio de etiqueta con la
informacin asociada. Si despus de esa etiqueta encuentra una cadena de texto, invocar el
mtodo characters() del parser con toda la informacin necesaria.
Por lo tanto, debemos implementar las sentencias necesarias para tratar cada uno de
los mtodos posibles que el analizador puede lanzar durante la lectura del documento XML.
Los principales mtodos que se pueden producir son los siguientes:

startDocument(): comienza el documento XML.

endDocument(): termina el documento XML.

startElement(): comienza una etiqueta XML.

endElement(): termina una etiqueta XML.

characters(): se ha encontrado una cadena de texto.

Puedes encontrar la lista completa de los mtodos en este enlace.


Estos mtodos se definen en la clase org.xml.sax.helpers.DefaultHandler. Por esto
hay que heredar esta clase y sobrescribir los mtodos necesarios. En este ejemplo la clase se
llama ParserSaxClasicoHandler:

public class ParserSaxClasicoHandler extends DefaultHandler{


// Variables temporales que usamos a lo largo del Handler
// Listado completo de las noticias
private List<Noticia> noticias;
// Noticia que estamos leyendo en ese momento
private Noticia noticiaActual;
// Variable temporal para almacenar el texto contenido en una etiqueta
private StringBuilder sbTexto;

// Mtodo que devuelve todo el listado de noticias


public List<Noticia> getNoticias(){

316

Introduccin al entorno Android

return noticias;
}

// Mtodo que se lanza al iniciar la lectura de un XML


@Override
public void startDocument() throws SAXException {
// Lanzamos el mtodo de la clase madre
super.startDocument();
// Iniciamos los variables temporales
noticias = new ArrayList<Noticia>();
sbTexto = new StringBuilder();
}

// Comienza una etiqueta XML


@Override
public void startElement(String uri, String localName,
String name, Attributes attributes) throws SAXException {
// Lanzamos el mtodo de la clase madre
super.startElement(uri, localName, name, attributes);
// Si leemos una nueva etiqueta item es que empieza una noticias
if (localName.equals(EtiquetasRSS.ITEM)) {
noticiaActual = new Noticia();
}
}

// Finaliza una etiqueta XML


@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
// Lanzamos el mtodo de la clase madre
super.endElement(uri, localName, name);
// Si estamos leyendo una noticia
if (this.noticiaActual != null) {

317

// Cargamos el campo correspondiente de la etiqueta que acabamos


de leer
if (localName.equals(EtiquetasRSS.TITLE)) {
noticiaActual.setTitulo(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.LINK)) {
noticiaActual.setEnlace(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.DESCRIPTION)) {
noticiaActual.setDescripcion(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.PUB_DATE)) {
noticiaActual.setFecha(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.ITEM)) {
// Si leemos el final de la etiqueta "item" aadimos la
noticia al listado
noticias.add(noticiaActual);
}
// Reiniciamos la variable temporal de texto
sbTexto.setLength(0);
}
}

// Se ha encontrado una cadena de texto


@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// Lanzamos el mtodo de la clase madre
super.characters(ch, start, length);
// Si estamos leyendo una noticia
if (this.noticiaActual != null)
// Asignamos el texto a la variable temporal
sbTexto.append(ch, start, length);
}
}

318

Introduccin al entorno Android

En el cdigo fuente anterior usamos la lista de noticias List<Noticia> para almacenar


todas la noticias ledas y el mtodo getNoticias() las devuelve al finalizar la lectura del
documento.
Despus, hay que implementar los mtodos SAX necesarios.
Una vez hemos implementado nuestro handler, vamos a crear la nueva clase
ParserSaxClasico que hace uso de este handler para analizar un documento XML en
concreto usando el modelo SAX.
Esta clase nicamente define un constructor que recibe como parmetro la direccin
de Internet del documento XML que hay que analizar. El mtodo pblico analizar() analiza el
documento XML y devuelve como resultado una lista de noticias. Veamos cmo queda esta
clase:

public class ParserSaxClasico

implements RSSParser {

// URL del archivo XML


private URL feedUrl;

// Constructor de la clase, se asigna la URL a la variable local


protected ParserSaxClasico(String feedUrl){
try
{
this.feedUrl= new URL(feedUrl);
}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
}

// Mtodo que lee el documento XML


public List<Noticia> analizar() {
// Creamos acceso a la API Sax de Android
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
// Creamos un analizador (parser) de SAX

319

SAXParser parser = factory.newSAXParser();


// Creamos el handle de SAX que implementamos en otra clase
ParserSaxClasicoHandler handler = new ParserSaxClasicoHandler();
// Analizamos el archivo con el handler anterior
parser.parse(getInputStream(), handler);
// Devolvemos las noticias encontradas
return handler.getNoticias();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

// Mtodo que abre una conexin a la URL y devuelve el


// puntero de tipo fichero al analizador correspondiente
private InputStream getInputStream()
{
try
{
return feedUrl.openConnection().getInputStream();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}

El constructor de la clase anterior acepta como parmetro una direccin URL del
documento XML que analiza y controla la validez de dicha direccin

generando una

excepcin si no puede crear la clase URL correspondiente.


Por otra parte, el mtodo analizar() es el encargado de crear un nuevo parser SAX y
de iniciar el proceso de anlisis pasando al parser una instancia del handler que hemos
creado anteriormente con una referencia al documento XML en forma de stream.
320

Introduccin al entorno Android

Para pasar una referencia en forma de stream, implementamos el mtodo auxiliar


privado getInputStream(), que abre la conexin con la direccin URL especificada mediante
el mtodo openConnection() y obtiene el stream de entrada mediante el mtodo
getInputStream().
En el apartado de Tratamiento de ficheros de la Unidad 4 hemos visto cmo usar la
clase stream en Android.
Finalmente, slo queda aplicar la clase ParserSaxClasico para cargar un documento
XML con el modelo SAX. Para ello, en la Actividad principal XMLActivity de la aplicacin
escribimos las siguientes sentencias:

// Creamos un objeto del parser (analizador XML) en funcin del tipo (opcin
// men principal). La direccin (URL) de la fuente de noticias es una
//constante en este ejemplo
RSSParser analizador = XMLParser.getParser(tipo, feedUrl);
// Guardamos el momento actual de inicio de
long inicio = System.currentTimeMillis();
// Descargamos y analizamos el fichero XML
noticias = analizador.analizar();
// Calculamos el tiempo que ha tardado en leer el XML
long duracion = System.currentTimeMillis() - inicio;
// Mostramos el tiempo de lectura del XML
Toast.makeText(this, "Se han cargado los datos en "+duracion+" milisegundos",
1).show();
// Creamos un listado con todos los ttulos de las noticias
List<String> titulos = new ArrayList<String>(noticias.size());
for (Noticia msg : noticias){
titulos.add(msg.getTitulo());
}

// Definimos Adaptador sencillo con un diseo sencillo y el listado ttulos


ArrayAdapter<String> adaptador =
new ArrayAdapter<String>(this, R.layout.fila, titulos);
this.setListAdapter(adaptador);

321

Primero creamos el parser correspondiente usando la direccin URL del documento


XML y, despus, ejecutamos el mtodo analizar() para obtener una lista de objetos de tipo
Noticia que, posteriormente, asignamos al adaptador del listado de la Actividad principal.
Si te fijas en el cdigo anterior estamos creando el objeto analizador a partir de la
clase XMLParser en lugar de ParserSaxClasico. Si abrimos el fichero que define la clase
XMLParser veremos el cdigo siguiente:

// Clase que crea un analizador XML del tipo necesario


// Se crea una interface RSSParser para poder renombrar el mtodo analizar()
public abstract class XMLParser {

public static RSSParser getParser(TiposParser tipo, String feedURL){


switch (tipo){
case SAX_CLASICO:
return new ParserSaxClasico(feedURL);
case DOM:
return new ParserDom(feedURL);
case SAX_SIMPLIFICADO:
return new ParserSaxSimplificado(feedURL);
case XML_PULL:
return new ParserXmlPull(feedURL);
default: return null;
}
}
}

Observa que, como estamos usando la misma aplicacin para mostrar cmo
funcionan todos los modelos de carga de archivos XML en Android, hemos creado una clase
abstracta que devuelve un objeto en funcin del tipo de analizador que el usuario ha decido
usar en ese momento.

NOTA: Para que esta aplicacin Android acceda a Internet, es necesario declararlo en el
fichero AndroidManifest.xml, que requiere el permiso "android.permission.INTERNET".

322

Introduccin al entorno Android

6.4.2

SAX simplificado en Android


El modelo SAX anterior de tratamiento de archivos XML, a pesar de funcionar perfecta

y eficientemente, tiene claras desventajas, ya que es obligatorio definir una clase


independiente para el handler. Adicionalmente, el modelo SAX implica poner bastante
atencin al definir dicho handler, ya que los mtodos SAX definidos no estn asignados a
etiquetas concretas del documento XML, sino que se lanzan para todas ellas. Esto obliga a
realizar distinciones entre etiquetas dentro de cada mtodo.
Esto se observa perfectamente en el mtodo endElement() que definimos en el
ejemplo anterior. En primer lugar, hay que comprobar con la sentencia condicional si el
atributo noticiaActual no est vaco (null), para no confundir el elemento <title> descendiente
de <channel> con el elemento <title> descendiente de <item>, que es el que queremos leer.
Posteriormente, hay que distinguir con unas sentencias IF entre todas las etiquetas
posibles la accin que debemos realizar.
Tengamos en cuenta que hemos usado un documento XML muy sencillo, pero si
tratamos un documento XML ms enrevesado, la complejidad de este handler aumenta
mucho y pueda dar lugar a errores de programacin.
Para evitar estos problemas, Android propone una variante del modelo SAX que evita
definir una clase separada para el handler y que permite asociar directamente las acciones a
etiquetas concretas dentro de la estructura del documento XML.
Veamos cmo queda el analizador XML utilizando SAX simplificado para Android:

public class ParserSaxSimplificado

implements RSSParser {

// Variables temporales que usamos a los largo del Handler


// Noticia que estamos leyendo en ese momento
private Noticia noticiaActual;
// Variable que define la etiqueta raz del XML que es <rss >
static final String RSS = "rss";
// URL del archivo XML
private URL feedUrl;

// Constructor de la clase, se asigna la URL a la variable local


protected ParserSaxSimplificado(String feedUrl){
try
{
this.feedUrl= new URL(feedUrl);

323

}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
}

// Mtodo que lee el documento XML


public List<Noticia> analizar() {
// Variable que almacena las noticias encontradas
final List<Noticia> noticias = new ArrayList<Noticia>();
// Buscamos el elemento raz <rss>
RootElement root = new RootElement(RSS);
// Buscamos el elemento channel dentro de la etiqueta raz (root)
Element channel = root.getChild(EtiquetasRSS.CHANNEL);
// Buscamos el elemento item dentro de la etiqueta channel
Element item = channel.getChild(EtiquetasRSS.ITEM);

/*
* Definimos los listerners de estos elementos anteriores
*/
// Mtodo de inicio de una nueva etiqueta item
item.setStartElementListener(new StartElementListener(){
public void start(Attributes attrs) {
noticiaActual = new Noticia();
}
});
// Mtodo de finalizacin de una nueva etiqueta item
item.setEndElementListener(new EndElementListener(){
public void end() {
noticias.add(noticiaActual);
}
});

324

Introduccin al entorno Android

// Mtodo de obtencin etiqueta title dentro de la etiqueta item


item.getChild(EtiquetasRSS.TITLE).setEndTextElementListener(new
EndTextElementListener(){
public void end(String body) {
noticiaActual.setTitulo(body);
}
});
// Mtodo de obtencin etiqueta link dentro de la etiqueta item
item.getChild(EtiquetasRSS.LINK).setEndTextElementListener(new
EndTextElementListener(){
public void end(String body) {
noticiaActual.setEnlace(body);
}
});
// Mtodo de obtencin etiqueta description dentro de la etiqueta item
item.getChild(EtiquetasRSS.DESCRIPTION).setEndTextElementListener(new
EndTextElementListener(){
public void end(String body) {
noticiaActual.setDescripcion(body);
}
});
// Mtodo de obtencin etiqueta pub_date dentro de la etiqueta item
item.getChild(EtiquetasRSS.PUB_DATE).setEndTextElementListener(new
EndTextElementListener(){
public void end(String body) {
noticiaActual.setFecha(body);
}
});
//Usamos el objeto Xml de Android para leer el archivo XML
try {
Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8,
root.getContentHandler());
} catch (Exception e) {
throw new RuntimeException(e);
}

325

// Devolvemos las noticias ledas


return noticias;
}

// Mtodo que abre una conexin a la URL y devuelve el


// puntero de tipo fichero al analizador correspondiente
private InputStream getInputStream()
{
try
{
return feedUrl.openConnection().getInputStream();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}

En este nuevo modelo SAX simplificado de Android las acciones que debemos realizar
dentro de cada mtodo se definen dentro de la misma clase asociadas a etiquetas concretas
del XML.
Para esto, lo primero que hacemos es navegar por la estructura del archivo XML hasta
encontrar las etiquetas que tenemos que tratar y asignarlaa a algunos mtodos de tipo
listeners ("escuchadores") disponibles como StartElementListener() de inicio de etiqueta o
EndElementListener() de finalizacin de etiqueta, incluyendo las sentencias oportunas dentro
de estos mtodos.
Por ejemplo, para obtener el elemento <item>, en primer lugar buscamos el elemento
raz del XML (<rss>) declarando un objeto RootElement. Despus, accedemos a su elemento
hijo <channel> y, finalmente, obtenemos de ste ltimo el elemento hijo <item>. En cada
"salto" hemos utilizado el mtodo getChild().
Una vez hemos llegado a la etiqueta buscada, asignamos los listeners necesarios. En
este caso, uno de apertura y otro de cierre de etiqueta item, donde inicializamos la noticia
actual y la aadimos a la lista final, respectivamente, de forma similar a como lo hemos hecho
para el modelo SAX clsico.

326

Introduccin al entorno Android

Para el resto de etiquetas internas de item, procedemos de la misma manera,


accediendo a ellas con getChild() y asignando los listeners necesarios.
Para acabar, arrancamos todo el proceso de anlisis del XML llamando al mtodo
parse(), definido en la clase android.Util.Xml, al que pasamos como parmetros el stream del
archivo XML, la codificacin del documento XML y un handler SAX obtenido directamente del
objeto RootElement definido anteriormente.
Este modelo SAX simplificado evita implementar el handler necesario en el modelo
SAX clsico. Adems, ayuda a evitar posibles errores de programacin disminuyendo la
complejidad del mismo
Hay que tener en cuenta que el modelo clsico es tan vlido y eficiente como ste, que
nicamente simplifica el trabajo al programador.

6.4.3

DOM en Android
Como hemos comentado, el modelo DOM debe leer el documento XML

completamente antes de poder realizar ninguna accin con su contenido. Es decir, cambia
radicalmente la manera de leer los archivos XML.
Al acabar la lectura del documento XML este modelo devuelve todo su contenido en
una estructura de tipo rbol, donde los distintos elementos del fichero XML se representan en
forma de nodos y su jerarqua padre-hijo se establece mediante relaciones entre dichos nodos.
Por ejemplo, si tenemos el siguiente documento XML:
<noticias>
<noticia>
<titulo>Ttulo 1</titulo>
<enlace>Enlace 1</link>
</noticia>
<noticia>
<titulo>Ttulo 2</titulo>
<enlace>Enlace 2</link>
</noticia>
<noticias>

El modelo DOM traduce este documento XML en el rbol siguiente:

327

Como vemos, este rbol conserva la misma informacin del fichero XML, pero en
forma de nodos y relaciones entre nodos. Por esta razn es sencillo buscar fcilmente dentro
de la estructura un elemento en concreto.
Este rbol se conserva en memoria una vez ledo el documento completo, lo que
permite procesarlo en cualquier orden y tantas veces como sea necesario, a diferencia del
modelo SAX, donde el tratamiento es secuencial y siempre desde el principio hasta el final del
documento. Es decir, no se puede volver atrs una vez finalizada la lectura del documento
XML.
El modelo DOM de Android ofrece una serie de clases y mtodos que permiten cargar
la informacin de la forma descrita y facilitan la bsqueda de elementos dentro de la estructura
creada.
Veamos cmo queda el analizador XML usando el modelo DOM de Android:

public class ParserDom

implements RSSParser {

// URL del archivo XML


private URL feedUrl;

// Constructor de la clase, se asigna la URL a la variable local


protected ParserDom(String feedUrl){
try
{
this.feedUrl= new URL(feedUrl);

328

Introduccin al entorno Android

}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
}

// Mtodo que lee el documento XML


public List<Noticia> analizar() {
// Creamos acceso a la API DOM de Android
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
List<Noticia> noticias = new ArrayList<Noticia>();
try {
// Creamos un analizador (parser) de DOM
DocumentBuilder parser = factory.newDocumentBuilder();
// Analizamos el archivo XML
Document dom = parser.parse(this.getInputStream());
// Obtenemos el elemento raz del parser
Element root = dom.getDocumentElement();
// Buscamos las etiquetas ITEM dentro del elemento raz
NodeList items = root.getElementsByTagName(EtiquetasRSS.ITEM);
// Recorremos todos los items y vamos cargando la lista de noticias
for (int i=0;i<items.getLength();i++){
// Creamos un nuevo objeto de Noticia
Noticia noticia = new Noticia();
// Leemos el item i
Node item = items.item(i);
// Obtenemos todas las etiquetas internas de item
// y buscamos los campos que nos interesan
NodeList etiquetas = item.getChildNodes();
for (int j=0;j<etiquetas.getLength();j++){
Node contenido = etiquetas.item(j);
String nombre = contenido.getNodeName();

329

if (nombre.equalsIgnoreCase(EtiquetasRSS.TITLE)){
noticia.setTitulo(contenido.getFirstChild().getNodeValue());
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.LINK)){
noticia.setEnlace(contenido.getFirstChild().getNodeValue());
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.DESCRIPTION)){
// Puede ocurrir que el texto est distribuido en varias
// lneas, por lo que hay que leer todos los nodos internos
StringBuilder text = new StringBuilder();
NodeList chars = contenido.getChildNodes();
for (int k=0;k<chars.getLength();k++){
text.append(chars.item(k).getNodeValue());
}
noticia.setDescripcion(text.toString());
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.PUB_DATE)){
noticia.setFecha(contenido.getFirstChild().getNodeValue());
}
} // end for j
// Aadimos la noticia al listado
noticias.add(noticia);
} // end for i
} catch (Exception e) {
throw new RuntimeException(e);
}
return noticias;
}

// Mtodo que abre una conexin a la URL y devuelve el


// puntero de tipo fichero al analizador correspondiente
private InputStream getInputStream()
{
try
{
return feedUrl.openConnection().getInputStream();
}
catch (IOException e)
{

330

Introduccin al entorno Android

throw new RuntimeException(e);


}
}
}

El mtodo ms importante es analizar(). De igual forma que se hace en el modelo


SAX,

el

primer

paso

es

instanciar

la

API

de

DOM

partir

de

la

clase

DocumentBuilderFactory. Posteriormente, creamos un nuevo parser a partir del mtodo


newDocumentBuilder().
Despus, nicamente hay que leer el documento XML invocando el mtodo parse()
del parser DOM, pasndole como parmetro el stream de entrada del fichero.
Al hacer esto, el documento XML se lee completamente y se crea la estructura de
rbol equivalente, que se devuelve como un objeto de tipo Document por el que podemos
movernos para buscar los elementos <item> que necesita la aplicacin.
Para esto, lo primero que hacemos es acceder al nodo raz (root) del rbol utilizando el
mtodo getDocumentElement(); en este ejemplo es la etiqueta <rss>,.
Una vez que sabemos dnde est el nodo raz, vamos a buscar todos los nodos con la
etiqueta <item>. Para esto, usamos el mtodo de bsqueda por el nombre de etiqueta
getElementsByTagName(nombre_de_etiqueta), que devuelve una lista de tipo NodeList
con todos los nodos hijos del nodo actual cuya etiqueta coincida con el nombre indicado.
Una vez hemos obtenido todos los elementos <item> que contienen cada noticia,
vamos a recorrerlos de uno en uno para crear todos los objetos de tipo Noticia necesarios.
Para cada uno de estos elementos obtenemos sus nodos hijos mediante el mtodo
getChildNodes().

Despus,

recorremos

estos

nodos

hijos

obteniendo

su

texto

almacenndolo en el campo correspondiente del objeto Noticia. Para saber qu etiqueta


representa cada nodo hijo utilizamos el mtodo getNodeName().

6.4.4

StAX en Android
Este modelo StAX de lectura de documentos XML es muy parecido a SAX. La

diferencia principal est en que, mientras que en el modelo SAX no hay control de ejecucin
una vez iniciada la lectura del XML (el parser lee automticamente todo el XML desde el inicio
hasta el final invocando los mtodos necesarios), en el modelo StAX podemos guiar la lectura
del documento o intervenir en ella, solicitando de forma explcita la lectura del siguiente
elemento del documento XML y respondiendo al resultado con las acciones oportunas.
En este ejemplo hemos usado la implementacin de StAX de Android que se lama
XmlPull. Fjate en el cdigo fuente de este analizador:

331

public class ParserXmlPull

implements RSSParser {

// URL del archivo XML


private URL feedUrl;

// Constructor de la clase, se asigna la URL a la variable local


protected ParserXmlPull(String feedUrl){
try
{
this.feedUrl= new URL(feedUrl);
}
catch (MalformedURLException e)
{
throw new RuntimeException(e);
}
}

// Mtodo que lee el documento XML


public List<Noticia> analizar() {
List<Noticia> noticias = null;
// Creamos un analizador (parser) de StAX que en Android se llama
XmlPullParser
XmlPullParser parser = Xml.newPullParser();

try {
// Asignamos el stream del XML al parsr
parser.setInput(this.getInputStream(), null);
// Guardamos el tipo de evento actual = START_DOCUMENT
int eventType = parser.getEventType();
Noticia noticiaActual = null;
// Variable que controla si se ha acabado el documento XML
boolean docAcabado = false;
// Mientras no acabe el documento

332

Introduccin al entorno Android

while (eventType != XmlPullParser.END_DOCUMENT && !docAcabado){


// Variable temporal que guarda el nombre de la etiqueta
String nombre = null;
switch (eventType){
case XmlPullParser.START_DOCUMENT:
// Creamos el listado con las noticias
noticias = new ArrayList<Noticia>();
break;
// Etiqueta de incicio
case XmlPullParser.START_TAG:
// Creamos el objeto noticia o guardamos el campo
correspondiente
nombre = parser.getName();
if (nombre.equalsIgnoreCase(EtiquetasRSS.ITEM)){
noticiaActual = new Noticia();
} else if (noticiaActual != null){
if (nombre.equalsIgnoreCase(EtiquetasRSS.LINK)){
noticiaActual.setEnlace(parser.nextText());
} else if
(nombre.equalsIgnoreCase(EtiquetasRSS.DESCRIPTION)){
noticiaActual.setDescripcion(parser.nextText());
} else if
(nombre.equalsIgnoreCase(EtiquetasRSS.PUB_DATE)){
noticiaActual.setFecha(parser.nextText());
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.TITLE)){
noticiaActual.setTitulo(parser.nextText());
}
}
break;
// Etiqueta de cierre
case XmlPullParser.END_TAG:
nombre = parser.getName();
if (nombre.equalsIgnoreCase(EtiquetasRSS.ITEM) &&
noticiaActual != null){
noticias.add(noticiaActual);
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.CHANNEL)){

333

docAcabado = true;
}
break;
}
eventType = parser.next();
} // end while
} catch (Exception e) {
throw new RuntimeException(e);
}
// Devolvemos las noticias
return noticias;
}

// Mtodo que abre una conexin a la URL y devuelve el


// puntero de tipo fichero al analizador correspondiente
private InputStream getInputStream()
{
try
{
return feedUrl.openConnection().getInputStream();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}

Una vez ms nos centramos en el mtodo analizar() de la clase.


Primero, creamos el nuevo analizador XmlPull y asignamos el fichero de entrada en
forma de stream. Despus, definimos un bucle en el que solicitamos en cada iteracin al
parser el siguiente evento encontrado en la lectura del archivo XML mediante el mtodo
parser.next(). Para cada evento devuelto obtenemos su tipo mediante el mtodo
parser.getEventType() y ejecutamos las sentencias oportunas.

334

Introduccin al entorno Android

Una vez identificado el tipo de evento, podemos consultar el nombre de la etiqueta del
elemento XML mediante parser.getName() y el texto contenido mediante parser.nextText().
Si ejecutas la aplicacin en el emulador de Android, vers que puedes acceder a los
distintos modelos de tratamiento de ficheros XML pulsando la tecla men

del emulador:

Si seleccionamos una de las opciones, podemos ver que se recarga el listado de


noticias y el tiempo que tarda en cargar el documento XML:

Se muestra un mensaje con el tiempo que ha tardado en cargar el documento XML.


As podemos valorar la eficacia de cada modelo a la hora de leer un XML completo.
Observars que los modelos SAX son los ms rpidos.

335

Si haces clic sobre una noticia vers que Android te permite seleccionar el navegador
que quieres usar para iniciar la accin Intent.ACTION_VIEW que permite abrir una pgina
Web:

Esto ocurre porque en el Ejemplo 2 de la Unidad 5 hemos definido un navegador


sencillo que carga la pgina en formato HTML para invocar implcitamente una Intencin
propia.

336

Introduccin al entorno Android

El trmino base de datos se refiere a una coleccin, conjunto o depsito de datos,


almacenados en un soporte magntico o de otro tipo, accesibles por mltiples
usuarios de forma rpida y eficaz mediante el ordenador a travs de una o de varias
aplicaciones informticas independientes de los datos.Para crear una Intencin hay
que usar el objeto Intent de Android.
En Android las bases de datos son privadas y nicamente una aplicacin puede
acceder a ellas para leer y escribir datos.
Para compartir informacin de base de datos entre aplicaciones Android hay que
usar los Content Providers.Explcita: invocando la clase Java del componente que
queremos ejecutar. Normalmente, se hace para invocar componentes de una
misma aplicacin.
Android usa SQLite como motor de base de datos relacional.
Antes de crear una base de datos con el ordenador, es preciso disearla
previamente sobre el papel.
Usar bases de datos Android hace ms lentas las aplicaciones debido a que es
necesario escribir y leer informacin de la memoria fsica del dispositivo. Por esto,
es recomendable usar hilos de ejecucin.
La forma usual en Android de crear, modificar y conectar con una base de datos
SQLite consiste en usar la clase Java SQLiteOpenHelper.
Existen dos formas de buscar y recuperar registros de una base de datos
SQLite. La primera de ellas consiste en utilizar directamente el comando de
consulta SQL rawQuery(). La segunda forma consiste en utilizar el mtodo
especfico query() con parmetros de consulta a la base de datos.
EXtensible Markup Language (XML) es un formato de datos que se usa
comnmente en las aplicaciones web modernas.
Los tres modelos ms extendidos para leer y escribir ficheros de tipo XML son
DOM (Document Object Model), SAX (Simple API for XML) y StAX (Streaming API
for XML).

337

El modelo DOM vuelca el documento XML en la memoria del dispositivo en


forma de estructura de rbol, de manera que se puede acceder aleatoriamente a
los elementos de las ramas.
El modelo SAX se basa en eventos. La aplicacin recorre todos los elementos del
archivo XML de una sola vez. La ventaja respecto a la anterior es que es ms rpido
y requiere menos memoria, si bien no permite el acceso aleatorio a una de sus
ramas.
El modelo StAX es una mezcla de las dos modelos anteriores. En este caso,
tambin se lee el fichero XML de forma secuencial, pero podemos controlar la
forma en que se leen los elementos. Este modelo es tambin mucho ms rpido
que DOM, pero algo ms lento que SAX.
Un analizador sintctico (en ingls parser) convierte el texto de entrada en otras
estructuras (comnmente rboles), que son ms tiles para el posterior anlisis;
tambin captura la jerarqua implcita de la entrada.

338

CONTENT PROVIDERS,
SERVICIOS Y
NOTIFICACIONES

NDICE
7.1

CONTENT PROVIDERS ............................................................. 341


7.1.1 Introduccin ................................................................... 341
7.1.2 Proveedores de contenido (Content Providers) ....... 341
7.1.3 Construccin de un Content Provider ........................ 342

7.2

Uso de un Content Provider nuevo ........................................ 352

7.3

Uso de un Content Provider ya existente en Android ......... 355

7.4 SERVICIOS DE ANDROID Y RECEPTORES DE MENSAJES DE


DIFUSIN .............................................................................................. 359
7.4.1 Servicios (Services) ...................................................... 359
7.4.2 Servicios propios ........................................................... 360
7.4.3 Receptor de mensajes de difucin (Broadcast Receiver)
361
7.4.4 Intencin pendiente (Pending Intent) ......................... 361
7.4.5 Ejemplo de Receptor de mensajes (Broadcast
Receiver) ................................................................................ 362
7.4.6 Ejemplo de envo y recepcin de mensajes internos en
una aplicacin y uso de servicios por defecto de Android364
7.4.7 Crear un servicio propio ............................................... 367
7.5

NOTIFICACIONES AL USUARIO EN ANDROID ..................... 373


7.5.1 Mensajes emergentes (Toast) .................................... 373
7.5.2 Notificaciones en la barra de estado.......................... 378

7.6

USO DE VIEWPAGER EN APLICACIONES ANDROID ......... 383


7.6.1 Cmo se usa el componente ViewPager .................. 385

Content Providers, servicios y notificaciones

7.1

CONTENT PROVIDERS

7.1.1 Introduccin
En esta Unidad vamos a estudiar los proveedores de contenidos (Content
Providers) para compartir informacin entre aplicaciones y el Resolvedor de contenidos
(Content Resolver) para consultar y actualizar la informacin de los Content Providers.
Despus, explicaremos cmo funcionan los servicios en Android.
A continuacin, detallaremos el uso de notificaciones en las aplicaciones Android.
Finalmente, veremos cmo utilizar el componente ViewPager que permite cambiar de
pantalla deslizando el dedo horizontalmente en el dispositivo.

7.1.2 Proveedores de contenido (Content Providers)


Un Proveedor de contenido (en ingls Content Provider) es el mecanismo
proporcionado por Android para compartir informacin entre aplicaciones.
Una aplicacin que desee compartir de manera controlada parte de la informacin que
almacena con resto de aplicaciones debe declarar un Content Provider al sistema operativo a
travs del cul se realiza el acceso a dicha informacin.
Este mecanismo lo utilizan muchas aplicaciones estndar de un dispositivo Android,
como la lista de contactos, la aplicacin de SMS para mensajes cortos, el calendario, etctera.
Es decir, podemos acceder desde una aplicacin cualquiera a los datos gestionados por otras
aplicaciones Android haciendo uso de los Content Providers correspondientes. Para ello, es
preciso que la aplicacin tenga asignados los permisos adecuados para acceder a estos
contenidos.
Android, de serie, incluye varios proveedores de contenido para los tipos de datos ms
comunes, como audio, vdeo, imgenes, agenda de contactos personal, etctera. Puedes ver el
listado completo en el paquete android.provider.
En este apartado vamos a tratar dos funcionalidades diferenciadas, que son las
siguientes:

Construccin de nuevos Content Providers personalizados, para que otras


aplicaciones puedan acceder a la informacin contenida en la nuestra.

Utilizacin de un Content Provider ya existente, para que la nuestra pueda


acceder a los datos publicados por otras aplicaciones.

En la Unidad 5 ya hemos visto un ejemplo muy sencillo sobre del acceso a un Content
Provider ya existente, concretamente en la lista de contactos de Android.

341

Dado que es importante conocer el funcionamiento interno de un Content Provider,


antes de pasar a utilizarlo en nuestras aplicaciones, vamos a estudiar cmo se construye.

7.1.3 Construccin de un Content Provider


Hay dos formas de compartir informacin de una aplicacin: implementando el
proveedor de contenidos propio mediante la clase ContentProvider de Android o agregando
datos a un proveedor ya existente en el dispositivo, siempre y cuando los tipos de datos sean
parecidos y la aplicacin tenga permiso para escribir en el Content Provider.
En el Ejemplo 1 de esta Unidad vamos a mostrar cmo crear un Content Provider
nuevo y cmo usarlo.
Por simplificacin, ser la misma aplicacin la que acceda al Content Provider interno,
si bien el cdigo necesario desde otra aplicacin es exactamente el mismo.
Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Content Provider) de la Unidad 7.
Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa, en el
que hemos utilizado un Content Provider.

Si ejecutas la aplicacin, vers que tiene el siguiente aspecto:

Fjate que en este ejemplo los botones "Insertar" y "Eliminar" son excluyentes. Slo se
puede borrar un alumno si previamente ha sido dado de alta y viceversa.
Se trata de un Content Provider que comparte informacin de los alumnos de un
colegio.
La aplicacin del colegio almacena la informacin que queremos compartir en una base
de datos SQLite.
342

Content Providers, servicios y notificaciones

Si bien internamente podemos tener la informacin almacenada de cualquier otra


forma, por ejemplo, en un ficheros de tipo texto, en XML, etctera, en este ejemplo vamos a
usar una base de datos porque es ms fcil gestionar informacin estructurada.
El Content Provider es el mecanismo que permite compartir estos datos con otras
aplicaciones de una forma homognea usando una interfaz estandarizada.
Las tablas de la base de datos SQLite usadas por un Content Provider deben incluir siempre el
campo _ID que identifica sus registros de forma unvoca.

En este ejemplo, los registros devueltos por el Content Provider de alumnos tiene este
aspecto:

Lo primero que hemos hecho en este Ejemplo es crear una aplicacin muy simple que
almacena y consulta los datos de los alumnos con la estructura similar a la tabla anterior.
Para esto, aplicamos los mismos conceptos que ya hemos estudiado en la Unidad 6
para el tratamiento de bases de datos.
Creamos una clase heredada de SQLiteOpenHelper donde definimos las sentencias
SQL que crean la tabla de alumnos implementando los mtodos onCreate() y onUpgrade(). El
cdigo de esta nueva clase tiene este aspecto:

public class ColegioSqliteHelper extends SQLiteOpenHelper {

//Sentencia SQL para crear la tabla de Alumnos en la BD BDColegio


String sqlCreate = "CREATE TABLE Alumnos " +
"(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" nombre TEXT, " +
" apellidos TEXT, " +
" curso TEXT )";

343

public ColegioSqliteHelper(Context contexto, String nombre,


CursorFactory factory, int version) {
super(contexto, nombre, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
//Se ejecuta la sentencia SQL de creacin de la tabla
db.execSQL(sqlCreate);

String[] nombres={"Juan", "Jos", "Miguel", "Antonio", "Alicia",


"Luis", "Fernanda", "Luca", "Mercedes", "Elisa"};
String[] apellidos={"Valle", "Fernndez", "Martn", "Navas", "Conde",
"Daz", "Verd", "Cuenca", "Prez", "Sevilla"};
String[] cursos={"1 ESO", "1 ESO", "2 ESO", "3 ESO", "1 ESO",
"4 ESO", "2 ESO", "2 ESO", "1 ESO", "4 ESO"};

//Insertamos 10 alumnos de ejemplo


for(int i=0; i<10; i++)
{
//Insertamos los datos en la tabla Alumnos
db.execSQL("INSERT INTO Alumnos (nombre, apellidos, curso) " +
"VALUES ('" + nombres[i] + "', '" + apellidos[i] +"', '"
+ cursos[i] + "')");
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int versionAnterior, int
versionNueva) {
// NOTA: Por simplicidad, se elimina la tabla anterior y se crea de nuevo.
//

Sin embargo, lo normal sera migrar datos de la tabla antigua

//

a la nueva, por lo que este mtodo debera ser ms complejo.

//Se elimina la versin anterior de la tabla


db.execSQL("DROP TABLE IF EXISTS Alumnos");

344

Content Providers, servicios y notificaciones

//Se crea la nueva versin de la tabla


db.execSQL(sqlCreate);
}
}

Fjate que hemos incluido el campo _id en la tabla de la base de datos de alumnos.
Este campo lo declaramos como INTEGER PRIMARY KEY AUTOINCREMENT para que se
incremente automticamente cada vez que insertamos un nuevo registro en la tabla.
Adems, esta clase aade algunos registros de ejemplo para poder hacer pruebas.
Una vez que ya contamos con una aplicacin que ha definido su base de datos, vamos
a construir el nuevo Content Provider que permite compartir sus datos con otras aplicaciones.
El acceso a un Content Provider se realiza siempre mediante un identificador URI. Un
identificador URI es una cadena de texto parecida a una direccin Web de Internet. Es decir, si
para acceder a Google con el navegador escribimos http://www.google.es, para acceder a un
Content

Provider

utilizamos

una

direccin

similar

content://es.mentor.unidad7.ejemplo/alumnos.
Los identificadores URI de los Content Providers se pueden dividir en tres partes:

Prefijo content://: indica que dicho recurso debe ser tratado por un Content
Provider.

Identificador del Content Provider (tambin llamado authority): este campo


debe ser nico en cada dispositivo Android; por esto, es una buena prctica
definir un authority con el nombre de clase java invertido, por ejemplo, en este
ejemplo es es.mentor.ejemplo7.ejemplo.

Esquema o Entidad concreta de datos que queremos que comparta el


Content Provider. En este caso indicamos simplemente la tabla de alumnos.
Un Content Provider puede contener datos de varias entidades distintas en
esta ltima parte del URI. Todo esto es importante, ya que ser nuestro
Content Provider el encargado de interpretar (parsear) el URI completo para
determinar los datos que se le estn solicitando. Esto lo veremos un poco ms
adelante en detalle.

Por ltimo, apuntamos que, en el URI se puede hacer referencia directamente a un


registro concreto de la entidad seleccionada. Esto se hace indicando al final del URI de dicho
registro. Por ejemplo, el URI content://es.mentor.unidad7.ejemplo/alumnos/17 hace referencia
directa al alumno con _ID = 17.
A continuacin, vamos a crear el Content Provider de la aplicacin. Para esto, hay que
extender la clase ContentProvider. Esta clase dispone de los mtodos abstractos siguientes ,
que podemos implementar:
345

onCreate(): se usa para inicializar todos los recursos necesarios para el


funcionamiento del nuevo Content Provider.

query(): permite consultar datos que haya en el Content Provider.

insert(): permite insertar datos en el Content Provider.

update(): permite actualizar datos del Content Provider.

delete(): permite borrar datos del Content Provider.

getType(): permite conocer el tipo de dato devuelto por el Content Provider.

Adems de implementar estos mtodos, tambin definimos una serie de cadenas


constantes en la clase del Content Provider.
A continuacin, estudiamos por partes la nueva clase ColegioContentProvider que
extienda de ContentProvider.
En primer lugar definimos el URI con el que se accede al Content Provider de la
aplicacin: Vamos a usar content://es.mentor.unidad7.ejemplo/alumnos:

//Definicin del CONTENT_URI


private static final String uri =
"content://es.mentor.unidad7.ejemplo/alumnos";
public static final Uri CONTENT_URI = Uri.parse(uri);

En todos los Content Providers de Android es necesario encapsular este identificador


URI en un objeto esttico del tipo Uri que hemos llamado CONTENT_URI.
A continuacin, definimos varias constantes con los nombres de los campos
proporcionados por el Content Provider. Como ya hemos comentado anteriormente, existen
columnas predefinidas que deben tener todos los Content Providers, como la columna _ID.
Esta columna estndar est definida internamente en la clase BaseColumns, por lo que al
aadir los campos (columnas) del Content Provider slo hay que indicar las nuevas columnas.

//Clase interna para declarar las constantes de las columnas = campos


public static final class Alumnos implements BaseColumns
{
private Alumnos() {}
//Nombres de las columnas
public static final String COL_NOMBRE = "nombre";
public static final String COL_APELLIDOS = "apellidos";
public static final String COL_CURSO = "curso";

346

Content Providers, servicios y notificaciones

Por ltimo, vamos a definir varias cadenas constantes privadas que almacenen
informacin auxiliar con el nombre de la base de datos, su versin y la tabla a la que accede el
Content Provider.

private ColegioSqliteHelper colegioBDhelper;


private static final String BD_NOMBRE = "BDColegio";
private static final int BD_VERSION = 1;
private static final String TABLA_ALUMNOS = "Alumnos";

Lo primero que debe hacer un Content Provider cuando otra aplicacin le solicita una
operacin es interpretar el URI utilizado. Para facilitar esta tarea al programador, Android
proporciona la clase llamada UriMatcher que interpreta los patrones en un URI.
Esto es muy til para determinar, por ejemplo, si un URI hace referencia a una tabla
genrica o a un registro concreto a travs de su ID:

content://es.mentor.unidad7.ejemplo/alumnos: acceso genrico a la tabla


de alumnos.

content://es.mentor.unidad7.ejemplo/alumnos/17: acceso directo al alumno


con el ID = 17.

Para ello definimos tambin en esta clase un objeto UriMatcher y dos nuevas
constantes que representan los dos tipos de URI que hemos indicado: acceso genrico a la
tabla (ALUMNOS) o acceso a un alumno por ID (ALUMNOS_ID).
Despus, creamos el objeto UriMatcher indicando el formato de ambos tipos de URI
de forma que pueda diferenciarlos y devolvernos su tipo (una de las dos constantes definidas,
ALUMNOS o ALUMNOS_ID):
//Necesario para UriMatcher
private static final int ALUMNOS = 1;
private static final int ALUMNOS_ID = 2;
private static final UriMatcher uriMatcher;

static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("es.mentor.unidad7.ejemplo", "alumnos", ALUMNOS);
uriMatcher.addURI("es.mentor.unidad7.ejemplo", "alumnos/#", ALUMNOS_ID);
}

347

En el cdigo anterior vemos que mediante el mtodo addUri() indicamos el campo


authority del URI, el formato de la entidad que estamos solicitando y el tipo que identifica el
formato del dato. Ms adelante veremos cmo utilizar esto de forma prctica.
Posteriormente, vamos a implementar los mtodos internos del Content Provider.
El primero de ellos es onCreate(). En este mtodo inicializamos la base de datos
mediante la clase ColegioSqliteHelper que creamos anteriormente:

public boolean onCreate() {


// Inicializamos el conector con la BD
colegioBDhelper = new ColegioSqliteHelper(
getContext(), BD_NOMBRE, null, BD_VERSION);
return true;
}

El mtodo ms importante del Content Provider es query(). Este mtodo recibe como
parmetros un URI, una lista de nombres de columna, un criterio de seleccin, una lista de
valores para las variables utilizadas en el criterio anterior y un criterio de ordenacin.
Todos estos parmetros son similares a los que estudiamos cuando tratamos sobre las
bases de datos SQLite para Android.
El mtodo query() devuelve los datos solicitados segn el URI, los criterios de
seleccin y ordenacin indicados como parmetros. As, si el URI hace referencia a un alumno
en concreto por su ID, se debe ser el nico registro devuelto. Si se solicita el contenido de la
tabla de alumnos, hay que realizar la consulta SQL correspondiente a la base de datos
respetando los criterios pasados como parmetros.
Para distinguir entre los dos tipos posibles de URI utilizamos el mtodo match() del
objeto uriMatcher. Si el tipo devuelto es ALUMNOS_ID, es decir, se ha solicitado informacin
de un alumno en concreto, sustituimos el criterio de seleccin por uno que busca en la tabla de
alumnos slo el registro con el ID indicado en la URI. Para obtener este ID utilizamos el mtodo
getLastPathSegment() del objeto uri, que extrae el ltimo elemento de la URI, en este caso el
ID del alumno.
Despus, hay que realizar la consulta a la base de datos mediante el mtodo query()
de SQLiteDatabase. Esto es muy fcil, ya que los parmetros son similares a los empleados
en el mtodo query() del Content Provider:
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
// Accedemos a la base de datos en modo lectura
SQLiteDatabase db = colegioBDhelper.getReadableDatabase();

348

Content Providers, servicios y notificaciones

//Si es una consulta a un ID concreto construimos el WHERE


String where = selection;
if(uriMatcher.match(uri) == ALUMNOS_ID){
// Obtenemos el ltimo segmento del URI
where = "_id=" + uri.getLastPathSegment();
}
// Hacemos la consulta a la BD
Cursor c = db.query(TABLA_ALUMNOS, projection, where,
selectionArgs, null, null, sortOrder);
return c;
}

Podemos observar que los resultados se devuelven en forma de Cursor, tal y como lo
hace el mtodo query() de SQLiteDatabase.
Por otra parte, los mtodos update() y delete() son completamente similares al mtodo
anterior. nicamente se diferencian en que stos devuelven como resultado el nmero de
registros afectados en lugar de un cursor. Veamos su cdigo:

@Override
public int update(Uri uri, ContentValues values,
String selection, String[] selectionArgs) {
// Variable temporal
int cont;
// Accedemos a la base de datos en modo escritura
SQLiteDatabase db = colegioBDhelper.getWritableDatabase();
//Si es una actualizacin a un ID concreto construimos el WHERE
String where = selection;
if(uriMatcher.match(uri) == ALUMNOS_ID){
where = "_id=" + uri.getLastPathSegment();
}
// Actualizamos la tabla
cont = db.update(TABLA_ALUMNOS, values, where, selectionArgs);
// Devolvemos el n de registros afectados por la consulta
return cont;
}

349

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Variable temporal
int cont;
// Accedemos a la base de datos en modo escritura
SQLiteDatabase db = colegioBDhelper.getWritableDatabase();
//Si borramos un ID concreto construimos el WHERE
String where = selection;
if(uriMatcher.match(uri) == ALUMNOS_ID){
where = "_id=" + uri.getLastPathSegment();
}
// Borramos los registros
cont = db.delete(TABLA_ALUMNOS, where, selectionArgs);
// Devolvemos el n de registros afectados por la consulta
return cont;
}

El mtodo insert() se implementa de forma ligeramente distinta. La diferencia en este


caso est en que hay que devolver el URI que hace referencia al nuevo registro insertado. Para
ello, obtenemos el nuevo ID del elemento insertado y construimos el nuevo URI de respuesta
mediante el mtodo auxiliar ContentUris.withAppendedId(), que recibe como parmetro el
URI del Content Provider y el ID del nuevo elemento:

public Uri insert(Uri uri, ContentValues values) {


// Variable temporal que guarda el ID dado de alta
long regId = -1;
// Accedemos a la base de datos en modo escritura
SQLiteDatabase db = colegioBDhelper.getWritableDatabase();
// Insertamos el registro en la tabla
regId = db.insert(TABLA_ALUMNOS, null, values);
// Uri con el resultado de la operacin
Uri newUri = ContentUris.withAppendedId(CONTENT_URI, regId);
return newUri;
}

350

Content Providers, servicios y notificaciones

Por ltimo, slo queda implementar el mtodo getType(). Este mtodo se utiliza para
identificar el tipo de datos que devuelve el Content Provider. Este tipo de datos se expresa
como un MIME Type, tal y como hacen los navegadores Web para determinar qu tipo de
datos se est recibiendo al hacer una peticin a un servidor. Identificar el tipo de datos que
devuelve un Content Provider ayuda a Android a determinar qu aplicaciones son capaces de
procesar dichos datos.
En este ejemplo, existen dos tipos MIME distintos para cada entidad del Content
Provider: el primero se usa cuando se devuelve un registro nico concreto y el segundo cuando
se devuelven varios registros simultneamente. As, podemos utilizar los siguientes patrones
para definir uno u otro tipo de datos:

vnd.android.cursor.item/vnd.xxxxxx: Registro nico

vnd.android.cursor.dir/vnd.xxxxxx: Listado de registros

En este ejemplo, hemos definido los siguientes tipos:

vnd.android.cursor.dir/vnd.mentor.alumno

vnd.android.cursor.item/vnd.mentor.alumno

Teniendo esto en cuenta, la implementacin del mtodo getType() tiene estas


sentencias:

@Override
public String getType(Uri uri) {
// Devolvemos un tipo de dato en funcin del URI
int match = uriMatcher.match(uri);
switch (match)
{
case ALUMNOS:
return "vnd.android.cursor.dir/vnd.mentor.alumno";
case ALUMNOS_ID:
return "vnd.android.cursor.item/vnd.mentor.alumno";
default:
return null;

Se puede observar que utilizamos de nuevo el objeto UriMatcher para determinar el


tipo de URI que se est solicitando y en funcin de ste devolvemos un tipo MIME u otro.
Para

finalizar

con

el

Content

Provider,

debemos

declararlo

en

el

fichero

AndroidManifest.xml, para que, al instalar la aplicacin en el dispositivo Android, ste


conozca la existencia de dicho recurso. Para ello, basta con aadir un nuevo elemento
351

<provider> dentro de <application> indicando el nombre del Content Provider y su


authority:

<application android:icon="@drawable/icon"
android:label="@string/app_name">

...

<provider android:name=".ColegioContentProvider"
android:authorities="es.mentor.unidad7.ejemplo"/>

</application>

7.2

Uso de un Content Provider nuevo


Una vez completado el Content Provider, vamos a usarlo desde la propia aplicacin

del ejemplo que hemos creado. Lo hacemos as para simplificar el ejemplo; de cualquier forma,
el cdigo necesario es exactamente el mismo si lo usamos desde otra aplicacin distinta.
Utilizar un Content Provider ya existente es muy sencillo, sobre todo si lo comparamos
con todo el proceso anterior de construccin de uno nuevo.
Para ello, vamos a usar la clase ContentResolver de Android que permite realizar
acciones (consultas de datos, actualizaciones de informacin, etctera) con cualquier Content
Provider que est disponible en el sistema operativo Android.
Desde la actividad principal hay que utilizar el mtodo getContentResolver() para
obtener la referencia de la aplicacin al objeto ContentResolver.
Una vez obtenida esta referencia, podemos utilizar sus mtodos query(), update(),
insert() y delete() para realizar las acciones equivalentes sobre el Content Provider.
En la aplicacin del ejemplo anterior hay tres botones en la pantalla principal: uno para
hacer una consulta de todos los alumnos, otro para insertar registros nuevos y el ltimo para
eliminar todos los registros nuevos insertados con el segundo botn.
Empecemos por la consulta de alumnos. El procedimiento es prcticamente igual al
que hemos estudiado para acceder a bases de datos SQLite.
Primero definimos una matriz con los nombres de las columnas de la tabla que
queremos recuperar en el resultado de la consulta: ID, nombre, apellidos y curso.

352

Content Providers, servicios y notificaciones

Tras esto, obtenemos una referencia al Content Resolver y utilizamos su mtodo


query() para obtener los resultados en forma de Cursor. El mtodo query() se invoca con los
parmetros siguientes: el Uri del Content Provider al que queremos acceder, la matriz de
columnas que queremos recuperar, el criterio de seleccin, los argumentos variables y el
criterio de ordenacin de los resultados.
En este caso, para no complicar el ejemplo tan slo indicamos los dos primeros:
CONTENT_URI del Content Provider y la matriz de columnas que acabamos de definir:
//Columnas de la tabla
String[] columnas = new String[] {
Alumnos._ID,
Alumnos.COL_NOMBRE,
Alumnos.COL_APELLIDOS,
Alumnos.COL_CURSO };

// Definimos la Uri que queremos usar


Uri alumnosUri =

ColegioContentProvider.CONTENT_URI;

// Acceso al contentresolver de la aplicacin


ContentResolver cr = getContentResolver();

//Hacemos la consulta
Cursor cur = cr.query(alumnosUri,
columnas, //Columnas solicitadas
null,

//Condicin de la query

null,
null);

//Argumentos variables de la query


//Orden de los resultados

Una vez solicitada la consulta, hay que recorrer el cursor para procesar los registros.
Veamos cmo queda el cdigo fuente:
// Si obtenemos resultados
if (cur.moveToFirst())
{
String nombre;
String apellidos;
String curso;

353

int colNombre = cur.getColumnIndex(Alumnos.COL_NOMBRE);


int colApellidos = cur.getColumnIndex(Alumnos.COL_APELLIDOS);
int colCurso = cur.getColumnIndex(Alumnos.COL_CURSO);
txtResultados.setText("Resultado consulta:\n\n");
// Recorremos todos los registros y los mostramos en pantalla

do {
nombre = cur.getString(colNombre);
apellidos = cur.getString(colApellidos);
curso = cur.getString(colCurso);
txtResultados.append(nombre + " " + apellidos + ". Curso: " +
curso + "\n");
} while (cur.moveToNext()); // end while
}

Insertar nuevos registros se implementa exactamente igual que si tratramos


directamente con bases de datos SQLite. Rellenamos primero un objeto ContentValues con
los datos del nuevo alumno y utilizamos el mtodo insert() pasndole como parmetros la
URI del Content Provider y los datos del nuevo registro:
ContentValues values = new ContentValues();
values.put(Alumnos.COL_NOMBRE, "Jess");
values.put(Alumnos.COL_APELLIDOS, "Sanz");
values.put(Alumnos.COL_CURSO, "BACHIDERATO");
ContentResolver cr = getContentResolver();
cr.insert(ColegioContentProvider.CONTENT_URI, values);
txtResultados.setText("Se ha insertado el alumno. Pulsa el botn 'Consultar'
para ver todos los alumnos.");

Por ltimo, la eliminacin de registros la hacemos directamente utilizando el mtodo


delete() del Content Resolver, indicando como segundo parmetro el criterio de identificacin
de los registros que queremos eliminar:
ContentResolver cr = getContentResolver();
cr.delete(ColegioContentProvider.CONTENT_URI, Alumnos.COL_NOMBRE + " =
'Jess'", null);
txtResultados.setText("Se ha borrado el alumno. Pulsa el botn 'Consultar'
para ver todos los alumnos.");

354

Content Providers, servicios y notificaciones

7.3

Uso de un Content Provider ya existente en Android

Hemos visto lo sencillo que resulta acceder a los datos proporcionados por un Content
Provider.
Mediante este mecanismo podemos utilizar en nuestras aplicaciones muchos datos de la
propia plataforma Android. En la documentacin oficial del paquete android.provider podemos
consultar los datos que estn disponibles a travs de este mecanismo. Entre ellos encontramos
el historial de llamadas, la agenda de contactos, etctera.
Para ver cmo se usan los Content Providers con un tipo de datos definido por Android, en
el Ejemplo 2 de esta Unidad vamos a consultar el historial de llamadas del dispositivo, usando
el Content Provider android.provider.CallLog.
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Historial de llamadas) de la Unidad 7.
Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa, en el
que hemos utilizado un Content Provider definido por Android.
Para poder ver algn dato en este ejemplo, en primer lugar, vamos a registrar varias
llamadas en el emulador de Android. As, los resultados de la consulta al historial de llamadas
devolvern algunos registros.
A continuacin, vamos a simular varias llamadas salientes desde el emulador y varias
llamadas entrantes desde el DDMS.
Las llamadas salientes son sencillas de realizar usando el emulador como si se tratara de
un telfono normal y corriente. Accedemos al icono telfono, marcamos un nmero y
descolgamos como si se tratara de un dispositivo fsico:

355

Para simular llamadas entrantes debemos acceder desde Eclipse a la vista del DDMS. En
esta vista, en la pestaa Emulator Control aparece el apartado Telephony Actions, donde
podemos introducir un nmero cualquiera de telfono origen Incoming number y pulsar el
botn Call para que el dispositivo del emulador reciba una llamada entrante.
Sin aceptar la llamada en el emulador, pulsaremos Hang Up para terminar la llamada
simulando as una llamada perdida.

Una vez hemos simulado tanto llamadas entrantes como llamadas salientes, vamos a
desarrollar una aplicacin que consulte el historial de llamadas.
Si consultamos la documentacin del Content Provider android.provider.CallLog, veremos
que podemos extraer diferentes datos relacionados con la lista de llamadas. En este ejemplo
vamos a usar nicamente el nmero origen o destino de la llamada y el tipo de llamada
(entrante, saliente y perdida). Los nombres de estas columnas se almacenan en las
constantes Calls.NUMBER y Calls.TYPE respectivamente.
A continuacin, definimos una matriz con las columnas que vamos a recuperar,
obtenemos la referencia al Content Resolver de la aplicacin y ejecutamos la consulta
llamando al mtodo query(). Por ltimo, recorremos el cursor obtenido y procesamos los
resultados. Veamos el cdigo fuente:

356

Content Providers, servicios y notificaciones

// Constantes que definen los campos que consultamos


String[] columnas = new String[] {Calls.TYPE, Calls.NUMBER };
// La Uri est predefinida en una constante del sistema
Uri llamadasUri =

Calls.CONTENT_URI;

// Cargamos el Content Resolver de la aplicacin


ContentResolver cr = getContentResolver();
// Hacemos una consulta de las llamadas
Cursor cur = cr.query(llamadasUri,
columnas, //Columnas a devolver
null,

//Condicin de la query

null,

//Argumentos variables de la query

null);

//Orden de los resultados

// Si hay llamadas mostramos la informacin


if (cur.moveToFirst())
{
int tipo;
String tipoLlamada = "";
String telefono;
// Obtenemos el ndice de las columnas
int colTipo = cur.getColumnIndex(Calls.TYPE);
int colTelefono = cur.getColumnIndex(Calls.NUMBER);
// Limpiamos la etiqueta de resultados
txtResultados.setText("");
// Mientras haya datos mostramos la informacin al usuario
do
{
// Obtenemos la informacin de las columnas
tipo = cur.getInt(colTipo);
telefono = cur.getString(colTelefono);
// Segn el tipo de llamada usamos un texto distinto
if(tipo == Calls.INCOMING_TYPE)
tipoLlamada = "ENTRADA";
else if(tipo == Calls.OUTGOING_TYPE)

357

tipoLlamada = "SALIDA";
else if(tipo == Calls.MISSED_TYPE)
tipoLlamada = "PERDIDA";
// Mostramos la informacin
txtResultados.append(tipoLlamada + " : " + telefono + "\n");
} while (cur.moveToNext()); // end while
} else txtResultados.setText("No hay ninguna llamada en el histrico del
telfono. Para que funcione bien esta aplicacin debes simular alguna
llamada entrante o saliente. En la teora del curso de esta Unidad se
muestra cmo hacerlo.");

Adems, en el cdigo fuente anterior decodificamos el valor del tipo de llamada


comparando el resultado con las constantes Calls.INCOMING_TYPE (llamada entrante),
Calls.OUTGOING_TYPE (llamada saliente) y Calls.MISSED_TYPE (llamada perdida).
Para que la aplicacin pueda acceder al historial de llamadas del dispositivo hay que
incluir en el fichero AndroidManifest.xml el permiso READ_CONTACTS:
<uses-permission android:name="android.permission.READ_CONTACTS">
</uses-permission>

Si ejecutas el ejemplo 2, vers que tiene el siguiente aspecto:

358

Content Providers, servicios y notificaciones

7.4

SERVICIOS DE ANDROID Y RECEPTORES DE MENSAJES DE


DIFUSIN

7.4.1 Servicios (Services)


Un Servicio (en ingls service) es un componente de una aplicacin Android que se
ejecuta en segundo plano, sin interactuar con el usuario (no tiene interfaz de usuario) y realiza
operaciones de larga duracin.
La plataforma Android ofrece una gran cantidad de servicios predefinidos en el sistema
a los que podemos acceder a travs de las clases de tipo Manager. En una

Actividad

podemos acceder a estos servicios a travs del mtodo getSystemService().


Cuando una aplicacin Android define sus propios Servicios, deben ser declarados en
el fichero AndroidManifest.xml del proyecto.
Un componente de una aplicacin Android puede iniciar un servicio que seguir
funcionando en segundo plano, incluso si el usuario cambiara a otra aplicacin.
Adems, un componente de la aplicacin puede unirse (en ingls bind) al servicio para
interactuar con l e incluso realizar comunicaciones entre procesos. Por ejemplo, un servicio
podra conectarse a Internet en un segundo plano para descargar noticias, reproducir msica,
etctera,.
Un servicio puede funcionar de dos modos:

Autnomo: cuando un componente de la aplicacin, por ejemplo, una


actividad, inicia el servicio mediante el mtodo StartService(). Una vez
arrancado, el servicio puede ejecutarse en segundo plano de forma indefinida,
incluso si el componente que lo inici se destruye. Normalmente, un servicio
iniciado de esta forma realiza una nica operacin y no devuelve el resultado al
componente que lo inicia. Por ejemplo, puede descargar de Internet un archivo
o cargarlo. Cuando la operacin finaliza, el servicio debe detenerse.

Dependiente o Ligado (en ingls a este modo se le denomina "bind"): cuando


un componente de la aplicacin se une al servicio mediante el mtodo
bindService(). Un servicio ligado ofrece una interfaz de tipo cliente-servidor
que permite a los componentes de una aplicacin interactuar con l enviando
peticiones y recibiendo su resultado. Un servicio ligado slo se ejecuta
mientras otro componente de la aplicacin est unido a l. Es posible unir un
mismo servicio a varios componentes de una o de varias aplicaciones al mismo
tiempo; sin embargo, cuando todos ellos se desligan, el servicio se destruye.

Un servicio puede funcionar de las dos formas anteriores simultneamente, es decir, se


puede arrancar en modo Autnomo (de manera indefinida) y tambin en modo Ligado.
359

Simplemente hay que implementar los mtodos onStartCommand() para el modo Autnomo y
onBind() para el modo Ligado.
Cualquier componente de una aplicacin puede iniciar un servicio. Incluso un
componente de otra aplicacin distinta a la que define el servicio tambin puede iniciarlo de la
misma forma que iniciaramos una Actividad de otra aplicacin mediante Intenciones.
Tambin se puede declarar un servicio como privado en la aplicacin, en el archivo
AndroidManifest.xml, y bloquear el acceso desde otras aplicaciones.
Los servicios tienen que ser declarados en el archivo AndroidManifest.xml con la
etiqueta <service android:name="nombreClase"> </service> y la implementacin de la
clase debe heredarse de la clase Service.
IMPORTANTE: los servicios propios de una aplicacin se ejecutan en el hilo principal de su
proceso; por lo tanto, para no bloquear el hilo principal o de la interfaz debemos, ejecutar estos
servicios con hilos de ejecucin, tal y como hemos visto en la Unidad 3.
7.4.2 Servicios propios
Una aplicacin puede declarar su propio servicio para llevar a cabo operaciones que
tarden en ejecutarse y no necesiten interactuar con el usuario o para suministrar una nueva
funcionalidad a otras aplicaciones.
A continuacin, se muestra un esquema con los mtodos que invoca Android cuando
lanzamos un servicio segn su modo de funcionamiento:

360

Content Providers, servicios y notificaciones

Una Actividad puede iniciar un servicio en modo Autnomo a travs del mtodo
StartService() y detenerlo mediante el mtodo StopService(). Cuando lo hacemos, Android
invoca su mtodo onCreate(); despus, se invoca el mtodo onStartCommand() con los datos
proporcionados por la Intencin de la actividad.
En el mtodo startService() tambin podemos indicar como parmetro el
comportamiento del ciclo de vida de los servicios:

START_STICKY: se utiliza para indicar que el servicio debe ser explcitamente


iniciado o parado.

START_NOT_STICKY: el servicio termina automticamente cuando el mtodo


onStartCommand() finaliza su ejecucin.

Si la actividad quiere interactuar con un servicio (modo Dependiente o Ligado) para, por
ejemplo, mostrar el progreso de una operacin, puede utilizar el mtodo bindService(). Para
esto, hay que usar el objeto ServiceConnection, que permite conectarse al servicio y devuelve
un objeto de tipo IBinder, que la actividad puede utilizar para comunicar con el servicio. Ms
adelante veremos en detalle cmo definir servicios en modo Ligado dentro de las aplicaciones
Android.

7.4.3 Receptor de mensajes de difucin (Broadcast Receiver)


Hay casos en los que se usan mensajes de difusin (Broadcast) para comunicar
eventos entre servicios. Estos mensajes son, en realidad, Intents.
En este caso usamos la clase Receptor de mensajes de difusin (BroadcastReceiver),
que debemos declarar en el archivo AndroidManifest.xml. Esta clase puede recibir
Intenciones (Intents), es decir, mensajes enviados por otro componente de Android mediante
el mtodo sendBroadcast() de la clase Context (contexto de la aplicacin).
La clase BroadCastReceiver define el nico mtodo OnReceive() donde se recibe el
mensaje de difusin; por lo tanto, fuera de este mtodo no se puede realizar ninguna operacin
asncrona porque el mensaje de difusin ya no est activo.

7.4.4 Intencin pendiente (Pending Intent)


En este apartado tambin hacemos uso de las Intenciones pendientes (Pending
Intents). Una Intencin pendiente es un tipo de Intent (mensaje entre componentes de
Android) que permite que otra aplicacin ejecute un bloque de cdigo predefinido con los
permisos de ejecucin de la aplicacin que inicia esta Intencin pendiente.
Este tipo de Intenciones se usa mucho para iniciar aplicaciones como el Administrador
de notificaciones (Notification Manager) y Administrador de alarmas (Alarm Manager).
361

Para enviar un mensaje de difusin mediante una Intencin pendiente hay que usar su
mtodo getBroadcast(). Para iniciar una subactividad mediante una Intencin pendiente hay
que usar su mtodo getActivity().

7.4.5 Ejemplo de Receptor de mensajes (Broadcast Receiver)


A continuacin, vamos a definir un receptor de mensajes de difusin (Broadcast
Receiver) que escucha los mensajes que lanza Android al resto de componentes del sistema
operativo cuando ocurre un cambio en el estado del telfono. Si el dispositivo recibe una
llamada de telfono, entonces nuestro receptor de mensajes recibir una notificacin y
registrar la llamada.
Para que la aplicacin funcione bien, debemos incluir las siguientes sentencias en el
archivo AndroidManifest.xml del proyecto:

<application android:icon="@drawable/icon"
android:label="@string/app_name">

<receiver android:name="ReceptorLlamadas">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE">
</action>
</intent-filter>
</receiver>
</application>
...

<uses-permission android:name="android.permission.READ_PHONE_STATE">
</uses-permission>

En las sentencias anteriores hemos declarado al sistema usando la etiqueta


<receiver> que esta aplicacin desea recibir los mensajes de difusin del tipo (etiqueta
<intent-filter>) estado del telfono (PHONE_STATE) usando la clase ReceptorLlamadas para
gestionarlas.
La clase ReceptorLlamadas que implementa el receptor de mensajes de difusin
contiene las siguientes sentencias:

public class ReceptorLlamadas extends BroadcastReceiver {

362

Content Providers, servicios y notificaciones

@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
String estado = extras.getString(TelephonyManager.EXTRA_STATE);
Log.w("ESTADO TELEFONO", estado);
if (estado.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
String numeroTelefono= extras
.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.w("NUMERO TELEFONO", numeroTelefono);
}
}
}
}

Como hemos comentado anteriormente, el mensaje de difusin se recibe en el mtodo


onReceive() de la clase BroadcastReceiver. En este mtodo hemos obtenido la informacin
extra de la intencin y la hemos mostrado en el Log de mensajes de Eclipse.

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Receptor de mensajes de difusin) de la


Unidad 7. Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del
programa anterior, en el que hemos definido un Receptor de mensajes de difusin.
Si ejecutamos la aplicacin, usando el DDMS para simular una llamada de telfono
entrante, veremos la siguiente pantalla:

363

7.4.6 Ejemplo de envo y recepcin de mensajes internos en una aplicacin y uso de


servicios por defecto de Android
En este Ejemplo 3 vamos a usar el Gestor de alarmas (AlarmManager) y el de
vibraciones del telfono (VibratorManager) para iniciar los servicios por defecto "Alarma" y
"Vibracin" de Android. Vamos a configurar una alarma en el gestor de alarmas de Android y,
cuando termine la cuenta atrs del tiempo que establezca el usuario, el gestor de alertas
mandar un mensaje de difusin al receptor de mensajes que hemos definido previamente en
la misma aplicacin.
Para recibir el mensaje de difusin hemos creado el receptor MiBroadcastReceiver a
partir de la clase BroadcastReceiver:

public class MiBroadcastReceiver extends BroadcastReceiver {


@Override
// Definimos el mtodo onReceive para recibir mensajes de difusin
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Se ha acabado la cuenta atrs! \nEl
telfono est vibrando", Toast.LENGTH_LONG).show();
// Vibramos el telfono durante 2 segundos obteniendo el servicio
Vibrator de Android
Vibrator vibrator = (Vibrator)
context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(2000);

364

Content Providers, servicios y notificaciones

}
}

Este receptor de mensajes busca el servicio de Vibracin (Vibrator), se conecta a l y


le indica que vibre el telfono durante dos segundos.
Para cargar el servicio "Vibracin" por defecto de Android hemos usado el mtodo
getSystemService(), al que indicamos como parmetro el nombre del servicio al que
queremos acceder.
Para que Android conozca que tiene disponible un receptor de mensajes de difusin y
permita a la aplicacin el acceso al servicio de vibracin, debemos aadir al fichero
AndroidManifest.xml las siguientes lneas:

<receiver android:name="MiBroadcastReceiver"></receiver>
...
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>

A continuacin, solo queda indicar en la actividad principal que se inicie una la cuenta
atrs:

public class AlarmaActivity extends Activity {


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

public void iniciarCuenta(View view) {


// Obtenemos el tiempo de la cuenta atrs
EditText texto = (EditText) findViewById(R.id.tiempo);
if (texto.getText().equals("")){
Toast.makeText(this, "Al menos debes indicar 1 segundo",
Toast.LENGTH_LONG).show();
return;
}

365

int i = Integer.parseInt(texto.getText().toString());
// Cargamos el BroadcastReceiver
Intent intent = new Intent(this, MiBroadcastReceiver.class);
// Lo iniciamos como una Intencin pendiente
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), 1, intent, 0);
// Creamos una alarma
AlarmManager alarmManager = (AlarmManager)
getSystemService(ALARM_SERVICE);
// Establecemos el tiempo de la alarma e indicamos el pendingIntent
que se debe ejecutar cuando acabe la cuenta
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ (i * 1000), pendingIntent);
// Mostramos un mensaje indicando que comienza la cuenta atrs
Toast.makeText(this, "Inicio de Cuenta atrs de " + i + " segundos",
Toast.LENGTH_LONG).show();
}
}

Para cargar el servicio "Alarma" por defecto de Android, hemos usado el mtodo
getSystemService(), al que indicamos como parmetro el nombre del servicio al que
queremos acceder.
En el cdigo anterior podemos observar que hemos usado la clase AlarmManager
para acceder al servicio de gestin de alarmas. Con su mtodo set() se crea una nueva alarma
que salta pasados n segundos y que lanza, a continuacin, la intencin pendiente (es realidad
es una intencin que hereda los permisos de la actividad principal).
Esta intencin pendiente se forma a partir de una intencin normal que invoca
explcitamente la clase que recibe el mensaje y que transformamos en un mensaje de difusin
con el mtodo getBroadcast() de PendingIntent.
Si ejecutas el Ejemplo 3 de esta Unidad vers las siguientes pantallas:

366

Content Providers, servicios y notificaciones

7.4.7 Crear un servicio propio


En el Ejemplo 4 de esta Unidad vamos a ver cmo definir un servicio privado en modo
Ligado dentro de una aplicacin Android.
Los servicios deben utilizarse para mantener en segundo plano tareas en ejecucin de
la aplicacin, como descargar mensajes de correo de un servidor.
Cuando el usuario solicita que se actualice su buzn de correo, la aplicacin que ya
est ligada (en ingls bind) al servicio, invoca uno de sus mtodos para obtener los nuevos
mensajes recibidos.
Como ya hemos comentado, para crear un servicio debemos definir una clase que se
extienda de la clase Service de Android:

public class Servicio extends

Service {

// Variable donde guardamos los datos que devuelve el servicio


private ArrayList<String> listado = new ArrayList<String>();
// Constante donde tenemos los datos que vamos a ir cargando cada 5
segundos en la variable anterior
private static String[] listadoDatos = {
"El comercio internacional ha aumentado un 7%",
"Hoy se publica un nuevo libro de Prez Jimnez",
"Benetton retira la foto que irrit al Vaticano",
"Diego Rivera vuelve al Nueva York de la crisis",

367

"Facebook reconoce un ataque coordinado",


"Bradley Cooper, el hombre ms sexy del mundo",
"Dimite el responsable en Europa del FMI por 'motivos personales'",
"El invierno ya est aqu" };
// Usamos el temporizador para ir aadiendo datos al listado
private Timer temporizador = new Timer();
// Cada 5 segundos actualizamos los datos del listado
private static final long INTERVALO = 5000;
// IBinder que usa la actividad principal para unirse al servicio y obtener
informacin
private final IBinder miBinder = new MiBinder();
// Variable que usamos para controlar el ltimo elemento aadido al listado
private int indice = 0;

// Debemos definir redefinir el mtodo onCreate


public void onCreate() {
super.onCreate();
// Iniciamos el temporizado que va cargando datos poco a poco en el
listado
temporizador.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// Si el listado ya contiene los 7 elementos, quitamos el primer
elemento
if (listado.size() >= 8) {
listado.remove(0);
}
// Aadimos el listado el elemento siguiente de la matriz
constante
listado.add(listadoDatos[indice++]);
// Si ya hemos llegado al ltimo elemento, volvemos a empezar
if (indice >= listadoDatos.length) {
indice = 0;
}
}

368

Content Providers, servicios y notificaciones

}, 0, INTERVALO);
}

// Debemos redefinir el mtodo onDestroy


@Override
public void onDestroy() {
super.onDestroy();
// Si el temporizador sigue funcionando, lo liberamos de la memoria
if (temporizador != null) {
temporizador.cancel();
}
}

// Es obligatorio redefinir este mtodo.


// Devuelve el canal de comunicacin con el servicio.
@Override
public IBinder onBind(Intent arg0) {
return miBinder;
}

// Clase que devuelve el contexto del servicio


public class MiBinder extends Binder {
Servicio getService() {
return Servicio.this;
}
}

// Mtodo del servicio que invoca la actividad principal


public List<String> getDatos() {
return listado;
}
}

369

Como vamos a usar el servicio en modo Ligado, hemos definido el mtodo onBind() en
el cdigo Java anterior.
En el archivo AndroidManifest.xml debemos declarar el nuevo servicio:

<service android:name=".Servicio"></service>

En la actividad principal del Ejemplo 4 implementamos cmo usar el servicio en modo


Ligado:

public class ServicioActivity extends Activity {


// Variable donde almacenamos el servicio
private Servicio s;
// Matriz que se usa para cargar el adaptador del ListView de la actividad
principal
private ArrayList<String> matrizAdaptador;
// Adaptador del ListView de la actividad principal
private ArrayAdapter<String> adaptador;

// Variable que recibe la Conexin al servicio de la aplicacin


private ServiceConnection miConexion = new ServiceConnection() {
// Al conectar al servicio, obtenemos una referencia del mismo y
// mostramos un mensaje al usuario
public void onServiceConnected(ComponentName className, IBinder
binder) {
s = ((Servicio.MiBinder) binder).getService();
Toast.makeText(ServicioActivity.this, "Conectado al servicio",
Toast.LENGTH_SHORT).show();
}
// Desconexin del servicio, liberamos variables
public void onServiceDisconnected(ComponentName className) {
s = null;
}
};

370

Content Providers, servicios y notificaciones

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// Unimos esta actividad al servicio indicando mediante una Intencin


explcita el nombre del servicio, la variable de conexin que
recibe el puntero del servicio y el modo de operacin
bindService(new Intent(this, Servicio.class), miConexion,
Context.BIND_AUTO_CREATE);

// Cargamos referencias ListView de la pantalla principal


matrizAdaptador = new ArrayList<String>();
adaptador = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, matrizAdaptador);
ListView list = (ListView) findViewById(R.id.list);
list.setAdapter(adaptador);
}

// Mtodo que se invoca cuando el usuario hace clic sobre el botn de la


pantalla principal
public void buscarDatosServicio(View view) {
// Si el servicio est activo
if (s != null) {
// Obtenemos los nuevos datos del servicio
List<String> datos = s.getDatos();
// Limpiamos el adaptador con los nuevos datos
matrizAdaptador.clear();
matrizAdaptador.addAll(datos);
// Indicamos que los datos del adaptador han cambiado
adaptador.notifyDataSetChanged();
}
}
}

Para conectar con el servicio definido en la clase Servicio, hemos escrito la sentencia:
371

bindService(new Intent(this, Servicio.class), miConexion,


Context.BIND_AUTO_CREATE);

El mtodo bindService (Intent service, ServiceConnection conn, int flags) se


invoca con los siguientes tres parmetros:

service: Intent que identifica el servicio al que queremos conectar. Este Intent
puede ser explcito (como en el ejemplo) indicando el nombre de la clase que
implementa el servicio o implcito sealando la accin que se define mediante
un IntentFilter de un servicio publicado en el sistema.

conn: recibe la informacin del resultado de la clase de conexin


ServiceConnection.

flags: opciones que podemos indicar al unirnos al servicio. Puede contener 0,


BIND_AUTO_CREATE (crea el servicio mientras haya componentes ligados a
l), BIND_DEBUG_UNBIND (incluye informacin de depuracin cuando se
produce un desligue de los componentes), BIND_NOT_FOREGROUND (no
permite que el servicio cambie de hilo de ejecucin), BIND_ABOVE_CLIENT
(el servicio tiene ms prioridad de ejecucin que la aplicacin que lo inicia),
BIND_ALLOW_OOM_MANAGEMENT

(servicio

normal

que

puede

ser

eliminado de memoria si el sistema la necesita) o BIND_WAIVE_PRIORITY (el


servicio se trata en segundo plano sin cambio de prioridad), etctera.

Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Servicio) de la Unidad 7. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que hemos
definido un Servicio.

Si ejecutas la aplicacin y pulsas el botn Cargar informacin del servicio, vers la


siguiente pantalla:

372

Content Providers, servicios y notificaciones

Si pulsas el botn cada 5 segundos, vers que la aplicacin recarga datos en la


pantalla principal.

7.5

NOTIFICACIONES AL USUARIO EN ANDROID

En Android existen varias formas de notificar mensajes o informacin al usuario.


En la Unidad 3 de este curso ya hemos visto el uso de Dilogos para mostrar al
usuario informacin e, incluso, solicitar que introduzca algn texto.
En este apartado vamos a estudiar dos tipos de notificaciones ms:

Mensajes emergentes: en ingls Toast. Aunque ya hemos usado este tipo de


mensajes previamente en el curso, vamos a describir con ms detalle toda su
funcionalidad, ya que son muy tiles en las aplicaciones Android.

Mensajes en la barra de estado. Son mensajes que aparecen en forma de


icono en la barra de estado en la parte superior del dispositivo:

7.5.1 Mensajes emergentes (Toast)


Un mensaje emergente (en ingls Toast) es un mensaje que se muestra en la pantalla
del dispositivo Android durante unos segundos y desaparece automticamente sin requerir
ningn tipo de actuacin por parte del usuario.
373

Este mensaje no recibe el foco de la aplicacin en ningn momento, es decir, no


interfiere con las acciones que est realizando el usuario en ese momento.
Por defecto, aparecen en la parte inferior de la pantalla, dentro de un rectngulo gris
ligeramente translcido. Este tipo de notificaciones son perfectas para mostrar mensajes
rpidos y sencillos al usuario, puesl no requiere confirmacin.
Ya hemos visto durante el curso que su utilizacin es muy sencilla. La clase Toast
dispone del mtodo esttico makeText(Context context, CharSequence text, int duration) al
que debemos pasar como parmetros el contexto de la actividad, el texto del mensaje y el
tiempo que de permanecer en la pantalla en milisegundos.
En el parmetro duration podemos usar las siguientes constantes definidas por
Android:

Toast.LENGTH_LONG: mensaje de duracin larga. Se usa para textos muy


largos.

Toast.LENGTH_SHORT: mensaje de duracin corta. Se usa para mensajes


ms cortos.

Tras obtener una referencia al objeto Toast a travs de este mtodo, usamos el
mtodo show() para mostrar el mensaje en la pantalla.
En el Ejemplo 5 de esta Unidad vamos a definir distintos tipos de Toast.
Desde Eclipse puedes abrir el proyecto Ejemplo 5 (Notificaciones) de la Unidad 7. Estudia el
cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que
hemos usado distintos tipos de Toast.

Para comenzar, vamos a incluir un botn que muestre un Toast bsico cuando
hagamos clic sobre l:

// Toast por defecto


xDefectoBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// Creamos el mensaje
Toast toast1 =
Toast.makeText(getApplicationContext(), "Toast por defecto",
Toast.LENGTH_SHORT);
// Mostramos el mensaje
toast1.show();

374

Content Providers, servicios y notificaciones

}
});

Si ejecutas la aplicacin y pulsas el botn Toast Por defecto vers la siguiente


pantalla:

Tambin podemos personalizar este Toast cambiando su posicin relativa en la


pantalla. Para esto utilizamos su mtodo setGravity(), al que indicamos en qu zona
deseamos que aparezca la notificacin. Esta zona se marca usando alguna de las constantes
definidas en la clase Gravity: CENTER, LEFT, BOTTOM, etctera, o utilizando una
combinacin de stas.
En el Ejemplo 5 vamos a colocar el mensaje en la zona central derecha de la pantalla.
Para esto, hay un segundo botn en la aplicacin que muestra un -Toast con estas
caractersticas:

// Toast con posicionamiento en pantalla


gravityBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Toast toast2 = Toast.makeText(getApplicationContext(), "Toast con
gravity", Toast.LENGTH_SHORT);
// Indicamos el posicionamiento
toast2.setGravity(Gravity.CENTER|Gravity.RIGHT,0,0);
toast2.show();

375

}
});

Si volvemos a ejecutar la aplicacin y pulsamos el nuevo botn, veremos que el Toast


aparece en la zona indicada de la pantalla:

Es posible personalizar por completo el aspecto del mensaje. Android ofrece la


posibilidad de definir un fichero de diseo (layout) XML propio para Toast, donde podemos
incluir todos los elementos necesarios para adaptar la notificacin a las necesidades de la
aplicacin. Para este Ejemplo 5 hemos definido un layout sencillo con una imagen y una
etiqueta de texto sobre un rectngulo gris. Si abres el fichero res/layout/layout_toast.xml
podrs ver su diseo:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layoutToast"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:background="#555555"

376

Content Providers, servicios y notificaciones

android:padding="5dip" >
<ImageView android:id="@+id/imagen"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/info" />

<TextView android:id="@+id/mensajeLbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="#FFFFFF"
android:paddingLeft="10dip" />
</LinearLayout>

Para asignar este fichero de diseo (layout) a un Toast, hay que proceder de una
forma algo distinta a como lo hemos hecho en las anteriores notificaciones.
En primer lugar, hay que inflar el layout mediante un objeto LayoutInflater, como ya
hemos usado en varias ocasiones a lo largo del curso, para disear la interfaz de usuario. Una
vez construido el layout, modificamos los valores de los distintos componentes internos de ste
para mostrar la informacin.
En este ejemplo, modificamos el mensaje de la etiqueta de texto y asignamos
estticamente una imagen en el layout XML mediante el atributo android:src. Despus,
establecemos la duracin de la notificacin con el mtodo setDuration() y asignamos el layout
personalizado al Toast mediante el mtodo setView(). El cdigo fuente incluido en el tercer
botn del ejemplo tiene este aspecto:

// Toast con diseo


layoutBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// Creamos el Toast
Toast toast3 = new Toast(getApplicationContext());
// Inflamos el diseo de layout_toast.xml
LayoutInflater inflater = getLayoutInflater();

377

View layout = inflater.inflate(R.layout.layout_toast,


(ViewGroup) findViewById(R.id.layoutToast));
// Asignamos los componentes del diseo
TextView txtMsg = (TextView)layout.findViewById(R.id.mensajeLbl);
txtMsg.setText("Toast con diseo personalizado");
// Indicamos la duracin corta para el mensaje
toast3.setDuration(Toast.LENGTH_SHORT);
// Asignamos el diseo al Toast
toast3.setView(layout);
// Mostramos el Toast
toast3.show();
}
});

Si ejecutamos ahora la aplicacin del ejemplo y pulsamos el botn Toast


Personalizado, aparece el Toast con la estructura definida en el archivo de diseo layout
personalizado:

7.5.2 Notificaciones en la barra de estado


En este apartado vamos a tratar otro tipo de notificaciones ms persistentes y
complejas de implementar, que son las notificaciones de la barra de estado de Android.
Estas notificaciones son las que muestran los dispositivos Android cuando recibimos un
mensaje SMS, hay actualizaciones disponibles, est el reproductor de msica funcionando en
segundo plano, etctera.
Estas notificaciones consisten en un icono y un texto que aparece en la barra de estado
superior. Adicionalmente, podemos indicar un mensaje ms largo y descriptivo y una marca de
fecha/hora que aparece al desplegar la bandeja del sistema.
378

Content Providers, servicios y notificaciones

Por ejemplo, cuando hay una llamada perdida en nuestro telfono, se muestra en un
lado el siguiente icono en la barra de estado:

Arrastrar

Si arrastramos la barra de estado del dispositivo, se despliega la bandeja del sistema


con ms informacin. En este ejemplo en concreto se informa del evento producido (Missed
calls), los nmeros de telfonos asociados y la fecha/hora del evento. Adems, al pulsar sobre
la notificacin se abre automticamente el historial de llamadas.

En el Ejemplo 5 de esta Unidad vamos a utilizar este tipo de notificaciones.


En este ejemplo hemos aadido un nuevo botn que genera una notificacin en la
barra de estado con los elementos comentados y con la posibilidad de dirigirnos a la propia
aplicacin del ejemplo cuando se pulsa sobre la notificacin.
Para generar notificaciones en la barra de estado del sistema, lo primero que hay que
hacer es obtener una referencia al servicio de notificaciones de Android usando la clase
NotificationManager.
Utilizamos el mtodo getSystemService() indicando como parmetro el identificador
del servicio al que queremos conectar, en este caso a Context.NOTIFICATION_SERVICE.

//Obtenemos una referencia al servicio de notificaciones

379

String ns = Context.NOTIFICATION_SERVICE;
NotificationManager notManager = (NotificationManager) getSystemService(ns);

Despus, configuramos las caractersticas de la notificacin. En primer lugar,


establecemos el icono y el texto que aparece en la barra de estado. Tambin registramos la
fecha y hora asociadas a la notificacin. Con estos datos construimos un objeto Notification.
En este ejemplo, utilizamos un icono predefinido de Android, el mensaje Atencin! y
registramos la fecha/hora actual indicada por el mtodo System.currentTimeMillis():

//Configuramos la notificacin que va a aparecer en la barra


int icono = android.R.drawable.stat_sys_warning;
CharSequence textoEstado = "Atencin!";
long hora = System.currentTimeMillis();

// Creamos la notificacin
Notification notificacion = new Notification(icono, textoEstado, hora);

A continuacin, utilizamos el mtodo setLatestEventInfo() para asociar a la


notificacin la informacin que aparece al desplegar la bandeja del sistema (ttulo y
descripcin) e indicar la actividad que debe iniciarse si el usuario pulsa sobre la notificacin.
Los dos primeros datos son simples cadenas de texto.
Para indicar la actividad que se debe ejecutar si el usuario pulsa sobre la notificacin,
debemos construir una Intencin pendiente PendingIntent, que ya hemos usado en el
apartado anterior de esta Unidad.
Esta Intencin pendiente contiene la informacin de la actividad asociada a la
notificacin que ser lanzada al pulsar sobre ella. Para esto, definimos un objeto Intent
indicando la clase de la actividad concreta que se debe ejecutar. En este ejemplo el objeto es
la propia actividad principal (NotificacionesActivity.class). Este Intent lo utilizamos para
construir el PendingIntent final mediante el mtodo PendingIntent.getActivity().
Veamos cmo queda esta ltima parte del cdigo, comentado:

Intent notIntent = new Intent(contexto, NotificacionesActivity.class);


// Usamos una PendingIntent para crear la notificacin
PendingIntent contIntent = PendingIntent.getActivity(
contexto, 0, notIntent, 0);
// Incluimos la informacin de la notificacin
notificacion.setLatestEventInfo(contexto, titulo, descripcion, contIntent);

380

Content Providers, servicios y notificaciones

Es posible indicar opciones adicionales, como, por ejemplo, que la notificacin


desaparezca automticamente de la bandeja del sistema cuando se pulsa sobre ella. Esto lo
conseguimos

usando

al

atributo

flags

de

la

notificacin

con

el

valor

Notification.FLAG_AUTO_CANCEL.
Tambin podramos indicar que, al crearse la notificacin, el dispositivo suene, vibre o
se encienda el LED de estado presente en muchos dispositivos. Para ello, basta con aadir al
atributo defaults de la notificacin los valores DEFAULT_SOUND, DEFAULT_VIBRATE o
DEFAULT_LIGHTS.

//AutoCancel: cuando se pulsa la notificacin desaparece


notificacion.flags |= Notification.FLAG_AUTO_CANCEL;

//Para aadir sonido, vibracin y luces hay que descomentar estas sentencias
//notif.defaults |= Notification.DEFAULT_SOUND;
//notif.defaults |= Notification.DEFAULT_VIBRATE;
//notif.defaults |= Notification.DEFAULT_LIGHTS;

Existen otras muchas opciones y personalizaciones de estos atributos flags y defaults


que se pueden consultar en la documentacin oficial de la clase Notification de Android.
Para acabar, una vez tenemos definidas las opciones de la notificacin, podemos
generarla invocando el mtodo notify() y pasando como parmetro un identificador nico
definido por la aplicacin, as como el objeto Notification construido anteriormente.

//Enviamos la notificacin
notManager.notify(ID_MEN_BARRA_NOTIF, notificacion);

Si volvemos a ejecutar la aplicacin y pulsamos de nuevo el botn Notificacin en la


barra de estado, veremos que aparece un icono en la barra de estado del dispositivo virtual:

381

Si desplegamos la bandeja del sistema, podemos verificar el resto de informacin de la


notificacin:

Por ltimo, si pulsamos sobre la notificacin, se abre automticamente de nuevo la


aplicacin de este ejemplo. Adems, la notificacin desaparece de la bandeja del sistema, ya
que lo habamos configurado en el cdigo Java con la opcin FLAG_AUTO_CANCEL:

382

Content Providers, servicios y notificaciones

Desde Eclipse puedes abrir el proyecto Ejemplo 5 (Notificaciones) de la Unidad 7. Estudia el


cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que
hemos lanzado una notificacin a la barra de estado del dispositivo.

7.6

USO DE VIEWPAGER EN APLICACIONES ANDROID

Si has utilizado alguna vez un dispositivo Android, te habrs dado cuenta de que
algunas aplicaciones permiten desplazar pginas deslizando el dedo horizontalmente sobre la
pantalla. Por ejemplo, en la aplicacin del Android Market y en el visor de imgenes podemos
cambiar de pgina dentro de la misma aplicacin:

383

Al desplazar el dedo
cambia de pantalla.

se

Para desarrollar esta funcionalidad hay que emplear el componente ViewPager de


Android que est heredado de la clase ViewGroup.
Este componente no forma parte de las clases por defecto del SDK de Android. Est
incluido en el paquete externo de Compatibilidad de Android que deberas haber aadido al
instalar el SDK de Android en Eclipse. Para comprobar que est bien aadido, haz clic en el
botn "Opens the Android SDK Manager" de Eclipse:

Debe aparecer el siguiente paquete como instalado ("Installed"):

Nota: el nmero de revisin puede ser mayor que 4.


Puedes encontrar estas libreras en el directorio
384

Content Providers, servicios y notificaciones

C:\cursos_Mentor\Android\android-sdk-windows\extras\android\support\v4

7.6.1 Cmo se usa el componente ViewPager


A continuacin, vamos a mostrar en el Ejemplo 6 de esta Unidad cmo utilizar el
componente ViewPager en una aplicacin Android.
Una vez que hemos comprobado que tenemos las libreras extra de compatibilidad de
Android, procedemos a incluirlas en el proyecto.
En este proyecto hemos creado la carpeta "libs" y copiado dentro el archivo androidsupport-v4.jar del directorio donde se encuentre la librera:

A continuacin, aadimos la librera al Build Path haciendo clic con el botn derecho
del ratn sobre el archivo de la librera y eligiendo la opcin "Build Path->Add to Build Path"
del men desplegable:

385

Para comprobar que hemos incluido la librera correctamente en Eclipse, debe


aparecer como Librera referenciada ("Referenced Libraries"):

La aplicacin que vamos a desarrollar consta de una Actividad que muestra un visor
sencillo de imgenes dentro del ViewPager. Para generar las pginas contenidas en este
ViewPager es necesario usar un objeto PagerAdapter, que se encarga de alimentar de
pginas al componente ViewPager.
Veamos las sentencias comentadas para crear la Actividad principal de la aplicacin:

public class ViewPagerActivity extends Activity {


// Define el n de pginas en el ViewPager
private static int NUMERO_VIEWS = 10;
// Variable de ViewPager
private ViewPager vPager;
// Adaptador del ViewPager
private CustomPagerAdapter vPagerAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Buscamos el ViewPager en el diseo main.xml
vPager = (ViewPager) findViewById(R.id.vPager);
// Creamos el adaptador de N Pginas y pasamos el contexto de la
aplicacin
vPagerAdapter = new CustomPagerAdapter(NUMERO_VIEWS, this);
// Asignamos el adaptador al ViewPager
vPager.setAdapter(vPagerAdapter);

386

Content Providers, servicios y notificaciones

// Definimos el evento de cambio de pgina en el ViewPager


vPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Toast.makeText(getBaseContext(), "Has cambiado a la pantalla " +
(position+1), 1).show();
}

@Override
public void onPageScrollStateChanged(int arg0) {
// No definimos nada en el evento al hacer scroll en la pgina
}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// No definimos nada en el evento al hacer scroll en la pgina
}
}); // end setOnPageChangeListener
}
}

En el cdigo anterior no hay nada especial que resaltar. Buscamos en el archivo de


diseo el ViewPager y le asignamos su adaptador con el mtodo setAdapter(). Adems,
usamos el mtodo setOnPageChangeListener() para mostrar un mensaje Toast cada vez que
el usuario cambie de pgina.
El archivo de diseo Layout de la actividad principal se implementa as:

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#a4c639">
<android.support.v4.view.ViewPager

387

android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/vPager"/>
</LinearLayout>

Como se trata de una Vista que se define en un paquete extra de Android, es necesario
incluir el nombre completo del mismo android.support.v4.view.ViewPager.
Luego, creamos el adaptador personalizado a partir de la clase PagerAdapter, para
que cree las pginas internas del ViewPager, devolviendo vistas segn vamos desplazando el
dedo horizontalmente por la pantalla:

public class CustomPagerAdapter extends PagerAdapter{


// Variables donde guardamos el contexto y el nmero de pginas
private Context contexto;
private int nViews;
// Constructor de la clase
public CustomPagerAdapter(int nViews, Context contexto) {
this.contexto=contexto;
this.nViews=nViews;
}
@Override
// Devuelve el n de pgina del Adaptador del ViewPager
public int getCount() {
return nViews;
}

/**
*

Crea la pgina de la position indicada. El adaptador

es el responsable de aadir componentes a cada pgina.

*
* @param collection La Vista (View) donde se almacena la pgina.
* @param position Nmero de pgina que debemos crear.
* @return Devuelve el objeto que representa la pgina.

388

No tiene por qu

Content Providers, servicios y notificaciones

* ser una Vista, puede contener a su vez otras pginas.


*/
@Override
public Object instantiateItem(View collection, int position) {
/* Creamos mediante sentencias Java el diseo de la pgina.
* Tambin podramos haber guardado el diseo en un archivo
* xml y haberlo inflado aqu.
*/
// Creamos el Layout donde aadimos el resto de Vistas
LinearLayout linearLayout = new LinearLayout(contexto);
//Orientacion vertical = 1
linearLayout.setOrientation(1);
// Definimos una etiqueta de texto
TextView tv = new TextView(contexto);
tv.setText("Imagen nmero " + (position+1));
tv.setTextColor(Color.WHITE);
tv.setTextSize(30);
// Definimos una imagen
ImageView imagen = new ImageView(contexto);
// Buscamos la imagen en el directorio /res/drawable en funcin del n
de pgina
int resID = contexto.getResources().getIdentifier("imagen"+
(position+1), "drawable", "es.mentor.unidad7.eje6.viewpager");
// Asignamos la imagen cargada del recurso
imagen.setImageResource(resID);
// Definimos unos parmetros para alinear la etiwueta superior
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 20);
params.gravity=Gravity.CENTER;
// Aadimos la etiqueta superior al Layout con los parmetros
anteriores
linearLayout.addView(tv, params);
// Aadimos la imagen al Layout
linearLayout.addView(imagen);

389

// Aadimos la pgina a la coleccin de pginas


((ViewPager) collection).addView(linearLayout,0);
// Devolvemos el diseo de la pgina
return linearLayout;
} // end instantiateItem

/**
*

Destruye el contenido de la pgina indicada en position. El adaptador

es el responsable de borrar los componentes de cada pgina.

*
* @param collection La Vista (View) donde se elimina la pgina.
* @param position Nmero de pgina que debemos eliminar.
* @return object El mismo objeto creado en {@link #instantiateItem(View,
int)}.
*/
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((LinearLayout) view);
}

/**
*

Compara si la Vista view est instanciada en el Objeto object. Mtodo


necesario para la clase ViewPager

*/
@Override
public boolean isViewFromObject(View view, Object object) {
return view==((LinearLayout)object);
}

/**
* Android invoca este mtodo cuando el cambio de una de las pginas se ha
completado.
*/
@Override

390

Content Providers, servicios y notificaciones

public void finishUpdate(View arg0) {}

/**
* Mtodo que se invoca cuando Android indica que hay que recuperar el
estado de ejecucin
*/
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {}

/**
* Mtodo que se invoca cuando Android indica que hay que guardar el estado
de ejecucin
*/
@Override
public Parcelable saveState() {
return null;
}

/**
* Android invoca este mtodo cuando se inicia el cambio de una de las
pginas.
*/
@Override
public void startUpdate(View arg0) {}
}

Los mtodos ms importantes del cdigo anterior son:

instantiateItem: crea la pgina para la posicin indicada como parmetro del


mtodo. Este adaptador es el responsable de aadir las Vistas a cada pgina.
Creamos el diseo de la Vistas contenidas en la pgina mediante sentencias
Java. Tambin podramos haber guardado el diseo en un archivo xml y
haberlo inflado.

destroyItem: destruye la pgina indicada en el parmetro posicin.

Las imgenes que se cargan en el visor de imgenes estn almacenadas en el


directorio /res/drawable del proyecto. Para cargarlas dinmicamente en funcin del nmero de
pgina que el adaptador CustomPagerAdapter debe crear hemos obtenido los recursos del
391

contexto con la orden contexto.getResources(); despus, hemos buscado el ID del recurso de


la

imagen

usando

el

mtodo

getIdentifier(nombre_recurso,

tipo_recurso,

paquete_recurso).
Desde Eclipse puedes abrir el proyecto Ejemplo 6 (ViewPager) de la Unidad 7. Estudia el
cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que
hemos usado un ViewPager.

Si ejecutamos la aplicacin y arrastramos el ratn horizontalmente sobre la pantalla


del emulador simulando el efecto de un dedo (puede costar un poco hacerlo con el ratn),
veremos las siguientes ventanas:

Arrastrar

Cambio de pgina

392

Arrastrar

Cambio de pgina

Content Providers, servicios y notificaciones

Un Proveedor de contenido (en ingls Content Provider) es el mecanismo


proporcionado por Android para compartir informacin entre aplicaciones.
Los Proveedores de contenidos los usan muchas aplicaciones estndar de un
dispositivo Android, como, por ejemplo, la lista de contactos, la aplicacin de SMS
mensajes cortos, el calendario, etctera.
Para implementar un Proveedor de contenidos propio, hay que usar la clase
ContentProvider de Android.
Las tablas de la base de datos SQLite usadas por un Content Provider deben
incluir siempre el campo _ID que identifica sus registros de forma unvoca.
El acceso a un Content Provider se realiza siempre mediante un identificador URI,
que es una cadena de texto parecida a una direccin Web de Internet.
La clase ContentResolver de Android permite realizar acciones con cualquier
Content Provider que est disponible en el sistema operativo Android.
Un Servicio (en ingls Service) es un componente de una aplicacin Android que se
ejecuta en segundo plano, sin interactuar con el usuario (no tiene interfaz de
usuario), para realizar operaciones de larga duracin.
La plataforma Android ofrece una gran cantidad de servicios predefinidos en el
sistema, a los que podemos acceder a travs de la clase de tipo Manager.
Un servicio puede funcionar de dos modos:

Autnomo: el servicio se puede ejecutar en segundo plano de forma


indefinida, incluso si el componente que lo inici se destruye.

Dependiente o Ligado (bind): ofrece una interfaz de tipo clienteservidor que permite a los componentes de una aplicacin interactuar
con l enviando peticiones y recibiendo su resultado. Un servicio ligado
slo se ejecuta mientras otro componente de la aplicacin est unido a
l.

393

Los servicios propios de una aplicacin se ejecutan en el hilo principal de su


proceso; por lo tanto, para no bloquear el hilo principal o de interfaz, debemos
ejecutar estos servicios con hilos de ejecucin.
Para implementar un servicio propio en una aplicacin tenemos que extender la
clase Service de Android.
Se pueden usar mensajes de difusin (Broadcast) para comunicar eventos entre
servicios. Estos mensajes son, en realidad, Intents.
La clase Receptor de mensajes de difusin (BroadcastReceiver) se usa para
recibir Intenciones (Intents), es decir, mensajes enviados por otro componente de
Android.
En Android existen varias formas de notificar mensajes o informacin al
usuario.

Dilogos: muestran o solicitan informacin al usuario.

Mensajes emergentes (en ingls Toast).

Mensajes de notificacin en la barra de estado del dispositivo.

Un mensaje emergente (en ingls Toast) es un mensaje que se muestra en la


pantalla

del

dispositivo

Android

durante

unos

segundos

desaparece

automticamente sin requerir ningn tipo de actuacin por parte del usuario.
Los mensajes de notificacin de la barra de estado de Android se muestran en
la barra de estado de los dispositivos Android cuando recibimos un mensaje
SMS, hay actualizaciones disponibles, est el reproductor de msica funcionando,
etctera.
Para generar notificaciones en la barra de estado del sistema, hay que obtener una
referencia

al

servicio

de notificaciones

de

Android

usando

la

clase

NotificationManager.
El componente ViewPager permite disear aplicaciones que incluyen pginas que
se pueden desplazar deslizando el dedo horizontalmente sobre la pantalla.
Este componente ViewPager no forma parte de las clases por defecto del SDK de
Android. Est incluido en el paquete externo de Compatibilidad de Android.
394

ANDROID AVANZADO

NDICE
8.1 INTRODUCCIN ................................................................................... 397
8.2 CMO DEPURAR APLICACIONES ANDROID CON ECLIPSE ..... 397
8.2.1 Estableciendo Puntos de interrupcin (Breakpoints) ............... 399
8.2.2 Iniciar la depuracin (Debug) del cdigo .................................... 400
8.2.3 Datos de depuracin (Debug) del cdigo................................... 401
8.2.4 Desactivar la depuracin de cdigo ............................................ 403
8.2.5 Propiedades de los puntos de interrupcin ............................... 404
8.2.6 Puntos de interrupcin de excepciones ..................................... 405
8.2.7 Puntos de interrupcin de mtodo .............................................. 405
8.2.8 Puntos de interrupcin de clase (class) ..................................... 405
8.2.9 Finalizar la Depuracin del cdigo .............................................. 406
8.3 USO DE MAPAS EN APLICACIONES ANDROID ............................ 406
8.3.1 Preparacin del Entorno de programacin ................................ 407
8.3.2 Cmo incluir mapas en las aplicaciones Android ..................... 410
8.4 DESARROLLO DE APLICACIONES SENSIBLES A LA
ORIENTACIN DEL DISPOSITIVO .................................................... 418
8.4.1 Cambio de orientacin automtica.............................................. 420
8.4.2 Mantener la informacin del estado durante el cambio de
orientacin ...................................................................................... 424
8.4.3 Cambio de orientacin Manual ...................................................... 427
8.5 DESPLEGAR APLICACIONES ANDROID EN DISPOSITIVOS
VIRTUALES (AVD) O REALES ........................................................... 431
8.6 CMO PUBLICAR APLICACIONES EN EL ANDROID MARKET .. 435
8.6.1 Alta de cuenta de desarrollador en el Android Market ............. 435
8.6.2 Recomendaciones sobre aplicaciones para Android Market .. 439
8.6.2.1 Recomendaciones sobre aplicaciones para Android
Market .................................................................................... 439

8.6.2.2 Buenas prcticas para el desarrollo de aplicaciones


Android .................................................................................. 440
8.6.3 Generar fichero APK con certificado para Android Market ..... 441
8.6.4 Publicar una aplicacin Android en el Android Market............. 445

Android Avanzado

8.1

INTRODUCCIN

En esta Unidad vamos a explicar cmo depurar (debug en ingls) aplicaciones


Android con Eclipse.
Despus, veremos cmo utilizar Mapas en aplicaciones Android mediante la API de
Google.
Asimismo, veremos cmo cambiar el aspecto de las aplicaciones Android cuando
cambia la orientacin del dispositivo.
Finalmente, conoceremos cmo desplegar aplicaciones en un dispositivo real
Android y publicar una aplicacin en el Android Market.

8.2

CMO DEPURAR APLICACIONES ANDROID CON ECLIPSE

La Depuracin de programas es el proceso de identificar y corregir errores de


programacin en tiempo de ejecucin. En ingls se denomina debugging, ya que se asemeja
a la eliminacin de bichos (bugs), que es como se denominan informalmente los errores de
programacin
Para depurar (en ingls Debug) una aplicacin Andriod, vamos a emplear las
capacidades disponibles en el entorno de desarrollo Eclipse. Para ello, nos serviremos de la
ltima versin disponible, la 3.7, a fecha de edicin de este documento.
Para que el alumno o alumna pueda practicar la Depuracin de cdigo Android con
Eclipse, hemos creado un proyecto Android con las siguientes clases:

public class DepuracionActivity extends Activity {


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// Usamos la clase que ya existe en el otro fichero


Contador contador = new Contador();
contador.count();
System.out.println("Hemos contado " + contador.getResultado() +
" veces.");

397

Object o = null;
o.toString();
}
}
// Clase sencilla que implementa un contador
public class Contador {

// Variable para guardar la cuenta actual


private int resultado=0;

public int getResultado() {


return resultado;
}

// Mtodo que cuenta de 2 en 2


public void count() {
for (int i = 0; i < 100; i++) {
resultado += i++;
}
}
}

Es recomendable abrir en Eclipse el Ejemplo 1 (Depuracin) de la Unidad 8 y practicar los


comandos que se muestran a continuacin.

Si ejecutas la aplicacin tal y como est, vers que aparece el siguiente mensaje de error:

398

Android Avanzado

Si haces clic en el botn "Force close", la aplicacin termina. Veamos cmo depurar
este programa que provoca un error.

8.2.1

Estableciendo Puntos de interrupcin (Breakpoints)


En el desarrollo de software, un punto de interrupcin (Breakpoint en ingls) es una

marca en el cdigo fuente que indica al depurador del lenguaje en que estemos programando
que debe detener o pausar la ejecucin del programa para poder evaluar los valores asignados
a las variables y permitir al programador detectar errores en tiempo de ejecucin.
Para establecer puntos de interrupcin con Eclipse, hay que hacer clic en la opcin
"Toggle Breakpoint" del men desplegable que aparece si pulsamos el botn derecho del
ratn sobre el nmero de lnea del cdigo fuente correspondiente. Tambin podemos hacer
doble clic en este nmero de lnea para activar o desactivar esta opcin:

399

8.2.2

Iniciar la depuracin (Debug) del cdigo


Para iniciar la depuracin del cdigo hay que hacer clic en la opcin "Run->Debug" del

men principal de Eclipse. Tambin podemos usar la tecla rpida [F11] o usar el icono

del

men principal.
Si lo hacemos, a continuacin se instalar y se ejecutar la aplicacin en el dispositivo
virtual. Despus, Eclipse muestra el siguiente mensaje:

Contestaremos que s para cambiar el tipo de Perspectiva a "Debug", muy til para
depurar programas. A continuacin, cambiar la perspectiva de Eclipse as:

Y la ejecucin del programa se parar en la primera lnea del cdigo que tenga un
punto de interrupcin.
Podemos usar los siguientes atajos de teclado para depurar el programa:

400

Android Avanzado

Comando Descripcin

F5

La ejecucin pasa a la siguiente sentencia del programa. Si la sentencia siguiente


es la llamada a un mtodo o funcin, se continuar con la ejecucin de las
sentencias de este mtodo o funcin.

F6

La ejecucin pasa a la siguiente sentencia del programa. Si la sentencia siguiente


es la llamada a un mtodo o funcin, se continuar con la ejecucin de la sentencia
siguiente sin entrar en el cdigo de este mtodo o funcin.

F7

La ejecucin sigue todas las sentencias de todos los mtodos o funciones que
formen nuestro programa. Es decir, ejecuta en secuencia todas las rdenes que
conforman el programa.

F8

El programa se ejecuta hasta que se encuentre otro punto de interrupcin o hasta


que el usuario lo cierre.

Nota: tambin existen unos botones de acceso rpido que permiten ejecutar estas
rdenes. Observa la imagen siguiente:

8.2.3

Datos de depuracin (Debug) del cdigo


La vista "Debug" permite ver el contenido de la Pila "Stack" de la aplicacin:

En la parte superior derecha de Eclipse podemos ver el contenido de las variables.


Tambin podemos usar el men para cambiar el tipo de variables que han de visualizarse,
opcin muy til cuando hemos definido muchas variables:

401

Es posible tambin usar este men para cambiar las columnas que han de aparecer en
esta vista:

Adems, es posible utilizar la opcin "New Detail Formater" (men desplegable con el
botn derecho del ratn) para modificar la informacin mostrada sobre la variable. Por ejemplo,
como

el

texto

(posicin

de

memoria

de

una

variable)

es.mentor.unidad8.eje1.depuracion.Contador@4051b760 no dice nada, podemos usar la


opcin "New Detail Formater"

402

Android Avanzado

para invocar un mtodo de una clase y mostrar su resultado:

Ahora ya podemos ver el resultado:

8.2.4

Desactivar la depuracin de cdigo


Si deseas desactivar temporalmente todos los puntos de interrupcin, puedes pulsar el

botn "Skip All Breakpoints":

Si pulsas este botn otra vez, los puntos de interrupcin se activarn de nuevo.

403

8.2.5

Propiedades de los puntos de interrupcin


Despus de establecer un punto de interrupcin, puedes seleccionar las propiedades

de este punto para, por ejemplo, establecer una condicin lgica de parada. En las
propiedades se puede, por ejemplo, activar el punto de interrupcin y parar la ejecucin del
programa slo cuando una variable tenga cierto valor o se cumpla cierta condicin.
Para acceder a las propiedades del punto de interrupcin, hay que hacer clic en la
opcin "Breakpoint Properties..." del men desplegable con el botn derecho del ratn sobre
el punto de interrupcin:

En la ventana emergente podemos establecer la condicin de parada del punto de


interrupcin:

404

Android Avanzado

8.2.6

Puntos de interrupcin de excepciones


Los puntos de interrupcin de excepciones detienen la ejecucin de la aplicacin si se

inicia una excepcin especfica. Para definir este tipo de punto de interrupcin, hay que hacer
clic en el icono de excepcin siguiente:

8.2.7

Puntos de interrupcin de mtodo


Un punto de interrupcin de tipo mtodo se define haciendo doble clic en el borde

izquierdo del editor del mtodo correspondiente. Detiene el programa durante al ejecutar el
mtodo o, despus, al finalizar la ejecucin del mismo.

8.2.8

Puntos de interrupcin de clase (class)


Un punto de interrupcin de tipo clase se define haciendo doble clic en el borde

izquierdo del editor de la declaracin de la clase correspondiente. Detiene el programa al


cargar esta clase Java:

405

8.2.9

Finalizar la Depuracin del cdigo


Para finalizar la depuracin del cdigo basta con cambiar la Perspectiva a "Java" de

nuevo. Cuando hagamos alguna modificacin del cdigo fuente, aparecer el siguiente
mensaje para indicar que no se puede sustituir el cdigo de una aplicacin ya instalada en el
emulador de Android y se pregunta si deseamos desconectar ("Disconnect") el modo Debug:

Nota: en esta Unidad 8 puedes encontrar el vdeo Cmo depurar aplicaciones Android en
Eclipse, que muestra visualmente cmo llevar a cabo la depuracin del Ejemplo 1 de esta
Unidad.

8.3

USO DE MAPAS EN APLICACIONES ANDROID

En este apartado vamos utilizar mapas en aplicaciones de Android haciendo uso de la


API Android de Google Maps.
La mayora de los dispositivos Android permiten determinar su ubicacin geogrfica
actual a travs de un mdulo GPS (del ingls Global Positioning System, que se traduce como
Sistema de Posicionamiento Global). Android dispone del paquete android.location, que
proporciona la API para determinar la posicin actual geogrfica.

406

Android Avanzado

8.3.1

Preparacin del Entorno de programacin


Antes de empezar a utilizar el servicio de mapas de Google es necesario comprobar

que tenemos instalado el paquete correspondiente a las APIs de Google. Este paquete se
llama normalmente Google APIs by Google, Android API x, revisin y.
Al instalar el SDK de Android en Eclipse deberas haber aadido ya este paquete. Para
comprobar que est correctamente instalado, haz clic en el botn "Opens the Android SDK
Manager" de Eclipse:

Debe aparecer el siguiente paquete como instalado ("Installed"):

Nota: el nmero de revisin puede ser mayor que 2.


Para poder probar las aplicaciones en el emulador, tambin es necesario crear un
nuevo dispositivo virtual AVD que utilice este paquete como "target". Para ello, pulsamos el
botn "Opens the Android Virtual Device Manager":

Y se presenta una ventana, donde pulsamos sobre el botn "New". A continuacin,


aparece otra nueva ventana, donde rellenamos los campos tal y como aparecen en esta
captura:

407

Para acabar de crear el dispositivo virtual, hacemos clic en el botn "Create AVD".
Para poder utilizar la API de Google Maps es necesario obtener previamente una
clave de uso (API Key) que estar asociada al certificado con el que firmamos digitalmente las
aplicaciones. En el apartado "Permisos y Seguridad" de la Unidad 5 ya hemos hablado de
estos certificados, necesarios para firmar aplicaciones.
Si cambiamos el certificado con el que firmamos nuestra aplicacin, algo que
normalmente se hace como paso previo a la publicacin de la aplicacin en el Android Market,
tendremos que modificar tambin la clave de uso de la API.
Cuando compilamos una aplicacin en Eclipse y la probamos en el emulador de
Android, se aplica automticamente un certificado de depuracin creado por defecto. Por lo
tanto, para poder depurar en el emulador aplicaciones que hagan uso de Google Maps, hay
que solicitar una clave asociada a este certificado de depuracin.
En primer lugar, hay que localizar el fichero donde se almacenan los datos del
certificado de depuracin "debug.keystore". Podemos conocer la ruta de este fichero
accediendo a las preferencias de Eclipse, seccin "Android", apartado "Build":

408

Android Avanzado

En esta ventana copiamos en el portapapeles la ruta que aparece en el campo


Default Debug Keystore. Observa que hemos borrado intencionalmente la parte de la ruta
que ser distinta en tu ordenador.
Una vez conocemos la ruta del fichero debug.keystore, vamos a acceder a l con la
herramienta keytool.exe de Java para obtener el hash MD5 del certificado. Esto lo hacemos
desde

una

ventana

de

lnea

de

comandos

en

el

directorio

C:\Program

Files

(x86)\Java\jre6\bin (o donde est instalado Java) mediante la orden:

C:\Program Files (x86)\Java\jre6\bin>keytool -list -alias androiddebugkey


"ruta_del_certificado\debug.keystore" -storepass android -keypass android

-keystore

Nota: es necesario usar la versin 6 de Java, pues en la 7 no funciona.


Si lo hacemos, veremos la siguiente ventana:

409

A continuacin, copiamos en el portapapeles el dato que aparece identificado como


Huella digital de certificado (MD5).
Despus, accedemos a la Web de Google para solicitar una clave de utilizacin de la
API de Google Maps para depurar aplicaciones. En esta Web tendremos que escribir la Huella
digital MD5 de nuestro certificado para obtener la clave de uso de la API. En la siguiente
imagen se muestra el resultado:

Nota: Observa que hemos borrado intencionalmente parte de la clave, pues, cuando
solicites sta, te darn otra diferente.
Ya hemos terminado la preparacin del entorno de programacin para poder utilizar los
servicios de Google Maps dentro de nuestras aplicaciones Android.

8.3.2

Cmo incluir mapas en las aplicaciones Android


En el Ejemplo 2 de esta Unidad vamos a desarrollar una aplicacin que incluye un

mapa sobre el que podemos hacer unas operaciones sencillas, como cambiar a vista satlite o
desplazar el mapa.
Para poder ver este proyecto en tu emulador Android es necesario que obtengas la clave de
uso de la API de Mapas de Google y la cambies en el fichero de diseo main.xml de la interfaz
de usuario. Si no lo haces, arrancar la aplicacin del ejemplo pero no se mostrar el mapa,
como en la imagen siguiente:

410

Android Avanzado

Hay que tener en cuenta que, a la hora de crear el proyecto Android en Eclipse,
tenemos que seleccionar "Google APIs" en el campo "Build Target" en las propiedades del
proyecto:

Para incluir un mapa de Google Maps en una aplicacin Android, utilizamos el


componente MapView. Este componente se puede aadir al diseo de la pantalla como otro
componente normal. Sin embargo, para poder usarlo, hay que indicar la clave de uso de
Google Maps en el atributo android:apiKey tal y como se muestra a continuacin:
411

<!-- Aqu se escribe la clave de uso de Google Maps -->


<com.google.android.maps.MapView
android:id="@+id/mapa"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="xxxxxxxxxxxxxLIdwwbCEmC3DeN1omnaSkig"
android:clickable="true" />

Adems, tambin hemos establecido el atributo clickable a true, para que el usuario
pueda interactuar con el componente si quiere, por ejemplo, desplazar el mapa con el dedo.
Los componentes MapView slo se pueden utilizar desde una actividad de tipo
MapActivity. La clase MapActivity se extiende de la clase Activity y permite la gestin del
ciclo de vida de la Actividad y de los servicios de visualizacin de un mapas. De igual forma
que ListActivity se usa para mostrar listas, MapActivity se usa para mostrar mapas.
En el Ejemplo 2 la Actividad principal hereda la clase MapActivity, tal y como vemos
en el siguiente cdigo:
public class MapasActivity extends MapActivity

// Variables donde se definen los controles de la Actividad


private MapView mapa = null;
private Button sateliteBtn = null;
private Button irBtn = null;
private Button animarBtn = null;
private Button moverBtn = null;
private MapController controlMapa = null;
// Constantes que llevan a un punto en el mapa
private static Double latitud = 40.6550*1E6;
private static Double longitud = -4.7000*1E6;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Obtenemos una referencia a las Vistas de la Actividad
mapa = (MapView)findViewById(R.id.mapa);

412

Android Avanzado

sateliteBtn = (Button)findViewById(R.id.SateliteBtn);
irBtn = (Button)findViewById(R.id.IrBtn);
animarBtn = (Button)findViewById(R.id.AnimarBtn);
moverBtn = (Button)findViewById(R.id.MoverBtn);
//Definimos el Controlador del mapa
controlMapa = mapa.getController();
// Definimos un nuevo punto de localizacin
GeoPoint loc = new GeoPoint(latitud.intValue(), longitud.intValue());
// Centramos el mapa en este punto
controlMapa.setCenter(loc);
// Hacemos zoon a 6 (puede tomar el valor de 1 a 21)
controlMapa.setZoom(6);
//Mostramos los controles de zoom sobre el mapa
mapa.setBuiltInZoomControls(true);

// Definimos el evento onClick del botn Satlite


sateliteBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// Intercambiamos la capa de tipo satlite en el mapa
if(mapa.isSatellite())
mapa.setSatellite(false);
else
mapa.setSatellite(true);
}
});

// Definimos el evento onClick del botn Ir a...


irBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {

// Definimos un nuevo punto de localizacin

413

GeoPoint

loc

new

GeoPoint(latitud.intValue(),
longitud.intValue());

// Centramos el mapa en este punto


controlMapa.setCenter(loc);
// Hacemos zoon a 10 (puede tomar el valor de 1 a 21)
controlMapa.setZoom(10);
}
});
// Definimos el evento onClick del botn Animar
animarBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// Definimos un nuevo punto de localizacin
GeoPoint

loc

new

GeoPoint(latitud.intValue(),
longitud.intValue());

// Movemos con animacin el mapa en este punto


controlMapa.animateTo(loc);
// Hacemos zoom sobre esa posicin del mapa
int zoomActual = mapa.getZoomLevel();
for(int i=zoomActual; i<12; i++)
{
controlMapa.zoomIn();
}
}
});
// Definimos el evento onClick del botn Mover
moverBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// Movemos el mapa 1000 pxeles en horizontal y 50 en
vertical
controlMapa.scrollBy(1000, 50);
}
});

414

Android Avanzado

// Mtodo obligado de la clase que indica si estamos mostrando una ruta


@Override
protected boolean isRouteDisplayed() {
return false;
}
}

A continuacin, vamos a explicar las partes pertinentes del cdigo anterior.


Como la Actividad principal se hereda de la clase MapActivity, es obligatorio
implementar el mtodo isRouteDisplayed(), que debe devolver el valor true si vamos a mostrar
algn tipo de informacin de ruta sobre el mapa. Segn los trminos de licencia de uso de la
API de Google Maps, debe indicarse cundo se usan sus mapas para este propsito. En este
ejemplo del curso nos limitamos a mostrar un mapa en la pantalla principal de la aplicacin, por
lo que devolvemos el valor false.
Adems, en el mtodo onCreate() de la Actividad se invoca el mtodo
setBuiltInZoomControls() de la referencia de componente MapView para mostrar los
controles de zoom estndar sobre el mapa, de modo que podamos acercar y alejar la vista del
mapa.
Por defecto, cuando usamos un MapView en una aplicacin, se muestra en el modo de
mapa tradicional. Sin embargo, este componente tambin permite cambiar las capas a la vista
satlite, ver fotos de la calle con StreetView o mostrar la informacin del trfico. Para ello,
podemos usar los siguientes mtodos de la clase MapView:

setSatellite(true)

setStreetView(true)

setTraffic(true)

Tambin existen otros tres mtodos para consultar el estado de cada uno de estos
modos: isSatellite(), isStreetView() y isTraffic().
En el evento onClick del botn sateliteBtn hemos usado el mtodo setSatellite() para
intercambiar el modo satlite y el estndar.
Adems de los mtodos para personalizar el aspecto grfico del mapa, tambin
disponemos de varios mtodos para consultar la informacin geogrfica visualizada en el
mismo. Por ejemplo, podemos saber las coordenadas geogrficas en las que el mapa est
centrado actualmente

mediante el mtodo getMapCenter() y el nivel de zoom que est

aplicando a travs del mtodo getZoomLevel().


415

Como podemos observar en el cdigo anterior, las coordenadas del centro del mapa se
obtienen mediante el mtodo getMapCenter() en forma de objeto GeoPoint que encapsula los
valores de latitud y longitud expresados en microgrados (grados * 1E6). Los valores en la
magnitud

grados

se

pueden

obtener

mediante

los

mtodos

getLatitudeE6()

getLongitudeE6() respectivamente.
El nivel de zoom del mapa contiene un valor entero entre 1 y 21, siendo 21 el que
ofrece mayor nivel de detalle en el mapa.
Para modificar el centro del mapa, en primer lugar, debemos acceder al controlador del
mapa (MapController) mediante el mtodo getController(). Este mtodo devuelve un objeto
MapController con el que podemos modificar la posicin central del mapa. Para ello, podemos
usar los mtodos setCenter() y setZoom() a los que podemos indicar las coordenadas
centrales del mapa y el nivel de zoom deseado, respectivamente.
En este ejemplo hemos incluido un botn irBtn que centra el mapa sobre un punto
determinado y hemos aplicado un nivel de zoom (10), que permite distinguir en el mapa
algunos detalle. Si pruebas el ejemplo del curso, vers que el desplazamiento a la posicin y el
zoom al nivel indicados se hacen de forma instantnea sin ningn tipo de animacin.
Para mejorar la sensacin de movimiento en el mapa, la API de Google nos ofrece otra
serie de mtodos que permiten desplazar el mapa a una posicin especfica de forma
progresiva y aumentar o disminuir el nivel de zoom de forma animada. El mtodo
animateTo(GeoPoint) desplaza el mapa hasta un punto determinado y los mtodos zoomIn()
y zoomOut() aumentan o disminuyen de forma progresiva, respectivamente, en 1 el nivel de
zoom. En el botn animarBtn hemos usado este mtodo para desplazar de forma animada el
mapa.
Para acabar, disponemos de otro mtodo que permite desplazar el mapa un
determinado nmero de pixeles en cierta direccin, tal y como puede hacer un usuario con el
dedo sobre el mapa. Este mtodo se llama scrollBy() y recibe como parmetros el nmero de
pixeles que queremos desplazarnos en horizontal y en vertical. En el botn moverBtn hemos
usado este mtodo para desplazar el mapa automticamente.
Finalmente, ten en cuenta que, para ejecutar la aplicacin del ejemplo sobre el
emulador de Android, hay que modificar el fichero AndroidManifest.xml. Es necesario
especificar que hacemos uso de la API de Google Maps mediante la clusula <uses-library> y,
en segundo lugar, hay que solicitar los permisos de acceso a Internet mediante la clusula
<uses-permission>.
Veamos el aspecto que tiene este fichero:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad8.eje2.mapas"
android:versionCode="1"

416

Android Avanzado

android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<uses-library android:required="true"
android:name="com.google.android.maps">
</uses-library>
<activity
android:label="@string/app_name"
android:name=".MapasActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Mapas) de la Unidad 8. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que hemos
utilizado un mapa.

Si ejecutas la aplicacin en el emulador de Android, vers que tiene el siguiente


aspecto:

417

8.4

DESARROLLO DE APLICACIONES SENSIBLES A LA ORIENTACIN


DEL DISPOSITIVO

Si has usado alguna vez un telfono con Android, vers que, al cambiar la orientacin
del mismo de vertical a horizontal y viceversa, normalmente se modifica el aspecto de la
aplicacin que ests usando distribuyndose las Vistas de la interfaz de usuario de forma
acorde.
Aunque a priori este cambio de orientacin del dispositivo parece sencillo, a veces los
desarrolladores de aplicaciones Android deben desarrollar complejos cdigos para controlarlo.
Este apartado describe cmo implementar esta funcionalidad.
Por ejemplo, si tenemos abierta la aplicacin de Contactos de Android y cambiamos la
orientacin del telfono de vertical a horizontal, la aplicacin modifica el aspecto de la interfaz
del usuario proporcionalmente:

418

Android Avanzado

Hay dos formas de controlar el cambio de orientacin del dispositivo Android:

Automtica: dejamos a Android que haga todo la tarea y definimos el fichero


de diseo xml que debe aplicar para cada tipo de orientacin vertical (portrait) u
horizontal (landscape).

Manual: controlamos con sentencias Java qu diseo debe cargar en cada


momento.

ATENCIN
Para cambiar la orientacin del emulador de Android
[BLOQUE_NUM_7], [Ctrl+F11], [BLOQUE_NUM_9], [Ctrl+F12].

podemos

usar

las

teclas

Ten en cuenta que el cambio de orientacin puede tardar unos segundos en el emulador
dependiendo de la capacidad del PC con el que trabajes.

En el Ejemplo 3 de esta Unidad vamos a mostrar cmo funcionan las dos formas de
controlar el cambio de orientacin del dispositivo Android.

Nota sobre Android 2.3.3


Hasta ahora en el curso hemos usado la versin 2.3.3 de Android en el emulador de
dispositivos. Cuando se ha escrito este texto, esta versin tiene un Bug al cambiar la
orientacin del emulador de horizontal a vertical (no informa al emulador de la nueva
orientacin y mantiene la horizontal).

419

Por lo tanto, hay que probar el Ejemplo 3 de esta Unidad en otra versin de Android.
Tal y como hemos hecho para la versin 2.3.3 en la Instalacin del curso, hay que descargar
las libreras de Android 2.2 y crear el dispositivo virtual correspondiente:

Tambin puedes usar la versin de Android del curso teniendo en cuenta que puedes
cambiar al modo horizontal, pero no volver de nuevo al vertical.

8.4.1

Cambio de orientacin automtica


Se trata de una forma muy fcil de personalizar la interfaz de usuario en funcin de la

orientacin de la pantalla del dispositivo. Consiste en crear una carpeta de diseo separada
(/res/layout) que contenga los archivos XML que determinan la interfaz de usuario en cada tipo
de orientacin.
Para definir el modo horizontal (landscape), hay que crear la carpeta res/layout-land.
Esta nueva carpeta contiene tambin el archivo main.xml:

Tambin se puede aplicar el nombre de extensin -land a la carpeta drawable donde


estn las imgenes de la aplicacin. Por ejemplo, la carpeta res/drawable-land contiene
imgenes que se han diseado teniendo en cuenta el modo horizontal, mientras que los
albergados en la carpeta res/drawable estn diseados para el modo vertical:

420

Android Avanzado

El archivo main.xml incluido en la carpeta /res/layout define la interfaz de usuario para


el modo vertical del dispositivo, mientras que el archivo main.xml de la carpeta /res/layoutland define la interfaz de usuario en el modo horizontal.
A continuacin, se muestra el contenido del archivo main.xml de la carpeta
/res/layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.85"
android:gravity="center_horizontal" >

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="10dp"
android:layout_marginTop="32dp"
android:text="Esta aplicacin muestra cmo
orientacin del dispositivo Android.

controlar

el

cambio

de

\n\nPara cambiar la orientacin del emulador de Android puede


usar las teclas [BLOQUE_NUM_7],
[Ctrl+F11], [BLOQUE_NUM_9], [Ctrl+F12] de tu ordenador"
android:textAppearance="?android:attr/textAppearanceMedium" />

421

<Button
android:id="@+id/boton1"
android:layout_width="150px"
android:layout_height="60px"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textView1"
android:layout_marginLeft="100dp"
android:layout_marginTop="93dp"
android:text="Botn" />

<EditText
android:id="@+id/editText"
android:layout_width="197dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/boton1"
android:layout_marginLeft="50dp"
android:layout_marginTop="150dp" />

</RelativeLayout>

Ahora vamos a ver el contenido del archivo main.xml de la carpeta /res/layout-land:

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.85"
android:gravity="center_horizontal" >

<TextView
android:id="@+id/textView1"

422

Android Avanzado

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:text="Esta aplicacin muestra cmo
orientacin del dispositivo Android.

controlar

el

cambio

de

\n\nPara cambiar la orientacin del emulador de Android puede


usar las teclas [BLOQUE_NUM_7],
[Ctrl+F11], [BLOQUE_NUM_9], [Ctrl+F12] de tu ordenador"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/boton1"
android:layout_width="150px"
android:layout_height="60px"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textView1"
android:layout_marginLeft="100dp"
android:layout_marginTop="30dp"
android:text="Botn" />

<EditText
android:id="@+id/editText"
android:layout_width="197dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/boton1"
android:layout_marginLeft="50dp"
android:layout_marginTop="70dp" />

</RelativeLayout>

423

Si no creas este archivo para el modo horizontal y ejecutas la aplicacin, vers que al
cambiar al modo horizontal desaparece el componente TextView:

Sin embargo, si creamos el archivo de diseo horizontal, cuando cambiemos la


orientacin del dispositivo, Android cambiar automticamente el diseo de la pantalla:

8.4.2

Mantener la informacin del estado durante el cambio de orientacin


Si en el ejemplo anterior escribes algo en el TextView y, a continuacin, cambias la

orientacin del dispositivo virtual, vers que el texto escrito en este componente se mantiene
sin aadir nuevo cdigo Java.
En el apartado "Guardar y recuperar el estado de una Actividad" de la Unidad 3 hemos
estudiado que, cuando cambia la orientacin de la pantalla (vertical/horizontal), Android reinicia
la Actividad usando el mtodo OnDestroy() e inmediatamente llama de nuevo a onCreate().
Este comportamiento de reinicio est diseado para que la aplicacin se adapte a la nueva
configuracin de forma automtica, y as cambiar la posicin de los componentes.

424

Android Avanzado

La mejor manera de manejar un cambio de configuracin de este tipo para preservar el


estado de la aplicacin es usar los mtodos onSaveInstanceState() y onCreate().
Lo primero que hay que tener en cuenta es que es imprescindible establecer el atributo
android:id de todas las Vistas de la actividad. Este atributo es indispensable para que Android
guarde automticamente el contenido de las Vistas cuando cambia la orientacin de la pantalla
y se destruye la Actividad.
Por ejemplo, si un usuario ha introducido un texto en una Vista de tipo EditText y
cambia la orientacin del dispositivo, si este EditText tiene asignado un valor al atributo
android:id, Android mantendr el texto existente y lo restaurar de forma automtica cuando la
actividad se vuelva a recrear. Si, por el contrario, la Vista de tipo EditText no tiene definido el
atributo android:id, el sistema no podr conservar el texto y cuando se recree la actividad, el
texto se perder.
Android invoca el mtodo onSaveInstanceState() cuando una Actividad est a punto
de ser destruida o va a pasar a un segundo plano. Por ejemplo, cuando se cambia la
orientacin de la pantalla, se invoca este mtodo para que se pueda guardar el estado actual
de la actividad y poder restaurarlo ms tarde.
Hay otro procedimiento que permite sustituir el evento onSaveInstanceState() para
guardar informacin extra necesaria en la Actividad y restaurarla cuando se recree. Por
ejemplo, el siguiente cdigo muestra cmo guardar la orientacin actual del dispositivo sin usar
el evento onSaveInstanceState():

* Se llama a este evento cuando Android inicia un cambio de orientacin.


* CUIDADO! Para que el cambio se haga de forma AUTOMTICA debemos delegarle
* esta funcionalidad. Esto se consigue quitando del archivo
* AndroidManifest.xml el atributo android:configChanges="orientation..."
*
* Si controlamos de forma MANUAL el cambio de orientacin, debes comentar
* este mtodo.
*/
@Override
public void onSaveInstanceState(Bundle outState)
{
// Obtenemos la orientacin actual del dispositivo
String texto="";
// Conectamos con el servicio de ventanas de Android y obtenemos los datos
de la pantalla principal
Display display = ((WindowManager)
getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

425

int orientation = display.getRotation();


if ((orientation==Surface.ROTATION_90) ||
(orientation==Surface.ROTATION_270))
texto="vertical";
else texto="horizontal";

// Guardamos una informacin del estado


outState.putString("dato", texto);
super.onSaveInstanceState(outState);
}

Cuando la actividad se vuelve a recrear, Android invoca primero el mtodo OnCreate(),


seguido por el mtodo onRestoreInstanceState(). Este ltimo mtodo permite recuperar el
estado de ejecucin guardado previamente:

* Se llama a este evento cuando Android inicia un cambio de orientacin.


* CUIDADO! Para que el cambio se haga de forma AUTOMTICA debemos delegarle
* esta funcionalidad. Esto se consigue quitando del archivo
* AndroidManifest.xml el atributo android:configChanges="orientation..."
*
* Si controlamos de forma MANUAL el cambio de orientacin debes comentar
* este mtodo.
*/
@Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
// Recuperamos la informacin del EditText
if (savedInstanceState.containsKey("dato"))
Toast.makeText(this, "Orientacin anterior: " +
savedInstanceState.getString("dato"), Toast.LENGTH_SHORT).show();
}

Hemos visto que el mtodo onSaveInstanceState() es til para guardar la informacin


del estado de ejecucin de una Actividad, aunque tiene la limitacin de que slo se puede
426

Android Avanzado

guardar informacin usando el objeto de tipo Bundle. No permite guardar estructuras de datos
ms complejas, como objetos.
Para estos casos, podemos usar el mtodo onRetainNonConfigurationInstance().
Este mtodo se activa cuando una actividad est a punto de ser destruida debido a un cambio
de configuracin, como un cambio de orientacin de la pantalla. Este mtodo permite guardar
una estructura de datos devolviendo un objeto como resultado de su ejecucin. Fjate en el
siguiente ejemplo:
@Override
public Object onRetainNonConfigurationInstance()
{
// Devolvemos un objeto donde hemos guardado un estado de ejecucin
return(objeto);
}

Fjate que el mtodo anterior devuelve el tipo objeto (Object), lo que permite
prcticamente devolver cualquier tipo de dato.
Para extraer los datos guardados se puede usar dentro del mtodo onCreate() el
mtodo getLastNonConfigurationInstance(). Por ejemplo, as:

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Recuperamos el objeto original
Objeto objeto = (Objeto) getLastNonConfigurationInstance();
}

8.4.3 Cambio de orientacin Manual


Hay casos en los que es necesario controlar el proceso de creacin-destruccin de una
aplicacin cuando se cambia la orientacin del dispositivo y no queremos que Android lo haga
de manera automtica.
En este caso, hay que especificar el atributo android:configChanges del elemento
<activity> en el archivo AndroidManifest.xml:

427

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad8.eje3.orientacion"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="8" />

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<!-- CUIDADO! Para que el cambio lo haga Android debemos permitirle que
gestione esta funcionalidad. Esto se consigue
quitando el atributo android:configChanges="orientation..." -->
<activity
android:label="@string/app_name"
android:name=".OrientacionActivity"
android:configChanges="orientation|keyboardHidden" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

El atributo anterior indica que la Actividad gestiona los cambios de orientacin


ocultando el teclado cuando este cambio ocurre. Adems, cuando este giro del dispositivo
ocurre, Android invoca el mtodo onConfigurationChanged(), en el que se puede volver a
dibujar la interfaz de usuario de la Actividad:

/* Se llama a este evento cuando Android cuando cambia la orientacin


* del dispositivo. CUIDADO! Para que este evento se invoque debemos

428

Android Avanzado

* gestionar de forma MANUAL la funcionalidad de cambio de orientacin.


* Esto se consigue aadiendo en el archivo AndroidManifest.xml el atributo
* android:configChanges="orientation..."
*
* Si controlamos de forma MANUAL el cambio de orientacin, ya no son
* necesarios los mtodos onSaveInstanceState() y onRestoreInstanceState()
* y debemos comentarlos.
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Si controlamos el cambio, tambin hay que guardar los contenidos de
los componentes visuales
String texto = et.getText().toString();
if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this,

"Cambio

a
horizontal",
Toast.LENGTH_SHORT).show();

setContentView(R.layout.main);
} else {
Toast.makeText(this,

"Cambio

a
vertical",
Toast.LENGTH_SHORT).show();

setContentView(R.layout.main);
}
//Obtenemos una referencia a las Vistas de la Actividad
et = (EditText)findViewById(R.id.editText);
//

Escribimos
orientacin

el

texto

que

tena

el

EditText

antes

del

cambio

de

et.setText(texto);
}

8.4.4 Cambiar la orientacin de la pantalla con sentencias Java

429

En ocasiones, es necesario asegurarse de que una aplicacin se muestra siempre en


una orientacin concreta. Por ejemplo, muchos juegos slo se visualizan bien en modo
horizontal. En este caso, mediante sentencias Java, se puede cambiar la orientacin de la
pantalla con el mtodo setRequestOrientation() de la clase de Activity:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//Obtenemos una referencia al EditText de la Actividad
et = (EditText)findViewById(R.id.editText);
}

Adems de utilizar el mtodo setRequestOrientation() para cambiar la orientacin de


la pantalla, tambin se puede utilizar el atributo android:screenOrientation dentro del
elemento <activity> en el archivo AndroidManifest.xml. Fjate en el siguiente ejemplo:

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad8.eje3.orientacion"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="8" />

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".OrientacionActivity"
android:screenOrientation="landscape"
<intent-filter >

430

>

Android Avanzado

<action android:name="android.intent.action.MAIN" />


<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Orientacin) de la Unidad 8. Estudia el


cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que mostramos
cmo manejar la orientacin de la pantalla de un dispositivo Android.

Nota: por defecto, el Ejemplo 3 funciona en modo Manual. Si quieres cambiar a

modo

automtico, debes modificar el archivo AndroidManifest.xml del proyecto.


Atencin: el emulador de Android no funciona muy bien a la hora de simular el cambio de
orientacin del dispositivo. Dependiendo de la versin de Android, algunos cambios de
orientacin no se pueden hacer o, a veces, un giro emulado del dispositivo destruye la
Actividad dos veces antes de cambiar la orientacin del terminal.
Sin embargo, la teora y funciones aqu expuestas s son vlidas para un dispositivo real que
funcionar correctamente segn lo esperado.

8.5

DESPLEGAR
APLICACIONES
VIRTUALES (AVD) O REALES

ANDROID

EN

DISPOSITIVOS

Para poder desplegar aplicaciones compiladas (tienen la extensin .apk), primero


debemos conectar un dispositivo real por USB o arrancar un dispositivo virtual desde Eclipse.
Esto es muy til si queremos ver en funcionamiento los modelos de las actividades
obligatorias de este curso, ya que nicamente se entregan compiladas.

Para arrancar manualmente un dispositivo virtual desde Eclipse hay que pulsar el
siguiente botn de la barra de herramientas:

Desde la ventana de

dispositivos

virtuales

seleccionamos

el

dispositivo

que

deseamos arrancar y pulsamos el botn "Start":


431

A continuacin, arrancar el dispositivo virtual.


Si queremos instalar la aplicacin en un dispositivo real de Android, no es necesario
iniciar ningn dispositivo virtual.

Nota: en el caso de algunos dispositivos reales, dependiendo de la marca de dispositivo


Android, puede ser necesario instalar los drivers para que el sistema operativo lo reconozca
correctamente.

Adems, el dispositivo real debe estar configurado para admitir la instalacin de aplicaciones
sin firmar por el Android Market. Si accedes en Ajustes->Aplicaciones debes marcar la
siguiente opcin:

432

Android Avanzado

Una vez disponemos de un dispositivo (real o virtual) de Android ejecutndose o


conectado por USB al PC, abrimos una consola de Windows (o del sistema operativo
correspondiente) y utilizamos la utilidad adb.exe (Android Debug Bridge) situada en la carpeta
platform-tools del SDK de Android.
En primer lugar, consultamos todos los identificadores de los dispositivos en ejecucin
mediante el comando "adb devices". Este comando debe devolver todas las instancias con los
dispositivos abiertos:

Los dispositivos que aparezcan con la etiqueta "emulator-xxx" son dispositivos


virtuales y los que muestren otra etiqueta son dispositivos reales (telfonos, tablets, etctera).
Adems, los dispositivos que aparezcan con la etiqueta "offline" estn conectados,
pero no estn disponibles al ADB (Android Debug Bridge).
Para este ejemplo, hemos seleccionado el dispositivo emulator-5556 que corresponde
al dispositivo virtual con Android 2.3.3 para instalar una aplicacin. Puedes ver el "id" del
dispositivo en la ventana del emulador:

433

Tras obtener este identificador del emulador, vamos a instalar la aplicacin mediante el
comando adb -s identificador-del-emulador install nombre-fichero-apk. Fjate en el
siguiente ejemplo:

Una vez instala la aplicacin, tenemos que ejecutarla en el dispositivo buscando su


icono en la pantalla de aplicaciones:

434

Android Avanzado

Hacemos clic en el icono de la aplicacin para ver su resultado:

8.6

CMO PUBLICAR APLICACIONES EN EL ANDROID MARKET

El Android Market (en espaol Mercado de Android) es una tienda de software en


lnea desarrollada por Google para los dispositivos Android. Es una aplicacin que est
preinstalada en la mayora de los dispositivos Android y que permite a los usuarios buscar y
descargar aplicaciones publicadas por terceros desarrolladores. Los usuarios tambin pueden
buscar y obtener informacin sobre aplicaciones a travs de una pgina Web.
Las aplicaciones en el Android Market pueden ser gratuitas o de pago. En este
apartado vamos a tratar cmo publicar una aplicacin gratuita.
En este apartado vamos a explicar los pasos para publicar una aplicacin en el Android
Market.

8.6.1

Alta de cuenta de desarrollador en el Android Market


El primer paso obligatorio es darse de alta como desarrollador en el Android Market.

Para ello, necesitamos disponer de una cuenta de Google (GMail).


Con el navegador de Internet accederemos a la direccin:
435

http://market.android.com/publish
En esta pgina introducimos el usuario y la contrasea de Google:

La primera vez que accedemos a la pgina se muestra un asistente para dar de alta
una nueva cuenta de desarrollador en el Android Market. Introducimos los datos que se
solicitan (nombre del desarrollador, correo electrnico, URL del sitio Web y nmero de
telfono). Despus, pulsamos el enlace "Seguir":

Para poder darnos de alta como desarrolladores del Android Market y publicar
aplicaciones, hay que abonar 25,00$. Se trata de una cuota nica sin caducidad. Para pagar

436

Android Avanzado

esta cuota podemos usar el servicio Google Checkout o pulsar en "Continuar" para pagar con
tarjeta de crdito:

A continuacin, aparece el detalle de la factura con el artculo "Android - Developer


Registration Free for xxx". En esta pgina introducimos los datos de nuestra tarjeta de crdito
para realizar el pago, as como la direccin postal de facturacin donde llegar la
correspondiente factura por correo ordinario:

437

Si todo est correcto, el asistente mostrar la siguiente ventana, indicando que "Su
pedido se ha enviado al Android Market". Para continuar con el proceso, pulsamos en el enlace
"Vuelve al sitio de desarrolladores de Android Market para completar el registro":

Despus, leemos la licencia de desarrollador para el Android Market. Si estamos de


acuerdo, hacemos clic en el enlace "Acepto las condiciones y deseo asociar la tarjeta de
crdito y la cuenta que he registrado anteriormente al Acuerdo de distribucin para
desarrolladores de Android Market". Pulsamos "Acepto. Continuar":

438

Android Avanzado

El asistente indicar que el registro ha concluido, con el mensaje "Se ha aprobado tu


registro en Android Market. Ahora puedes subir y publicar aplicaciones de software en Android
Market". A partir de este momento ya podremos usar nuestra cuenta para publicar aplicaciones:

8.6.2

Recomendaciones sobre aplicaciones para Android Market


Cuando desarrollemos aplicaciones que vamos a publicar en el Android Market,

debemos prestar especial atencin a una serie de caractersticas.


8.6.2.1

Recomendaciones sobre aplicaciones para Android Market

Antes de empezar a desarrollar aplicaciones Android que vamos a publicar en el


Market, hay que saber que cuando un usuario realiza una bsqueda de una aplicacin en el
Market usando su dispositivo Android, slo le aparecern las aplicaciones que cumplan los
filtros (de permisos y de caractersticas del dispositivo) y el nivel de API (API Level) indicados
en el archivo AndroidManifest.xml.
El "API Level" es la versin de Android compatible con la aplicacin. Por ejemplo,
durante el curso hemos usado la versin 2.3.3 de Android que corresponde con el "API Level"
10. Si publicamos una aplicacin desarrollada con esta versin de Android, nicamente ser
visible y slo podr instalarse en dispositivos con una versin igual o superior a la 2.3.3 de
Android.

439

Los filtros de permisos permiten a una aplicacin solicitar acceso a recursos de


Android. Ya hemos estudiado que si, por ejemplo, una aplicacin requiere acceder a la cmara
de fotos, debemos indicarlo en el archivo AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
Al Indicar estos permisos, esta aplicacin no aparecer en las bsquedas realizadas
desde dispositivos Android que no dispongan de cmara de fotos. Es decir, si solicitamos
acceder a un recurso (cmara, wifi, bluetooth, etctera) que el dispositivo no tiene, la aplicacin
no ser visible en el Market.
Adems, existen otros filtros con las caractersticas del dispositivo en el archivo
AndroidManifest.xml que hacen que la aplicacin aparezca o no en el Market para un
dispositivo determinado:

<supports-screens>: establece el tipo de pantalla (resolucin mnima) que


necesita la aplicacin para funcionar.

<uses-feature>: especifica el uso de caractersticas del dispositivo, por


ejemplo:
o

Para utilizar Bluetooth:


<uses-feature android:name="android.hardware.bluetooth" />

Para usar la cmara:


<uses-feature android:name="android.hardware.camera" />

<uses-library>: indica las libreras especficas que requiere la aplicacin.

Es importante tener en cuenta que cuanto mayores sean los requisitos de hardware (cmara,
bluetooth, GPS, brjula, sensor de movimiento, etctera), la aplicacin ser visible e instalable
en un menor nmero de dispositivos Android.

8.6.2.2

Buenas prcticas para el desarrollo de aplicaciones Android

A continuacin, mostramos algunas recomendaciones a la hora de desarrollar


aplicaciones Android tiles, profesionales y fiables:

Siempre hay que tener en cuenta que estamos desarrollando aplicaciones para
dispositivos con pantalla muy pequea, si son telfonos, lo que no ocurre en
los tablets, y teclado limitado, por lo que las aplicaciones deberan mostrar
pocos campos de texto y opciones reducidas.

Antes de desarrollar una aplicacin Android, es recomendable buscar en el


Market si ya existe una aplicacin similar. Si queremos que nuestra aplicacin
sea til para los usuarios, debe ser interesante, original y sencilla incorporando
funciones que no tengan otras.

440

Android Avanzado

Hay que procurar, en la medida de lo posible, desarrollar aplicaciones que se


puedan instalar en el mayor nmero posible de dispositivos para que tenga
ms difusin. Por lo tanto, debemos realizar aplicaciones con la versin de
Android mnima y los requisitos de hardware bsicos.

Las aplicaciones deben ser rpidas. Si es necesario realizar algn proceso que
pueda tardar unos segundos, es recomendable avisar al usuario o, incluso,
usar hilos de ejecucin, servicios, etctera. El usuario de un dispositivo mvil
espera siempre rapidez de respuesta.

8.6.3

Generar fichero APK con certificado para Android Market


Cuando compilamos un proyecto Android al hacer "Run" en Eclipse, el fichero .apk

(paquete de instalacin de la aplicacin Android) generado dentro del directorio /bin no es


vlido para subirlo directamente al Android Market. Si intentamos subir este fichero
directamente aparecer este mensaje:

Market does not accept apks signed with the debug certificate. Create a new certificate
that is valid for at least 50 years. Market requires that the certificate used to sign the apk be
valid until at least October 22, 2033. Create a new certificate. Market requires the
minSdkVersion to be set to a positive 32-bit integer in AndroidManifest.xml.
Uno de los requisitos para poder publicar de aplicaciones en Android Market es
que el paquete de instalacin APK debe estar firmado con un certificado vlido de al
menos 25 aos. A continuacin, explicamos cmo hacerlo.
En primer lugar, una vez desarrollada y probada la aplicacin Android con Eclipse,
hacemos clic con el botn derecho del ratn sobre la carpeta del proyecto y seleccionamos la
opcin "Export" del men emergente:

441

Abrimos la carpeta "Android" y seleccionamos "Export Android Application";


despus, pulsamos el botn "Next":

En la ventana siguiente, en el campo "Project", podemos seleccionar otro proyecto si


nos hemos equivocado. Pulsamos de nuevo el botn "Next":

442

Android Avanzado

A continuacin, si no disponemos de una clave, seleccionamos la opcin "Create new


keystore". Introducimos un directorio y nombre para el almacn de claves, por ejemplo
C:\cursos_Mentor\Android\claves.android. Introducimos la contrasea para el almacn de
claves:

Si ya disponemos de un almacn de claves, seleccionamos "Use existing keystore" y


seleccionamos el certificado escribiendo la clave correspondiente.
Pulsamos el botn "Next" para seguir.
A continuacin, escribimos los datos administrativos de la clave que vamos a crear
para certificar nuestras aplicaciones:
443

Alias: identificador de la clave.

Password: contrasea de la clave, debemos guardarla o recordarla pues la


necesitaremos cada vez que vayamos a publicar una nueva aplicacin o
actualizar una ya existente en el Android Market.

Confirm: reescribimos la contrasea anterior.

Validity (years): validez del certificado, al menos 25 aos.

First and Last Name: nombre del desarrollador o de la empresa.

Organization Unit: departamento.

Organization: nombre de la empresa.

City or Locality: ciudad.

State or Province: provincia.

Country Code: cdigo postal de la ciudad.

Tras introducir los datos pulsamos el botn "Next":


A continuacin, indicamos la carpeta y el nombre del paquete APK compilado que se
firma con el certificado anterior y que ser el fichero que finalmente subiremos al Android
Market. En es caso hemos seleccionado la carpeta C:\cursos_Mentor\Android\androidmarket del curso:

444

Android Avanzado

Si hemos seguido bien los pasos anteriores, ya dispondremos del fichero APK firmado
con el certificado que podemos publicar en el Android Market:

8.6.4

Publicar una aplicacin Android en el Android Market


Vamos a explicar cmo publicar una aplicacin firmada con el certificado para que

aparezca en Android Market y los usuarios puedan descargarla e instalarla.


Accedemos a la web de Android Market con la cuenta de desarrollador que hemos
dado de alta anteriormente escribiendo en la barra de direcciones del navegador:

https://market.android.com/publish/Home
Pulsamos en el enlace "Subir aplicacin":

445

Aparecer una pagina donde podemos seleccionar el fichero APK pulsando en


"Examinar" para elegir el fichero APK de nuestra aplicacin Android firmada con el certificado:

Pulsamos en el botn "Publicar" para subirla al Android Market.


Si el paquete APK est correcto y cumple con todos los requisitos (versin de Android,
certificado, compilacin, etctera), el asistente muestra el botn "Guardar" y los datos del APK
(nombre de la aplicacin, nombre de la versin, cdigo de la versin, permisos que necesita,
funciones que necesita, tamao, nombre de la clase Java). Pulsamos el botn "Guardar" para
almacenar la aplicacin:

446

Android Avanzado

Tras subirlo, pulsamos en el enlace "Activar" para introducir los datos necesarios para
publicar la aplicacin en el Android Market. Desde esta pgina podemos activar o desactivar la
publicacin de las aplicaciones subidas. Por ejemplo, si hemos detectado algn error y no
queremos que los usuarios se descarguen una aplicacin hasta solucionar el problema,
podremos desactivarla:

Si pulsamos el botn "Activar", a continuacin, aparece una ventana donde debemos


aadir todos los datos requeridos en la pestaa "Informacin de producto" para acabar de dar
de alta la nueva aplicacin:

Capturas de pantalla de la aplicacin: al menos debemos subir dos capturas;


es recomendable que tengan buena calidad, para que el usuario se haga una
idea del aspecto que tiene la aplicacin.
447

Icono de la aplicacin: la aplicacin se identifica con un icono que aparece en


la parte izquierda de la pantalla del Android Market cuando los usuarios buscan
aplicaciones.

Imagen promocional, imagen de funciones y vdeo promocional de


Youtube: son datos opcionales que sirven para incluir ms informacin de la
aplicacin.

Si no deseamos que la aplicacin se anuncie fuera de Android Market,


marcamos la Casilla: "No promocionar mi aplicacin salvo en Android Market y
en los sitios web o para mviles propiedad de Google. Asimismo, soy
consciente de que cualquier cambio relacionado con esta preferencia puede
tardar sesenta das en aplicarse".

Podemos elegir varios idiomas para escribir la descripcin de las funciones y


uso de la aplicacin. El ingls es obligatorio. En este punto se solicitan los
campos:

Ttulo de la aplicacin: nombre que aparece en las bsquedas, no


debe ser muy largo (inferior a 30 caracteres).

Descripcin: descripcin detallada (hasta 4000 caracteres) de la


funcionalidad de la aplicacin.

Cambios recientes: si se trata de una actualizacin, podemos indicar


aqu las ltimas mejoras implementadas.

Si hemos incluido un vdeo promocional, podemos aadir un texto


promocional.

Tipo de aplicacin: seleccionamos en el desplegable el tipo que ms


se ajuste a la funcionalidad de la aplicacin.

Categora: seleccionamos en el desplegable la categora que ms se


ajuste a la aplicacin.

Proteccin contra copias: lo usual es que no est seleccionada esta opcin,


ya que, como indica Android Market, esta funcin quedar obsoleta en breve,
siendo sustituida por el servicio de licencias.

Clasificacin del contenido: marcamos si nuestra aplicacin es para todos


los pblicos o contiene algn tipo de contenido para mayores.

Precios: aqu indicamos si la aplicacin es gratuita o de pago.

Precio predeterminado: si hemos elegido de pago, en este campo


introducimos el precio de la aplicacin. Pulsando el botn "Autocompletar" har
los ajustes para los diferentes pases en los que queramos publicarla.

448

Android Avanzado

Tambin se indica el nmero aproximado de modelos de dispositivos


Android sobre los que se podr instalar la aplicacin en funcin de los
filtros indicados en el archivo de manifiesto.

Informacin de contacto:
o

Sitio web.

Correo electrnico.

Telfono.

En la siguiente ventana se muestra parte de los datos que hay que incluir:

Una vez introducidos los datos, pulsamos en el botn "Guardar" de la parte superior
derecha. A continuacin, se comprueba si los datos son completos y correctos y, si no hay
errores, se guardarn los datos asociados al archivo APK.
449

Despus, pulsamos en el botn "Publicar" (a la izquierda del botn "Guardar") para


publicar definitivamente la aplicacin en Android Market:

Tras finalizar la publicacin, se mostrar en "Todos los elementos de Android Market"


la nueva aplicacin con el estado "Publicada". En esta pgina podemos llevar a cabo un
seguimiento del nmero de instalaciones, posibles errores, comentarios de los usuarios,
popularidad, etctera.

450

Android Avanzado

La Depuracin de programas (en ingls Debug) es el proceso de identificar y


corregir errores de programacin en tiempo de ejecucin.
Un Punto de interrupcin (Breakpoint en ingls) es una marca en el cdigo fuente
que pausa la ejecucin de un programa, para que el programador pueda evaluar los
valores asignados a las variables y detectar errores en tiempo de ejecucin.
El entorno de desarrollo Eclipse permite llevar a cabo de manera sencilla la
Depuracin de programas.
Es posible incluir mapas en las aplicaciones de Android haciendo uso de la API
Android de Google Maps.
Para poder utilizar la API de Google Maps, es necesario disponer de una clave de
uso (API Key) que estar asociada al certificado con el que firmamos digitalmente
las aplicaciones.
El Android Market (en espaol Mercado de Android) es una tienda de software en
lnea desarrollada por Google para los dispositivos Android.
Para poder publicar aplicaciones en el Android Market, es necesario darse de alta
y pagar una cuota.
El paquete de instalacin APK de una aplicacin del Android Market debe estar
firmado con un certificado vlido de al menos 25 aos.

451