Sunteți pe pagina 1din 162

Para Carlos, Daniel y

Rosalinda Curso
Programacin
Android
Parte 2

Rosalinda Hernndez
Tabla de contenido
Localizacin geogrfica en Android ................................................................................................................................ 2
Localizacin geogrfica en Android (I) ........................................................................................................................ 2
Localizacin geogrfica en Android (II) ....................................................................................................................... 9
Content Providers en Android ...................................................................................................................................... 15
Content Providers en Android (I): Construccin ....................................................................................................... 15
Content Providers en Android (II): Utilizacin .......................................................................................................... 25
Notificaciones en Android ............................................................................................................................................ 31
Notificaciones en Android (I): Toast.......................................................................................................................... 31
Notificaciones en Android (II): Barra de Estado ........................................................................................................ 36
Notificaciones en Android (III): Dilogos ................................................................................................................... 39
Depuracin en Android ................................................................................................................................................. 47
Depuracin en Android: Logging............................................................................................................................... 47
Acceso a Servicios Web ................................................................................................................................................ 50
Acceso a Servicios Web SOAP en Android (1/2)........................................................................................................ 50
Acceso a Servicios Web SOAP en Android (2/2)........................................................................................................ 59
Acceso a Servicios Web REST en Android (1/2) ........................................................................................................ 71
Acceso a Servicios Web REST en Android (2/2) ........................................................................................................ 78
Tareas en segundo plano en Android ........................................................................................................................... 87
Tareas en segundo plano en Android (I): Thread y AsyncTask .................................................................................. 87
Tareas en segundo plano en Android (II): IntentService ........................................................................................... 96
Google Play Services ................................................................................................................................................... 100
Google Play Services: Introduccin y Preparativos ................................................................................................. 100
Google Play Services: Mapas en Android .................................................................................................................... 103
Mapas en Android (Google Maps Android API v2) I ............................................................................................. 103
Mapas en Android (Google Maps Android API v2) II ............................................................................................ 110
Mapas en Android (Google Maps Android API v2) III ........................................................................................... 115
Google Play Services: Notificaciones Push Android .................................................................................................... 122
Notificaciones Push Android: Google Cloud Messaging (GCM). Introduccin ........................................................ 122
Notificaciones Push Android: Google Cloud Messaging (GCM). Implementacin Servidor .................................... 126
Notificaciones Push Android: Google Cloud Messaging (GCM). Implementacin Cliente (Nueva Versin) ............ 134
Integracin con Google+ ............................................................................................................................................. 147
Integracin con Google+ (I): Google+ Sign-In.......................................................................................................... 147
Integracin con Google+ (II): Datos de perfil y Crculos .......................................................................................... 158
Localizacin geogrfica en Android
Localizacin geogrfica en Android (I)
by Sgoliver on 27/04/2011 in Android, Programacin

La localizacin geogrfica en Android es uno de esos servicios que, a pesar de requerir poco cdigo para ponerlos en
marcha, no son para nada intuitivos ni fciles de llegar a comprender por completo. Y esto no es debido al diseo de
la plataforma Android en s, sino a la propia naturaleza de este tipo de servicios. Por un lado, existen multitud de
formas de obtener la localizacin de un dispositivo mvil, aunque la ms conocida y popular es la localizacin por
GPS, tambin es posible obtener la posicin de un dispositivo por ejemplo a travs de las antenas de telefona mvil
o mediante puntos de acceso Wi-Fi cercanos, y todos cada uno de estos mecanismos tiene una precisin, velocidad y
consumo de recursos distinto. Por otro lado, el modo de funcionamiento de cada uno de estos mecanismos hace que
su utilizacin desde nuestro cdigo no sea todo lo directa e intuitiva que se deseara. Iremos comentando todo esto
a lo largo del artculo, pero vayamos paso a paso.

Qu mecanismos de localizacin tenemos disponibles?

Lo primero que debe conocer una aplicacin que necesite obtener la localizacin geogrfica es qu mecanismos de
localizacin (proveedores de localizacin, o location providers) tiene disponibles en el dispositivo. Como ya hemos
comentado, los ms comunes sern el GPS y la localizacin mediante la red de telefona, pero podran existir otros
segn el tipo de dispositivo.

La forma ms sencilla de saber los proveedores disponibles en el dispositivo es mediante una llamada al
mtodo getAllProviders() de la clase LocationManager, clase principal en la que nos basaremos siempre a la hora de
utilizar la API de localizacin de Android. Para ello, obtendremos una referencia al location manager llamando
a getSystemService(LOCATION_SERVICE), y posteriormente obtendremos la lista de proveedores mediante el
mtodo citado para obtener la lista de nombres de los proveedores:

1 LocationManager locManager = (LocationManager)getSystemService(LOCATION_SERVICE);

2 List<String> listaProviders = locManager.getAllProviders();

Una vez obtenida la lista completa de proveedores disponibles podramos acceder a las propiedades de cualquiera
de ellos (precisin, coste, consumo de recursos, o si es capaz de obtener la altitud, la velocidad, ). As, podemos
obtener una referencia al provider mediante su nombre llamando al mtodo getProvider(nombre) y posteriormente
utilizar los mtodos disponibles para conocer sus propiedades, por ejemplo getAccuracy() para saber su precisin
(tenemos disponibles las constantes Criteria.ACCURACY_FINE para precisin alta, y Criteria.ACCURACY_COARSE para
precisin media), supportsAltitude() para saber si obtiene la altitud, o getPowerRequirement()para obtener el nivel
de consumo de recursos del proveedor. La lista completa de mtodos para obtener las caractersticas de un
proveedor se puede consultar en la documentacin oficial de la clase LocationProvider.

1 LocationManager locManager = (LocationManager)getSystemService(LOCATION_SERVICE);

2 List<String> listaProviders = locManager.getAllProviders();

4 LocationProvider provider = locManager.getProvider(listaProviders.get(0));

5 int precision = provider.getAccuracy();

6 boolean obtieneAltitud = provider.supportsAltitude();

7 int consumoRecursos = provider.getPowerRequirement();

Al margen de esto, hay que tener en cuenta que la lista de proveedores devuelta por el
mtodogetAllProviders() contendr todos los proveedores de localizacin conocidos por el dispositivo, incluso si
stos no estn permitidos (segn los permisos de la aplicacin) o no estn activados, por lo que esta informacin
puede que no nos sea de mucha ayuda.

Qu proveedor de localizacin es mejor para mi aplicacin?

Android proporciona un mecanismo alternativo para obtener los proveedores que cumplen unos determinados
requisitos entre todos los disponibles. Para ello nos permite definir un criterio de bsqueda, mediante un objeto de
tipo Criteria, en el que podremos indicar las caractersticas mnimas del proveedor que necesitamos utilizar (podis
consultar la documentacin oficial de la clase Criteria para saber todas las caractersticas que podemos definir). As,
por ejemplo, para buscar uno con precisin alta y que nos proporcione la altitud definiramos el siguiente criterio de
bsqueda:

1 Criteria req = new Criteria();

2 req.setAccuracy(Criteria.ACCURACY_FINE);

3 req.setAltitudeRequired(true);

Tras esto, podremos utilizar los mtodos getProviders() o getBestProvider() para obtener la lista de proveedores que
se ajustan mejor al criterio definido o el proveedor que mejor se ajusta a dicho criterio, respectivamente. Adems,
ambos mtodos reciben un segundo parmetro que indica si queremos que slo nos devuelvan proveedores que
estn activados actualmente. Veamos cmo se utilizaran estos mtodos:

1 //Mejor proveedor por criterio

2 String mejorProviderCrit = locManager.getBestProvider(req, false);

4 //Lista de proveedores por criterio

5 List<String> listaProvidersCrit = locManager.getProviders(req, false);

Con esto, ya tenemos una forma de seleccionar en cada dispostivo aquel proveedor que mejor se ajusta a nuestras
necesidades.

Est disponible y activado un proveedor determinado?

Aunque, como ya hemos visto, tenemos la posibilidad de buscar dinmicamente proveedores de localizacin segn
un determinado criterio de bsqueda, es bastante comn que nuestra aplicacin est diseada para utilizar uno en
concreto, por ejemplo el GPS, y por tanto necesitaremos algn mecanismo para saber si ste est activado o no en el
dispositivo. Para esta tarea, la clase LocationManager nos proporciona otro mtodo llamado isProviderEnabled() que
nos permite hacer exactamente lo que necesitamos. Para ello, debemos pasarle el nombre del provider que
queremos consultar. Para los ms comunes tenemos varias constantes ya definidas:

LocationManager.NETWORK_PROVIDER. Localizacin por la red de telefona.

LocationManager.GPS_PROVIDER. Localizacin por GPS.

De esta forma, si quisiramos saber si el GPS est habilitado o no en el dispositivo (y actuar en consecuencia),
haramos algo parecido a lo siguiente:

1 //Si el GPS no est habilitado

2 if (!locManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {

3 mostrarAvisoGpsDeshabilitado();

4 }

En el cdigo anterior, verificamos si el GPS est activado y en caso negativo mostramos al usuario un mensaje de
advertencia. Este mensaje podramos mostralo sencillamente en forma de notificacin de tipo toast, pero en el
prximo artculo sobre localizacin veremos cmo podemos, adems de informar de que el GPS est desactivado,
invitar al usuario a activarlo dirigindolo automticamente a la pantalla de configuracin del dispositivo.

El GPS ya est activado, y ahora qu?

Una vez que sabemos que nuestro proveedor de localizacin favorito est activado, ya estamos en disposicin de
intentar obtener nuestra localizacin actual. Y aqu es donde las cosas empiezan a ser menos intuitivas. Para
empezar, en Android no existe ningn mtodo del tipo obtenerPosicinActual(). Obtener la posicin a travs de un
dispositivo de localizacin como por ejemplo el GPS no es una tarea inmediata, sino que puede requerir de un cierto
tiempo de procesamiento y de espera, por lo que no tendra sentido proporcionar un mtodo de ese tipo.

Si buscamos entre los mtodos disponibles en la clase LocationManager, lo ms parecido que encontramos es un
mtodo llamado getLastKnownLocation(String provider), que como se puede suponer por su nombre, nos devuelve
la ltima posicin conocida del dispositivo devuelta por un provider determinado. Es importante entender esto: este
mtodo NO devuelve la posicin actual, este mtodo NO solicita una nueva posicin al proveedor de localizacin,
este mtodo se limita a devolver la ltima posicin que se obtuvo a travs del proveedor que se le indique como
parmetro. Y esta posicin se pudo obtener hace pocos segundos, hace das, hace meses, o incluso nunca (si el
dispositivo ha estado apagado, si nunca se ha activado el GPS, ). Por tanto, cuidado cuando se haga uso de la
posicin devuelta por el mtodo getLastKnownLocation().

Entonces, de qu forma podemos obtener la posicin real actualizada? Pues la forma correcta de proceder va a
consistir en algo as como activar el proveedor de localizacin y suscribirnos a sus notificaciones de cambio de
posicin. O dicho de otra forma, vamos a suscribirnos al evento que se lanza cada vez que un proveedor recibe
nuevos datos sobre la localizacin actual. Y para ello, vamos a darle previamente unas indicaciones (que no ordenes,
ya veremos esto en el prximo artculo) sobre cada cuanto tiempo o cada cuanta distacia recorrida necesitaramos
tener una actualizacin de la posicin.

Todo esto lo vamos a realizar mediante una llamada al mtodo requestLocationUpdates(), al que deberemos pasar 4
parmetros distintos:

Nombre del proveedor de localizacin al que nos queremos suscribir.

Tiempo mnimo entre actualizaciones, en milisegundos.

Distancia mnima entre actualizaciones, en metros.

Instancia de un objeto LocationListener, que tendremos que implementar previamente para definir las
acciones a realizar al recibir cada nueva actualizacin de la posicin.

Tanto el tiempo como la distancia entre actualizaciones pueden pasarse con valor 0, lo que indicara que ese criterio
no se tendr en cuenta a la hora de decidir la frecuencia de actualizaciones. Si ambos valores van a cero, las
actualizaciones de posicin se recibirn tan pronto y tan frecuentemente como estn disponibles. Adems, como ya
hemos indicado, es importante comprender que tanto el tiempo como la distancia especificadas se entendern
como simples indicaciones o pistas para el proveedor (al menos para versiones no recientes de Android), por lo
que puede que no se cumplan de forma estricta. En el prximo artculo intentaremos ver esto con ms detalle para
entenderlo mejor. Por ahora nos basta con esta informacin.

En cuanto al listener, ste ser del tipo LocationListener y contendr una serie de mtodos asociados a los distintos
eventos que podemos recibir del proveedor:

onLocationChanged(location). Lanzado cada vez que se recibe una actualizacin de la posicin.

onProviderDisabled(provider). Lanzado cuando el proveedor se deshabilita.

onProviderEnabled(provider). Lanzado cuando el proveedor se habilita.

onStatusChanged(provider, status, extras). Lanzado cada vez que el proveedor cambia su estado, que puede
variar entre OUT_OF_SERVICE, TEMPORARILY_UNAVAILABLE,AVAILABLE.
Por nuestra parte, tendremos que implementar cada uno de estos mtodos para responder a los eventos del
proveedor, sobre todo al ms interesante, onLocationChanged(), que se ejecutar cada vez que se recibe una nueva
localizacin desde el proveedor. Veamos un ejemplo de cmo implementar un listener de este tipo:

1 LocationListener locListener = new LocationListener() {

3 public void onLocationChanged(Location location) {

4 mostrarPosicion(location);

5 }

7 public void onProviderDisabled(String provider){

8 lblEstado.setText("Provider OFF");

9 }

10

11 public void onProviderEnabled(String provider){

12 lblEstado.setText("Provider ON");

13 }

14

15 public void onStatusChanged(String provider, int status, Bundle extras){

16 lblEstado.setText("Provider Status: " + status);

17 }

18 };

Como podis ver, en nuestro caso de ejemplo nos limitamos a mostrar al usuario la informacin recibida en el
evento, bien sea un simple cambio de estado, o una nueva posicin, en cuyo caso llamamos al mtodo
auxiliar mostrarPosicion() para refrescar todos los datos de la posicin en la pantalla. Para este ejemplo hemos
construido una interfaz muy sencilla, donde se muestran 3 datos de la posicin (latitud, longitud y precisin) y un
campo para mostrar el estado del proveedor. Adems, se incluyen dos botones para comenzar y detener la
recepcin de nuevas actualizaciones de la posicin. No incluyo aqu el cdigo de la interfaz para no alargar ms el
artculo, pero puede consultarse en el cdigo fuentesuministrado al final del texto. El aspecto de nuestra ventana es
el siguiente:
En el mtodo mostrarPosicion() nos vamos a limitar a mostrar los distintos datos de la posicin pasada como
parmetro en los controles correspondientes de la interfaz, utilizando para ello los mtodos proporcionados por la
clase Location. En nuestro caso utilizaremos getLatitude(),getAltitude() y getAccuracy() para obtener la latitud,
longitud y precisin respectivamente. Por supuesto, hay otros mtodos disponibles en esta clase para obtener la
altura, orientacin, velocidad, etc que se pueden consultar en la documentacin oficial de la clase Location.
Veamos el cdigo:

1 private void mostrarPosicion(Location loc) {

2 if(loc != null)

3 {

4 lblLatitud.setText("Latitud: " + String.valueOf(loc.getLatitude()));

5 lblLongitud.setText("Longitud: " + String.valueOf(loc.getLongitude()));

6 lblPrecision.setText("Precision: " + String.valueOf(loc.getAccuracy()));

7 }

8 else

9 {

10 lblLatitud.setText("Latitud: (sin_datos)");

11 lblLongitud.setText("Longitud: (sin_datos)");

12 lblPrecision.setText("Precision: (sin_datos)");

13 }

14 }

Por qu comprobamos si la localizacin recibida es null? Como ya hemos dicho anteriomente, no tenemos mucho
control sobre el momento ni la frecuencia con la que vamos a recibir las actualizaciones de posicin desde un
proveedor, por lo que tampoco estamos seguros de tenerlas disponibles desde un primer momento. Por este
motivo, una tcnica bastante comn es utilizar la posicin que devuelve el mtodo getLastKnownLocation() como
posicin provisional de partida y a partir de ah esperar a recibir la primera actualizacin a travs
del LocationListener. Y como tambin dijimos, la ltima posicin conocida podra no existir en el dispositivo, de ah
que comprobemos si el valor recibido esnull. Para entender mejor esto, a continuacin tenis la estructura completa
del mtodo que lanzamos al comenzar la recepcin de actualizaciones de posicin (al pulsar el botn Activar de la
interfaz):
1 private void comenzarLocalizacion()

2 {

3 //Obtenemos una referencia al LocationManager

4 locManager =

5 (LocationManager)getSystemService(Context.LOCATION_SERVICE);

7 //Obtenemos la ltima posicin conocida

8 Location loc =

9 locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

10

11 //Mostramos la ltima posicin conocida

12 mostrarPosicion(loc);

13

14 //Nos registramos para recibir actualizaciones de la posicin

15 locListener = new LocationListener() {

16 public void onLocationChanged(Location location) {

17 mostrarPosicion(location);

18 }

19

20 //Resto de mtodos del listener

21 //...

22 };

23

24 locManager.requestLocationUpdates(

25 LocationManager.GPS_PROVIDER, 30000, 0, locListener);

26 }

Como se puede observar, al comenzar la recepcin de posiciones, mostramos en primer lugar la ltima posicin
conocida, y posteriormente solicitamos al GPS actualizaciones de poscin cada 30 segundos.

Por ltimo, nos quedara nicamente comentar cmo podemos detener la recepcin de nuevas actualizaciones de
posicin. Algo que es tan sencillo como llamar al mtodo removeUpdates() dellocation manager. De esta forma, la
implementacin del botn Desactivar sera tan sencilla como esto:

1 btnDesactivar.setOnClickListener(new OnClickListener() {

2 @Override

3 public void onClick(View v) {

4 locManager.removeUpdates(locListener);
5 }

6 });

Con esto habramos concluido nuestra aplicacin de ejemplo. Sin embargo, si descargis el cdigo completo del
artculo y ejecutis la aplicacin en el emulador veris que, a pesar funcionar todo correctamente, slo recibiris una
lectura de la posicin (incluso puede que ninguna). Esto es debido a que la ejecucin y prueba de aplicaciones de
este tipo en el emulador de Android, al no tratarse de un dispositivo real y no estar disponible un receptor GPS,
requiere de una serie de pasos adicionales parasimular cambios en la posicin del dispositivo.

Todo esto, adems de algunas aclaraciones que nos han quedado pendientes en esta primera entrega sobre
localizacin, lo veremos en el prximo artculo.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Localizacin geogrfica en Android (II)
by Sgoliver on 08/05/2011 in Android, Programacin

En el artculo anterior del curso de programacin en Android comentamos los pasos bsicos necesarios para
construir aplicaciones que accedan a la posicin geogrfica del dispositivo. Ya comentamos algunas particularidades
de este servicio de la API de Android, pero dejamos en el tintero algunas aclaraciones ms detalladas y un tema
importante y fundamental, como es la depuracin de este tipo de aplicaciones que manejan datos de localizacin. En
este nuevo artculo intentar abarcar estos temas y dejar para un tercer artculo algunas otras caractersticas ms
avanzadas de los servicios de localizacin.

Como base para este artculo voy a utilizar la misma aplicacin de ejemplo que construimos en laanterior entrega,
haciendo tan slo unas pequeas modificaciones:

Reduciremos el tiempo entre actualizaciones de posicin a la mitad, 15 segundos, para evitar tiempos de
espera demasiado largos durante la ejecucin de la aplicacin.

Generaremos algunos mensajes de log en puntos clave del cdigo para poder estudiar con ms detalle el
comportamiento de la aplicacin en tiempo de ejecucin.

La generacin de mensajes de log resulta ser una herramienta perfecta a la hora de depurar aplicaciones del tipo
que estamos tratando, ya que en estos casos el cdigo no facilita demasiado la depuracin tpica paso a paso que
podemos realizar en otras aplicaciones.

En nuestro caso de ejemplo slo vamos a generar mensajes de log cuando ocurran dos ciscunstancias:

Cuando el proveedor de localizacin cambie de estado, evento onStatusChanged(), mostraremos el nuevo


estado.

Cuando se reciba una nueva actualizacin de la posicin, evento onLocationChanged(), mostraremos las
nuevas coordenadas recibidas.

Nuestro cdigo quedara por tanto tal como sigue:

1 private void actualizarPosicion()

2 {

3 //Obtenemos una referencia al LocationManager

4 locationManager =

5 (LocationManager)getSystemService(Context.LOCATION_SERVICE);

7 //Obtenemos la ltima posicin conocida

8 Location location =

9 locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

10

11 //Mostramos la ltima posicin conocida

12 muestraPosicion(location);

13

14 //Nos registramos para recibir actualizaciones de la posicin

15 locationListener = new LocationListener() {


16 public void onLocationChanged(Location location) {

17 muestraPosicion(location);

18 }

19 public void onProviderDisabled(String provider){

20 lblEstado.setText("Provider OFF");

21 }

22 public void onProviderEnabled(String provider){

23 lblEstado.setText("Provider ON");

24 }

25 public void onStatusChanged(String provider, int status, Bundle extras){

26 Log.i("LocAndroid", "Provider Status: " + status);

27 lblEstado.setText("Provider Status: " + status);

28 }

29 };

30

31 locationManager.requestLocationUpdates(

32 LocationManager.GPS_PROVIDER, 15000, 0, locationListener);

33 }

34

35 private void muestraPosicion(Location loc) {

36 if(loc != null)

37 {

38 lblLatitud.setText("Latitud: " + String.valueOf(loc.getLatitude()));

39 lblLongitud.setText("Longitud: " + String.valueOf(loc.getLongitude()));

40 lblPrecision.setText("Precision: " + String.valueOf(loc.getAccuracy()));

41 Log.i("LocAndroid", String.valueOf(

42 loc.getLatitude() + " - " + String.valueOf(loc.getLongitude())));

43 }

44 else

45 {

46 lblLatitud.setText("Latitud: (sin_datos)");

47 lblLongitud.setText("Longitud: (sin_datos)");

48 lblPrecision.setText("Precision: (sin_datos)");

49 }
50 }

Si ejecutamos en este momento la aplicacin en el emulador y pulsamos el botn Activar veremos cmo los cuadros
de texto se rellenan con la informacin de la ltima posicin conocida (si existe), pero sin embargo estos datos no
cambiarn en ningn momento ya que por el momento el emulador de Android tan slo cuenta con esa informacin.
Cmo podemos simular la actualizacin de la posicin del dispositivo para ver si nuestra aplicacin responde
exactamente como esperamos?

Pues bien, para hacer esto tenemos varias opciones. La primera de ellas, y la ms sencilla, es el envo manual de una
nueva posicin al emulador de Android, para simular que ste hubiera cambiado su localizacin. Esto se puede
realizar desde la perspectiva de DDMS, en la pestaa Emulator Control, donde podemos encontrar una seccin
llamada Location Controls, mostrada en la imagen siguiente (click para ampliar):

Con estos controles podemos enviar de forma manual al emulador en ejecucin unas nuevas coordenadas de
posicin, para simular que stas se hubieran recibido a travs del proveedor de localizacin utilizado. De esta forma,
si introducimos unas coordenadas de longitud y latitud y pulsamos el botn Send mientras nuestra aplicacin se
ejecuta en el emulador, esto provocar la ejecucin del evento onLocationChanged() y por consiguiente se
mostrarn estos mismos datos en sus controles correspondientes de la interfaz, como vemos en la siguiente captura
de pantalla:

Por supuesto, si hacemos nuevos envos de coordenadas desde Eclipse veremos cmo sta se va actualizando en
nuestra aplicacin sin ningn tipo de problamas. Sin embargo este mtodo de manual no resulta demasiado
adecuado ni cmodo para probar toda la funcionalidad de nuestra aplicacin, por ejemplo la actualizacin de
posicin cada 15 segundos.
Por ello, Android proporciona otro mtodo algo menos manual de simular cambios frecuentes de posicin para
probar nuestras aplicaciones. Este mtodo consiste en proporcionar, en vez de una sla coordenada cada vez, una
lista de coordenadas que se iran enviando automticamente al emulador una tras otra a una determinada velocidad,
de forma que podamos simular que el dispositivo se mueve constantemente y que nuestra aplicacin responde de
forma correcta y en el momento adecuado a esos cambios de posicin. Y esta lista de coordenadas se puede
proporcionar de dos formas distintas, enformato GPX o como fichero KML. Ambos tipos de fichero son ampliamente
utilizados por aplicaciones y dispositivos de localizacin, como GPS, aplicaciones de cartografa y mapas, etc. Los
ficheros KML podemos generarlos por ejemplo a travs de la aplicacin Google Earth o manualmente con cualquier
editor de texto, pero recomiendo consultar los dos enlaces anteriores para obtener ms informacin sobre cada
formato. Para este ejemplo, yo he generado un fichero KML de muestra con una lista de 1000 posiciones geogrficas
al azar.

Para utilizar este fichero como fuente de datos para simular cambios en la posicin del dispositivo, accedemos
nuevamente a los Location Controls y pulsamos sobre la pestaa GPX o KML, segn el formato que hayamos elegido,
que en nuestro caso ser KML. Pulsamos el botn Load KML para seleccionar nuestro fichero y veremos la lista
de coordenadas como en la siguiente imagen:

Una vez cargado el fichero, tendremos disponibles los cuatro botones inferiores para (de izquierda a derecha):

Avanzar automticamente por la lista.

Ir a la posicin anterior de la lista de forma manual.

Ir a la posicin siguiente de la lista de forma manual.

Establecer la velocidad de avance automtico.

Entendido esto, vamos a utilizar la lista de posiciones para probar nuestra aplicacin. Para ello, ejecutamos la
aplicacin en el emulador, pulsamos nuestro botn Activar para comenzar a detectar cambios de posicin, y
pulsamos el botn de avance automtico (botn verde) que acabamos de comentar.

Llegados a este punto pueden ocurrir varias cosas, dependiendo del dispositivo o el emulador que estemos
utilizando, y por supuesto de la versin de Android sobre la que ejecutemos el ejemplo. O bien todo funciona segn
lo esperado y empezamos a recibir una lectura de posicin cada 15 segundos, o bien pueden aparecernos varias
actualizaciones con una frecuencia mucho menor que el periodo especificado. La primera situacin parece lgica, es
lo que habamos pedido. Pero cmo es posible la segunda? No habamos configurado el LocationListener para
obtener actualizaciones de posicin cada 15 segundos? Por qu llegan ms actualizaciones de las esperadas? Antes
de contestar a esto, dejemos por ejemplo que la aplicacin se ejecute durante un minuto sobre un emulador con
versin 2.2 de Android (API 8). Tras unos 60 segundos de ejecucin detenemos la captura de posiciones pulsando
nuestro botn Desactivar.
Ahora vayamos a la ventana de log del DDMS y veamos los mensajes de log ha generado nuestra aplicacin para
intentar saber qu ha ocurrido. En mi caso, los mensajes generados son los siguientes (en tu caso deben ser muy
parecidos):

1 05-08 10:50:37.921: INFO/LocAndroid(251): 7.0 - -11.999998333333334

2 05-08 10:50:38.041: INFO/LocAndroid(251): Provider Status: 2

3 05-08 10:50:38.901: INFO/LocAndroid(251): 7.000001666666666 - -11.999996666666668

4 05-08 10:50:39.941: INFO/LocAndroid(251): 7.000001666666666 - -11.999996666666668

5 05-08 10:50:41.011: INFO/LocAndroid(251): 7.000003333333333 - -11.999995000000002

6 05-08 10:50:43.011: INFO/LocAndroid(251): 7.000005000000001 - -11.999993333333334

7 05-08 10:50:45.001: INFO/LocAndroid(251): 7.000006666666667 - -11.999991666666665

8 05-08 10:50:46.061: INFO/LocAndroid(251): 7.000008333333333 - -11.999989999999999

9 05-08 10:50:47.131: INFO/LocAndroid(251): 7.000008333333333 - -11.999989999999999

10 05-08 10:50:47.182: INFO/LocAndroid(251): Provider Status: 1

11 05-08 10:51:02.232: INFO/LocAndroid(251): 7.000023333333333 - -11.999975

12 05-08 10:51:02.812: INFO/LocAndroid(251): 7.000023333333333 - -11.999973333333333

13 05-08 10:51:02.872: INFO/LocAndroid(251): Provider Status: 2

14 05-08 10:51:03.872: INFO/LocAndroid(251): 7.000024999999999 - -11.999973333333333

15 05-08 10:51:04.912: INFO/LocAndroid(251): 7.000026666666668 - -11.999971666666665

16 05-08 10:51:05.922: INFO/LocAndroid(251): 7.000026666666668 - -11.999971666666665

17 05-08 10:51:06.982: INFO/LocAndroid(251): 7.000028333333334 - -11.99997

18 05-08 10:51:08.032: INFO/LocAndroid(251): 7.000028333333334 - -11.999968333333333

19 05-08 10:51:09.062: INFO/LocAndroid(251): 7.00003 - -11.999968333333333

20 05-08 10:51:10.132: INFO/LocAndroid(251): 7.000031666666667 - -11.999966666666667

21 05-08 10:51:12.242: INFO/LocAndroid(251): 7.000033333333333 - -11.999965000000001

22 05-08 10:51:13.292: INFO/LocAndroid(251): 7.000033333333333 - -11.999963333333335

23 05-08 10:51:13.342: INFO/LocAndroid(251): Provider Status: 1

24 05-08 10:51:28.372: INFO/LocAndroid(251): 7.000048333333333 - -11.999950000000002

25 05-08 10:51:28.982: INFO/LocAndroid(251): 7.000048333333333 - -11.999950000000002

26 05-08 10:51:29.032: INFO/LocAndroid(251): Provider Status: 2

27 05-08 10:51:30.002: INFO/LocAndroid(251): 7.000050000000001 - -11.999948333333334

28 05-08 10:51:31.002: INFO/LocAndroid(251): 7.000051666666667 - -11.999946666666665

29 05-08 10:51:33.111: INFO/LocAndroid(251): 7.000053333333333 - -11.999944999999999

30 05-08 10:51:34.151: INFO/LocAndroid(251): 7.000053333333333 - -11.999944999999999

31 05-08 10:51:35.201: INFO/LocAndroid(251): 7.000055 - -11.999943333333333


32 05-08 10:51:36.251: INFO/LocAndroid(251): 7.0000566666666675 - -11.999941666666667

33 05-08 10:51:37.311: INFO/LocAndroid(251): 7.0000566666666675 - -11.999941666666667

34 05-08 10:51:38.361: INFO/LocAndroid(251): 7.0000583333333335 - -11.99994

35 05-08 10:51:38.431: INFO/LocAndroid(251): Provider Status: 1

Estudiemos un poco este log. Si observamos las marcas de fecha hora vemos varias cosas:

Un primer grupo de actualizaciones entre las 10:50:37 y las 10:50:47, con 8 lecturas.

Un segundo grupo de actualizaciones entre las 10:51:02 y las 10:51:13, con 11 lecturas.

Un tercer grupo de actualizaciones entre las 10:51:28 y las 10:51:38, con 10 lecturas.

Entre cada grupo de lecturas transcurren aproximadamente 15 segundos.

Los grupos estn formados por un nmero variable de lecturas.

Por tanto ya podemos sacar algunas conclusiones. Indicar al location listener una frecuencia de 15 segundos entre
actualizaciones no quiere decir que vayamos a tener una sola lectura cada 15 segundos, sino que al menos
tendremos una nueva con dicha frecuencia. Sin embargo, como podemos comprobar en los logs, las lecturas se
recibirn por grupos separados entre s por el intervalo de tiempo indicado.

Ms conclusiones, ahora sobre el estado del proveedor de localizacin. Si buscamos en el log los momentos donde
cambia el estado del proveedor vemos dos cosas importantes:

Despus de recibir cada grupo de lecturas el proveedor pasa a estado 1 (TEMPORARILY_UNAVAILABLE).

Tras empezar a recibir de nuevo lecturas el proveedor pasa a estado 2 (AVAILABLE).

Estos cambios en el estado de los proveedores de localizacin pueden ayudarnos a realizar diversas tareas. Un
ejemplo tpico es utilizar el cambio de estado a 1 (es decir, cuando se ha terminado de recibir un grupo de lecturas)
para seleccionar la lectura ms precisa del grupo recibido, algo especialmente importante cuando se estn utilizando
varios proveedores de localizacin simultneamente, cada uno con una precisin distinta.

A modo de resumen, en este artculo hemos visto cmo podemos utilizar las distintas herramientas que proporciona
la plataforma Android y el entorno de desarrollo Eclipse para simular cambios de posicin del dispositivo durante la
ejecucin de nuestras aplicaciones en el emulador. Tambi hemos visto cmo la generacin de mensajes de log
pueden ayudarnos a depurar este tipo de aplicaciones, y finalmente hemos utilizado esta herramienta de depuracin
para entender mejor el funcionamiento de los location listener y el comportamiento de los proveedores de
localizacin.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Content Providers en Android
Content Providers en Android (I): Construccin
by Sgoliver on 28/08/2011 in Android, Programacin

En este nuevo artculo del Curso de Programacin en Android que estamos publicando vamos a tratar eltemido [o a
veces incomprendido] tema de los Content Providers.

Un Content Provider no es ms que el mecanismo proporcionado por la plataforma Android para permitir compartir
informacin entre aplicaciones. Una aplicacin que desee que todo o parte de la informacin que almacena est
disponible de una forma controlada para el resto de aplicaciones del sistema deber proporcionar un content
provider a travs del cul se pueda realizar el acceso a dicha informacin. Este mecanismo es utilizado por muchas
de las aplicaciones estandard de un dispositivo Android, como por ejemplo la lista de contactos, la aplicacin de
SMS, o el calendario/agenda. Esto quiere decir que podramos acceder a los datos gestionados por estas aplicaciones
desde nuestras propias aplicaciones Android haciendo uso de los content providers correspondientes.

Son por tanto dos temas los que debemos tratar en este apartado, por un lado a construir nuevos content providers
personalizados para nuestras aplicaciones, y por otro utilizar un content provider ya existente para acceder a los
datos publicados por otras aplicaciones.

En gran parte de la bibliografa sobre programacin en Android se suele tratar primero el tema del acceso a content
providers ya existentes (como por ejemplo el acceso a la lista de contactos de Android) para despus pasar a la
construccin de nuevos content providers personalizados. Yo sin embargo voy a tratar de hacerlo en orden inverso,
ya que me parece importante conocer un poco el funcionamiento interno de un content provider antes de pasar a
utilizarlo sin ms dentro de nuestras aplicaciones. As, en este primer artculo sobre el tema veremos cmo crear
nuestro propio content provider para compartir datos con otras aplicaciones, y en el prximo artculo veremos como
utilizar este mecanismo para acceder directamente a datos de terceros.

Empecemos a entrar en materia. Para aadir un content provider a nuestra aplicacin tendremos que:

1. Crear una nueva clase que extienda a la clase android ContentProvider.

2. Declarar el nuevo content provider en nuestro fichero AndroidManifest.xml

Por supuesto nuestra aplicacin tendr que contar previamente con algn mtodo de almacenamiento interno para
la informacin que queremos compartir. Lo ms comn ser disponer de una base de datos SQLite, por lo que ser
esto lo que utilizar para todos los ejemplos de este artculo, pero internamente podramos tener los datos
almacenados de cualquier otra forma, por ejemplo en ficheros de texto, ficheros XML, etc. El content provider sera
el mecanismo que nos permita publicar esos datos a terceros de una forma homogenea y a travs de una interfaz
estandarizada.

Un primer detalle a tener en cuenta es que los registros de datos proporcionados por un content provider deben
contar siempre con un campo llamado _ID que los identifique de forma unvoca del resto de registros. Como
ejemplo, los registros devueltos por un content provider de clientes podra tener este aspecto:

_ID Cliente Telefono Email

3 Antonio 900123456 email1@correo.com

7 Jose 900123123 email2@correo.com


9 Luis 900123987 email3@correo.com

Sabiendo esto, es interesante que nuestros datos tambin cuenten internamente con este campo _ID (no tiene por
qu llamarse igual) de forma que nos sea ms sencillo despus generar los resultados del content provider.

Con todo esto, y para tener algo desde lo que partir, vamos a construir en primer lugar una aplicacin de ejemplo
muy sencilla con una base de datos SQLite que almacene los datos de una serie de clientes con una estructura similar
a la tabla anterior. Para ello seguiremos los mismos pasos que ya comentamos en los artculos dedicados al
tratamiento de bases de datos SQLite en Android (consultarndice del curso).

Por volver a recordarlo muy brevemente, lo que haremos ser crear una nueva clase que extienda
aSQLiteOpenHelper, definiremos las sentencias SQL para crear nuestra tabla de clientes, e implementaremos
finalmente los mtodos onCreate() y onUpgrade(). El cdigo de esta nueva clase, que yo he
llamado ClientesSqliteHelper, quedara como sigue:

1 public class ClientesSqliteHelper extends SQLiteOpenHelper {

3 //Sentencia SQL para crear la tabla de Clientes

4 String sqlCreate = "CREATE TABLE Clientes " +

5 "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +

6 " nombre TEXT, " +

7 " telefono TEXT, " +

8 " email TEXT )";

10 public ClientesSqliteHelper(Context contexto, String nombre,

11 CursorFactory factory, int version) {

12

13 super(contexto, nombre, factory, version);

14 }

15

16 @Override

17 public void onCreate(SQLiteDatabase db) {

18 //Se ejecuta la sentencia SQL de creacin de la tabla

19 db.execSQL(sqlCreate);

20

21 //Insertamos 15 clientes de ejemplo

22 for(int i=1; i<=15; i++)

23 {

24 //Generamos los datos de muestra


25 String nombre = "Cliente" + i;

26 String telefono = "900-123-00" + i;

27 String email = "email" + i + "@mail.com";

28

29 //Insertamos los datos en la tabla Clientes

30 db.execSQL("INSERT INTO Clientes (nombre, telefono, email) " +

31 "VALUES ('" + nombre + "', '" + telefono +"', '" + email + "')");

32 }

33 }

34

35 @Override

36 public void onUpgrade(SQLiteDatabase db, int versionAnterior, int versionNueva) {

37 //NOTA: Por simplicidad del ejemplo aqu utilizamos directamente la opcin de

38 // eliminar la tabla anterior y crearla de nuevo vaca con el nuevo formato.

39 // Sin embargo lo normal ser que haya que migrar datos de la tabla antigua

40 // a la nueva, por lo que este mtodo debera ser ms elaborado.

41

42 //Se elimina la versin anterior de la tabla

43 db.execSQL("DROP TABLE IF EXISTS Clientes");

44

45 //Se crea la nueva versin de la tabla

46 db.execSQL(sqlCreate);

47 }

48 }

Como notas relevantes del cdigo anterior:

Ntese el campo _id que hemos incluido en la base de datos de clientes por lo motivos indicados un poco
ms arriba. Este campo lo declaramos como INTEGER PRIMARY KEY AUTOINCREMENT, de forma que se
incremente automticamente cada vez que insertamos un nuevo registro en la base de datos.

En el mtodo onCreate(), adems de ejecutar la sentencia SQL para crear la tabla Clientes, tambin inserta
varios registros de ejemplo.

Para simplificar el ejemplo, el mtodo onUpgrade() se limita a eliminar la tabla actual y crear una nueva con
la nueva estructura. En una aplicacin real habra que hacer probblemente la migracin de los datos a la
nueva base de datos.

Dado que la clase anterior ya se ocupa de todo, incluso de insertar algunos registro de ejemplo con los que podamos
hacer pruebas, la aplicacin principal de ejemplo no mostrar en principio nada en pantalla ni har nada con la
informacin. Esto lo he decidido as para no complicar el cdigo de la aplicacin innecesariamente, ya que no nos va
a interesar el tratamiento directo de los datos por parte de la aplicacin principal, sino su utilizacin a travs del
content provider que vamos a construir.

Una vez que ya contamos con nuestra aplicacin de ejemplo y su base de datos, es hora de empezar a construir el
nuevo content provider que nos permitir compartir sus datos con otras aplicaciones.

Lo primero que vamos a comentar es la forma con que se hace referencia en Android a los content providers. El
acceso a un content provider se realiza siempre mediante una URI. Una URI no es ms que una cadena de texto
similar a cualquiera de las direcciones web que utilizamos en nuestro navegador. Al igual que para acceder a mi blog
lo hacemos mediante la direccin http://www.sgoliver.net, para acceder a un content provider utilizaremos una
direccin similar a content://net.sgoliver.android.contentproviders/clientes.

Las direcciones URI de los content providers estn formadas por 3 partes. En primer lugar el prefijo content:// que
indica que dicho recurso deber ser tratado por un content provider. En segundo lugar se indica el identificador en s
del content provider, tambin llamado authority. Dado que este dato debe ser nico es una buena prctica utilizar
un authority de tipo nombre de clase java invertido, por ejemplo en mi caso
net.sgoliver.android.contentproviders. Por ltimo, se indica la entidad concreta a la que queremos acceder dentro
de los datos que proporciona el content provider. En nuestro caso ser simplemente la tabla de clientes, ya que
ser la nica existente, pero dado que un content provider puede contener los datos de varias entidades distintas en
este ltimo tramo de la URI habr que especificarlo. Indicar por ltimo que en una URI se puede hacer referencia
directamente a un registro concreto de la entidad seleccionada. Esto se hara indicando al final de la URI el ID de
dicho registro. Por ejemplo la uri content://net.sgoliver.android.contentproviders/clientes/23 hara referencia
directa al cliente con _ID = 23.

Todo esto es importante ya que ser nuestro content provider el encargado de interpretar/parsear la URI completa
para determinar los datos que se le estn solicitando. Esto lo veremos un poco ms adelante.

Sigamos. El siguiente paso ser extender a la clase ContentProvider. Si echamos un vistazo a los mtodos abstractos
que tendremos que implementar veremos que tenemos los siguientes:

onCreate()

query()

insert()

update()

delete()

getType()

El primero de ellos nos servir para inicializar todos los recursos necesarios para el funcionamiento del nuevo
content provider. Los cuatro siguientes sern los mtodos que permitirn acceder a los datos (consulta, insercin,
modificacin y eliminacin, respectivamente) y por ltimo, el mtodo getType()permitir conocer el tipo de datos
devueltos por el content provider (ms tade intentaremos explicar algo mejor esto ltimo).

Adems de implementar estos mtodos, tambin definiremos una serie de constantes dentro de nuestra nueva clase
provider, que ayudarn posteriormente a su utilizacin. Veamos esto paso a paso. Vamos a crear una nueva
clase ClientesProvider que extienda de ContentProvider.

Lo primero que vamos a definir es la URI con la que se acceder a nuestro content provider. En mi caso he elegido la
siguiente: content://net.sgoliver.android.contentproviders/clientes. Adems, para seguir la prctica habitual de
todos los content providers de Android, encapsularemos adems esta direccin en un objeto esttico de
tipo Uri llamado CONTENT_URI.

1 //Definicin del CONTENT_URI

2 private static final String uri =


3 "content://net.sgoliver.android.contentproviders/clientes";

5 public static final Uri CONTENT_URI = Uri.parse(uri);

A continuacin vamos a definir varias constantes con los nombres de las columnas de los datos proporcionados por
nuestro content provider. Como ya dijimos antes existen columnas predefinidas que deben tener todos los content
providers, por ejemplo la columna _ID. Estas columnas estandar estn definidas en la clase BaseColumns, por lo que
para aadir la nuevas columnas de nuestro content provider definiremos una clase interna pblica tomando como
base la clase BaseColumns y aadiremos nuestras nuevas columnas.

1 //Clase interna para declarar las constantes de columna

2 public static final class Clientes implements BaseColumns

3 {

4 private Clientes() {}

6 //Nombres de columnas

7 public static final String COL_NOMBRE = "nombre";

8 public static final String COL_TELEFONO = "telefono";

9 public static final String COL_EMAIL = "email";

10 }

Por ltimo, vamos a definir varios atributos privados auxiliares para almacenar el nombre de la base de datos, la
versin, y la tabla a la que acceder nuestro content provider.

1 //Base de datos

2 private ClientesSqliteHelper clidbh;

3 private static final String BD_NOMBRE = "DBClientes";

4 private static final int BD_VERSION = 1;

5 private static final String TABLA_CLIENTES = "Clientes";

Como se indic anteriormente, la primera tarea que nuestro content provider deber hacer cuando se acceda a l
ser interpretar la URI utilizada. Para facilitar esta tarea Android proporciona una clase llamada UriMatcher, capaz
de interpretar determinados patrones en una URI. Esto nos ser til para determinar por ejemplo si una URI hace
referencia a una tabla genrica o a un registro concreto a travs de su ID. Por ejemplo:

content://net.sgoliver.android.contentproviders/clientes > Acceso genrico a tabla de clientes

content://net.sgoliver.android.contentproviders/clientes/17 > Acceso directo al cliente con ID = 17

Para conseguir esto definiremos tambin como miembro de la clase un objeto UriMatcher y dos nuevas constantes
que representen los dos tipos de URI que hemos indicado: acceso genrico a tabla (lo llamar CLIENTES) o acceso a
cliente por ID (lo llamar CLIENTES_ID). A continuacin inicializaremos el objeto UriMatcher indicndole el formato
de ambos tipos de URI, de forma que pueda diferenciarlos y devolvernos su tipo (una de las dos constantes
definidas, CLIENTES oCLIENTES_ID).

1 //UriMatcher

2 private static final int CLIENTES = 1;


3 private static final int CLIENTES_ID = 2;

4 private static final UriMatcher uriMatcher;

6 //Inicializamos el UriMatcher

7 static {

8 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

9 uriMatcher.addURI("net.sgoliver.android.contentproviders", "clientes", CLIENTES);

10 uriMatcher.addURI("net.sgoliver.android.contentproviders", "clientes/#", CLIENTES_ID);

11 }

En el cdigo anterior vemos como mediante el mtodo addUri() indicamos el authority de nuestra URI, el formato de
la entidad que estamos solicitando, y el tipo con el que queremos identificar dicho formato. Ms tarde veremos
cmo utilizar esto de forma prctica.

Bien, pues ya tenemos definidos todos los miembros necesarios para nuestro nuevo content provider. Ahora toca
implementar los mtodos comentados anteriormente.

El primero de ellos es onCreate(). En este mtodo nos limitaremos simplemente a inicializar nuestra base de datos, a
travs de su nombre y versin, y utilizando para ello la claseClientesSqliteHelper que creamos al principio del
artculo.

1 @Override

2 public boolean onCreate() {

4 clidbh = new ClientesSqliteHelper(

5 getContext(), BD_NOMBRE, null, BD_VERSION);

7 return true;

8 }

La parte interesante llega con el mtodo query(). Este mtodo recibe como parmetros una 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 datos son anlogos a los que comentamos cuando tratamos la consulta de
datos en SQLite para Android, artculo que recomiendo releer si no tenis muy frescos estos conocimientos. El
mtodo query deber devolver los datos solicitados segn la URI indicada y los criterios de seleccin y ordenacin
pasados como parmetro. As, si la URI hace referencia a un cliente concreto por su ID se deber ser el nico
registro devuelto. Si por el contrario es un acceso genrico a la tabla de clientes habr que realizar la consulta SQL
correspondiente a la base de datos respetanto los criterios pasados como parmetro.

Para disitinguir entre los dos tipos de URI posibles utilizaremos como ya hemos indicado el objetouriMatcher,
utilizando su mtodo match(). Si el tipo devuelto es CLIENTES_ID, es decir, que se trata de un acceso a un cliente
concreto, sustituiremos el criterio de seleccin por uno que acceda a la tabla de clientes slo por el ID indicado en la
URI. Para obtener este ID utilizaremos el mtodogetLastPathSegment() del objeto uri que extrae el ltimo elemento
de la URI, en este caso el ID del cliente.

Hecho esto, ya tan slo queda realizar la consulta a la base de datos mediante el mtodo query() deSQLiteDatabase.
Esto es sencillo ya que los parmetros son anlogos a los recibidos en el mtodoquery() del content provider.
1 @Override

2 public Cursor query(Uri uri, String[] projection,

3 String selection, String[] selectionArgs, String sortOrder) {

5 //Si es una consulta a un ID concreto construimos el WHERE

6 String where = selection;

7 if(uriMatcher.match(uri) == CLIENTES_ID){

8 where = "_id=" + uri.getLastPathSegment();

9 }

10

11 SQLiteDatabase db = clidbh.getWritableDatabase();

12

13 Cursor c = db.query(TABLA_CLIENTES, projection, where,

14 selectionArgs, null, null, sortOrder);

15

16 return c;

17 }

Como vemos, los resultados se devuelven en forma de Cursor, una vez ms exactamente igual a como lo hace el
mtodo query() de SQLiteDatabase.

Por su parte, los mtodos update() y delete() son completamente anlogos a ste, con la nica diferencia de que
devuelven el nmero de registros afectados en vez de un cursor a los resultados. Vemos directamente el cdigo:

1 @Override

2 public int update(Uri uri, ContentValues values,

3 String selection, String[] selectionArgs) {

5 int cont;

7 //Si es una consulta a un ID concreto construimos el WHERE

8 String where = selection;

9 if(uriMatcher.match(uri) == CLIENTES_ID){

10 where = "_id=" + uri.getLastPathSegment();

11 }

12

13 SQLiteDatabase db = clidbh.getWritableDatabase();
14

15 cont = db.update(TABLA_CLIENTES, values, where, selectionArgs);

16

17 return cont;

18 }

19

20 @Override

21 public int delete(Uri uri, String selection, String[] selectionArgs) {

22

23 int cont;

24

25 //Si es una consulta a un ID concreto construimos el WHERE

26 String where = selection;

27 if(uriMatcher.match(uri) == CLIENTES_ID){

28 where = "_id=" + uri.getLastPathSegment();

29 }

30

31 SQLiteDatabase db = clidbh.getWritableDatabase();

32

33 cont = db.delete(TABLA_CLIENTES, where, selectionArgs);

34

35 return cont;

36 }

El mtodo insert() s es algo diferente, aunque igual de sencillo. La diferencia en este caso radica en que debe
devolver la URI que hace referencia al nuevo registro insertado. Para ello, obtendremos el nuevo ID del elemento
insertado como resultado del mtodo insert() de SQLiteDatabase, y posteriormente construiremos la nueva URI
mediante el mtodo auxiliarContentUris.withAppendedId() que recibe como parmetro la URI de nuestro content
provider y el ID del nuevo elemento.

1 @Override

2 public Uri insert(Uri uri, ContentValues values) {

4 long regId = 1;

6 SQLiteDatabase db = clidbh.getWritableDatabase();

7
8 regId = db.insert(TABLA_CLIENTES, null, values);

10 Uri newUri = ContentUris.withAppendedId(CONTENT_URI, regId);

11

12 return newUri;

13 }

Por ltimo, tan slo nos 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 expresar como unMIME Type, al igual que hacen los
navegadores web para determinar el tipo de datos que estn recibiendo tras una peticin a un servidor. Identificar el
tipo de datos que devuelve un content provider ayudar por ejemplo a Android a determinar qu aplicaciones son
capaces de procesar dichos datos.

Una vez ms existirn dos tipos MIME distintos para cada entidad del content provider, uno de ellos destinado a
cuando se devuelve una lista de registros como resultado, y otro para cuando se devuelve un registro nico
concreto. De esta forma, seguiremos los siguientes patrones para definir uno y otro tipo de datos:

vnd.android.cursor.item/vnd.xxxxxx > Registro nico

vnd.android.cursor.dir/vnd.xxxxxx > Lista de registros

En mi caso de ejemplo, he definido los siguientes tipos:

vnd.android.cursor.item/vnd.sgoliver.cliente

vnd.android.cursor.dir/vnd.sgoliver.cliente

Con esto en cuenta, la implementacin del mtodo getType() quedara como sigue:

1 @Override

2 public String getType(Uri uri) {

4 int match = uriMatcher.match(uri);

6 switch (match)

7 {

8 case CLIENTES:

9 return "vnd.android.cursor.dir/vnd.sgoliver.cliente";

10 case CLIENTES_ID:

11 return "vnd.android.cursor.item/vnd.sgoliver.cliente";

12 default:

13 return null;

14 }

15 }

Como se puede observar, utilizamos una vez ms el objeto UriMatcher para determinar el tipo de URI que se est
solicitando y en funcin de sta devolvemos un tipo MIME u otro.
Pues bien, con esto ya hemos completado la implementacin del nuevo content provider. Pero an nos queda un
paso ms, como indicamos al principio del artculo. Debemos declarar el content provider en nuestro
fichero AndroidManifest.xml de forma que una vez instalada la aplicacin en el dispositivo Android conozca la
existencia de dicho recurso.

Para ello, bastar insertar un nuevo elemento <provider> dentro de <application> indicando el nombre del content
provider y su authority.

1 <application android:icon="@drawable/icon"

2 android:label="@string/app_name">

4 ...

6 <provider android:name="ClientesProvider"

7 android:authorities="net.sgoliver.android.contentproviders"/>

9 </application>

Ahora s hemos completado totalmente la construccin de nuestro nuevo content provider mediante el cual otras
aplicaciones del sistema podrn acceder a los datos almacenados por nuestra aplicacin.

En el siguiente artculo veremos cmo utilizar este nuevo content provider para acceder a los datos de nuestra
aplicacin de ejemplo, y tambin veremos cmo podemos utilizar alguno de los content provider predefinidos por
Android para consultar datos del sistema, como por ejemplo la lista de contactos o la lista de llamadas realizadas.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Content Providers en Android (II): Utilizacin
by Sgoliver on 31/08/2011 in Android, Programacin

En el artculo anterior del Curso de Programacin en Android vimos como construir un content
providerpersonalizado para permitir a nuestras aplicaciones Android compartir datos con otras aplicaciones del
sistema. En este nuevo artculo vamos a ver el tema desde el punto de vista opuesto, es decir, vamos a aprender a
hacer uso de un content provider ya existente para acceder a datos de otras aplicaciones. Adems, veremos cmo
tambin podemos acceder a datos del propio sistema Android (logs de llamadas, lista de contactos, agenda
telefnica, bandeja de entrada de sms, etc) utilizando este mismo mecanismo.

Vamos a comenzar explicando cmo podemos utilizar el content provider que implementamos en el artculo anterior
para acceder a los datos de los clientes. Para no complicar mucho el ejemplo ni hacer ms dificil las pruebas y la
depuracin en el emulador de Android vamos a hacer uso el content provider desde la propia aplicacin de ejemplo
que hemos creado. De cualquier forma, el cdigo necesario sera exactamente igual si lo hiciramos desde otra
aplicacin distinta.

Utilizar un content provider ya existente es muy sencillo, sobre todo comparado con el laborioso proceso de
construccin de uno nuevo. Para comenzar, debemos obtener una referencia a un Content Resolver, objeto a travs
del que realizaremos todas las acciones necesarias sobre el content provider. Esto es tan fcil como utilizar el
mtodo getContentResolver() desde nuestra actividad para obtener la referencia indicada. Una vez obtenida la
referencia al content resolver, podremos utilizar sus mtodosquery(), update(), insert() y delete() para realizar las
acciones equivalentes sobre el content provider. Por ver varios ejemplos de la utilizacin de estos mtodos
aadiremos a nuestra aplicacin de ejemplo tres botones en la pantalla principal, uno para hacer una consulta de
todos los clientes, otro para insertar registros nuevos, y el ltimo para eliminar todos los registros nuevos insertados
con el segundo botn.

Empecemos por la consulta de clientes. El procedimiento ser prcticamente igual al que vimosen los artculos de
acceso a bases de datos SQLite (consultar el ndice del curso). Comenzaremos por definir un array con los nombres
de las columnas de la tabla que queremos recuperar en los resultados de la consulta, que en nuestro caso sern el
ID, el nombre, el telfono y el email. Tras esto, obtendremos como dijimos antes una referencia al content resolver y
utilizaremos su mtodo query() para obtener los resultados en forma de cursor. El mtodo query() recibe, como ya
vimos en el artculo anterior, la Uri del content provider al que queremos acceder, el array de columnas que
queremos recuperar, el criterio de seleccin, los argumentos variables, y el criterio de ordenacin de los resultados.
En nuestro caso, para no complicarnos utilizaremos tan slo los dos primeros, pasndole el CONTENT_URI de
nuestro provider y el array de columnas que acabamos de definir.

1 //Columnas de la tabla a recuperar

2 String[] projection = new String[] {

3 Clientes._ID,

4 Clientes.COL_NOMBRE,

5 Clientes.COL_TELEFONO,

6 Clientes.COL_EMAIL };

8 Uri clientesUri = ClientesProvider.CONTENT_URI;

10 ContentResolver cr = getContentResolver();

11
12 //Hacemos la consulta

13 Cursor cur = cr.query(clientesUri,

14 projection, //Columnas a devolver

15 null, //Condicin de la query

16 null, //Argumentos variables de la query

17 null); //Orden de los resultados

Hecho esto, tendremos que recorrer el cursor para procesar los resultados. Para nuestro ejemplo, simplemente los
escribiremos en un cuadro de texto (txtResultados) colocado bajo los tres botones de ejemplo. Una vez ms, si tienes
dudas sobre cmo recorrer un cursor, puedes consultar los artculos del curso dedicados al tratamiento de bases de
datos SQLite, por ejemplo ste. Veamos cmo quedara el cdigo:

1 if (cur.moveToFirst())

2 {

3 String nombre;

4 String telefono;

5 String email;

7 int colNombre = cur.getColumnIndex(Clientes.COL_NOMBRE);

8 int colTelefono = cur.getColumnIndex(Clientes.COL_TELEFONO);

9 int colEmail = cur.getColumnIndex(Clientes.COL_EMAIL);

10

11 txtResultados.setText("");

12

13 do

14 {

15 nombre = cur.getString(colNombre);

16 telefono = cur.getString(colTelefono);

17 email = cur.getString(colEmail);

18

19 txtResultados.append(nombre + " - " + telefono + " - " + email + "\n");

20

21 } while (cur.moveToNext());

22 }

Para insertar nuevos registros, el trabajo ser tambin exactamente igual al que se hace al tratar directamente con
bases de datos SQLite. Rellenaremos en primer lugar un objeto ContentValues con los datos del nuevo cliente y
posteriormente utilizamos el mtodo insert() pasndole la URI del content provider en primer lugar, y los datos del
nuevo registro como segundo parmetro.
1 ContentValues values = new ContentValues();

3 values.put(Clientes.COL_NOMBRE, "ClienteN");

4 values.put(Clientes.COL_TELEFONO, "999111222");

5 values.put(Clientes.COL_EMAIL, "nuevo@email.com");

7 ContentResolver cr = getContentResolver();

9 cr.insert(ClientesProvider.CONTENT_URI, values);

Por ltimo, y ms sencillo todava, la eliminacin de registros la haremos directamente utilizando el


mtodo delete() del content resolver, indicando como segundo parmetro el criterio de localizacin de los registros
que queremos eliminar, que en este caso sern los que hayamos insertado nuevos con el segundo botn de ejemplo
(aquellos con nombre = ClienteN).

1 ContentResolver cr = getContentResolver();

3 cr.delete(ClientesProvider.CONTENT_URI,

4 Clientes.COL_NOMBRE + " = 'ClienteN'", null);

Como muestra grfica, veamos por ejemplo el resultado de la consulta de clientes (primer botn) en la aplicacin de
ejemplo.

Con esto, hemos visto lo sencillo que resulta acceder a los datos proporcionados por un content provider. Pues bien,
ste es el mismo mecanismo que podemos utilizar para acceder a muchos datos de la propia plataforma Android. En
la documentacin oficial del paquete android.providerpodemos consultar los datos que tenemos disponibles a
travs de este mecanismo, entre ellos encontramos por ejemplo: el historial de llamadas, la agenda de contactos y
telfonos, las bibliotecas multimedia (audio y video), o el historial y la lista de favoritos del navegador.

Por ver un ejemplo de acceso a este tipo de datos, vamos a realizar una consulta al historial de llamadas del
dispositivo, para lo que accederemos al content provider android.provider.CallLog.

En primer lugar vamos a registrar varias llamadas en el emulador de Android, de forma que los resultados de la
consulta al historial de llamadas contenga algunos registros. Haremos por ejemplo varias llamadas salientes desde el
emulador y simularemos varias llamadas entrantes desde el DDMS. Las primeras son sencillas, simplemente ve al
emulador, accede al telfono,marca y descuelga igual que lo haras en un dispositivo fsico. Y para emular llamadas
entrantes podremos hacerlo una vez ms desde Eclipse, accediendo a la vista del DDMS. En esta vista, si accedemos
a la seccin Emulator Control veremos un apartado llamado Telephony Actions. Desde ste, podemos introducir
un nmero de telfono origen cualquiera y pulsar el botn Call para conseguir que nuestro emulador reciba una
llamada entrante. Sin aceptar la llamada en elemulador pulsaremos Hang Up para teminar la llamada simulando
as una llamada perdida.

Hecho esto, procedemos a realizar la consulta al historial de llamadas utilizando el content provider indicado, y para
ello aadiremos un botn ms a la aplicacin de ejemplo.

Consultando la documentacin del content provider veremos que podemos extraer diferentes datos relacionados
con la lista de llamadas. Nosotros nos quedaremos slo con dos significativos, el nmero origen o destino de la
llamada, y el tipo de llamada (entrante, saliente, perdida). Los nombres de estas columnas se almacenan en las
constantes Calls.NUMBER y Calls.TYPE respectivamente.

Decidido esto, actuaremos igual que antes. Definiremos el array con las columnas que queremos recuperar,
obtendremos la referencia al content resolver y ejecutaremos la consulta llamando al mtodoquery(). Por ltimo,
recorremos el cursor obtenido y procesamos los resultados. Igual que antes, lo nico que haremos ser escribir los
resultados al cuadro de texto situado bajo los botones. Veamos el cdigo:

1 String[] projection = new String[] {

2 Calls.TYPE,

3 Calls.NUMBER };

5 Uri llamadasUri = Calls.CONTENT_URI;

7 ContentResolver cr = getContentResolver();

9 Cursor cur = cr.query(llamadasUri,

10 projection, //Columnas a devolver

11 null, //Condicin de la query

12 null, //Argumentos variables de la query

13 null); //Orden de los resultados

14
15 if (cur.moveToFirst())

16 {

17 int tipo;

18 String tipoLlamada = "";

19 String telefono;

20

21 int colTipo = cur.getColumnIndex(Calls.TYPE);

22 int colTelefono = cur.getColumnIndex(Calls.NUMBER);

23

24 txtResultados.setText("");

25

26 do

27 {

28 tipo = cur.getInt(colTipo);

29 telefono = cur.getString(colTelefono);

30

31 if(tipo == Calls.INCOMING_TYPE)

32 tipoLlamada = "ENTRADA";

33 else if(tipo == Calls.OUTGOING_TYPE)

34 tipoLlamada = "SALIDA";

35 else if(tipo == Calls.MISSED_TYPE)

36 tipoLlamada = "PERDIDA";

37

38 txtResultados.append(tipoLlamada + " - " + telefono + "\n");

39

40 } while (cur.moveToNext());

41 }

Lo nico fuera de lo normal que hacemos en el cdigo anterior es la decodificacin del valor del tipo de llamada
recuperado, que la hacemos comparando el resultado con las
constantesCalls.INCOMING_TYPE (entrante), Calls.OUTGOING_TYPE (saliente), Calls.MISSED_TYPE(perdida)
proporcionadas por la propia clase provider.

Un ltimo detalle importante. Para que nuestra aplicacin pueda acceder al historial de llamadas del dispositivo
tendremos que incluir en el fichero AndroidManifest.xml el permiso READ_CONTACTS yREAD_CALL_LOG utilizando
la clusula <uses-permission> correspondiente.

1 <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>

2 <uses-permission android:name="android.permission.READ_CALL_LOG"></uses-permission>
Si ejecutamos la aplicacin y realizamos la consulta podremos ver un resultado similar al siguiente:

Y con esto terminamos con el tema dedicado a los content providers. Espero que os haya sido til para aprender a
incluir esta funcionalidad a vuestras aplicaciones y a utilizar este mecanismo para acceder a datos propios del
sistema.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Notificaciones en Android
Notificaciones en Android (I): Toast
by Sgoliver on 09/06/2011 in Android, Programacin

Un tema rpido antes de seguir con el Curso de Programacin Android que estamos realizando. En Android existen
varias formas de notificar mensajes al usuario, como por ejemplo los cuadros de dilogo modales o las notificaciones
de la bandeja del sistema (o barra de estado). Pero en este artculo nos vamos a centrar en primer lugar en la forma
ms sencilla de notificacin: los llamados Toast.

Un toast es un mensaje que se muestra en pantalla durante unos segundos al usuario para luego volver a
desaparecer automticamente sin requerir ningn tipo de actuacin por su parte, y sin recibir el foco en ningn
momento (o dicho de otra forma, sin interferir en las acciones que est realizando el usuario en ese momento).
Aunque son personalizables, aparecen por defecto en la parte inferior de la pantalla, sobre un rectngulo gris
ligeramente translcido. Por sus propias caractersticas, este tipo de notificaciones son ideales para mostrar
mensajes rpidos y sencillos al usuario, pero por el contrario, al no requerir confirmacin por su parte, no deberan
utilizarse para hacer notificaciones demasiado importantes.

Su utilizacin es muy sencilla, concentrndose toda la funcionalidad en la clase Toast. Esta clase dispone de un
mtodo esttico makeText() al que deberemos pasar como parmetro el contexto de la actividad, el texto a mostrar,
y la duracin del mensaje, que puede tomar los valores LENGTH_LONG oLENGTH_SHORT, dependiendo del tiempo
que queramos que la notificacin aparezca en pantalla. Tras obtener una referencia al objeto Toast a travs de este
mtodo, ya slo nos quedara mostrarlo en pantalla mediante el mtodo show().

Vamos a construir una aplicacin de ejemplo para demostrar el funcionamiento de este tipo de notificaciones. Y para
empezar vamos a incluir un botn que muestre un toast bsico de la forma que acabamos de describir:

1 btnDefecto.setOnClickListener(new OnClickListener() {

2 @Override

3 public void onClick(View arg0) {

4 Toast toast1 =

5 Toast.makeText(getApplicationContext(),

6 "Toast por defecto", Toast.LENGTH_SHORT);

8 toast1.show();

9 }

10 });

Si ejecutamos esta sencilla aplicacin en el emulador y pulsamos el botn que acabamos de aadir veremos como en
la parte inferior de la pantalla aparece el mensaje Toast por defecto, que tras varios segundos desaparecer
automticamente.
Como hemos comentado, ste es el comportamiento por defecto de las notificaciones toast, sin embargo tambin
podemos personalizarlo un poco cambiando su posicin en la pantalla. Para esto utilizaremos el mtodo setGravity(),
al que podremos indicar en qu zona deseamos que aparezca la notificacin. La zona deberemos indicarla con
alguna de las constantes definidas en la claseGravity: CENTER, LEFT, BOTTOM, o con alguna combinacin de stas.

Para nuestro ejemplo vamos a colocar la notificacin en la zona central izquierda de la pantalla. Para ello, aadamos
un segundo botn a la aplicacin de ejemplo que muestre un toast con estas caractersticas:

1 btnGravity.setOnClickListener(new OnClickListener() {

2 @Override

3 public void onClick(View arg0) {

4 Toast toast2 =

5 Toast.makeText(getApplicationContext(),

6 "Toast con gravity", Toast.LENGTH_SHORT);

8 toast2.setGravity(Gravity.CENTER|Gravity.LEFT,0,0);

10 toast2.show();

11 }

12 });

Si volvemos a ejecutar la aplicacin y pulsamos el nuevo botn veremos como el toast aparece en la zona indicada
de la pantalla:
Si esto no es suficiente y necesitamos personalizar por completo el aspecto de la notificacin, Android nos ofrece la
posibilidad de definir un layout XML propio para toast, donde podremos incluir todos los elementos necesarios para
adaptar la notificacin a nuestras necesidades. para nuestro ejemplo vamos a definir un layout sencillo, con una
imagen y una etiqueta de texto sobre un rectngulo gris:

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

2 <LinearLayout

3 xmlns:android="http://schemas.android.com/apk/res/android"

4 android:id="@+id/lytLayout"

5 android:layout_width="fill_parent"

6 android:layout_height="fill_parent"

7 android:orientation="horizontal"

8 android:background="#555555"

9 android:padding="5dip" >

10

11 <ImageView android:id="@+id/imgIcono"

12 android:layout_height="wrap_content"

13 android:layout_width="wrap_content"

14 android:src="@drawable/marcador" />

15

16 <TextView android:id="@+id/txtMensaje"

17 android:layout_width="wrap_content"

18 android:layout_height="wrap_content"

19 android:layout_gravity="center_vertical"

20 android:textColor="#FFFFFF"
21 android:paddingLeft="10dip" />

22

23 </LinearLayout>

Guardaremos este layout con el nombre toast_layout.xml, y como siempre lo colocaremos en la carpeta
res\layout de nuestro proyecto.

Para asignar este layout a nuestro toast tendremos que actuar de una forma algo diferente a las anteriores. En
primer lugar deberemos inflar el layout mediante un objeto LayoutInflater, como ya vimos en varias ocasiones al
tratar los artculos de interfaz grfica. Una vez construido el layout modificaremos los valores de los distintos
controles para mostrar la informacin que queramos. En nuestro caso, tan slo modificaremos el mensaje de la
etiqueta de texto, ya que la imagen ya la asignamos de forma esttica en el layout XML mediante el
atributo android:src. Tras esto, slo nos quedar establecer la duracin de la notificacin con setDuration() y asignar
el layout personalizado al toast mediante el mtodo setView(). Veamos cmo quedara todo el cdigo incluido en un
tercer botn de ejemplo:

1 btnLayout.setOnClickListener(new OnClickListener() {

2 @Override

3 public void onClick(View arg0) {

4 Toast toast3 = new Toast(getApplicationContext());

6 LayoutInflater inflater = getLayoutInflater();

7 View layout = inflater.inflate(R.layout.toast_layout,

8 (ViewGroup) findViewById(R.id.lytLayout));

10 TextView txtMsg = (TextView)layout.findViewById(R.id.txtMensaje);

11 txtMsg.setText("Toast Personalizado");

12

13 toast3.setDuration(Toast.LENGTH_SHORT);

14 toast3.setView(layout);

15 toast3.show();

16 }

17 });

Si ejecutamos ahora la aplicacin de ejemplo y pulsamos el nuevo botn, veremos como nuestro toast aparece con
la estructura definida en nuestro layout personalizado.
Como podis comprobar, mostrar notificaciones de tipo Toast en nuestras aplicaciones Android es algo de lo ms
sencillo, y a veces resultan un elemento de lo ms interesante para avisar al usuario de determinados eventos.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Notificaciones en Android (II): Barra de Estado
by Sgoliver on 20/10/2011 in Android, Programacin

Hace algn tiempo, ya tratamos dentro de este curso un primer mecanismo de notificaciones disponibles en la
plataforma Android: los llamados Toast. Como ya comentamos, este tipo de notificaciones, aunque resultan tiles y
prcticas en muchas ocasiones, no deberamos utilizarlas en situaciones en las que necesitemos asegurarnos la
atencin del usuario ya que no requiere de ninguna intervencin por su parte, se muestran y desaparecen
automticamente de la pantalla.

En este nuevo artculo vamos a tratar otro tipo de notificaciones algo ms persistentes, las notificaciones de la barra
de estado de Android. Estas notificaciones son las que se muestran en nuestro dispositivo por ejemplo cuando
recibimos un mensaje SMS, cuando tenemos actualizaciones disponibles, cuando tenemos el reproductor de msica
abierto en segundo plano, Estas notificaciones constan de un icono y un texto mostrado en la barra de estado
superior, y adicionalmente un mensaje algo ms descriptivo y una marca de fecha/hora que podemos consultar
desplegando la bandeja del sistema. A modo de ejemplo, cuando tenemos una llamada perdida en nuestro terminal,
se nos muestra por un lado un icono en la barra de estado superior (ms un texto que aparece durante unos
segundos).

y un mensaje con ms informacin al desplegar la bandeja del sistema, donde en este caso concreto se nos
informa del evento que se ha producido (Missed call), el nmero de telfono asociado, y la fecha/hora del evento.
Adems, al pulsar sobre la notificacin se nos dirige automticamente al historial de llamadas.

Pues bien, aprendamos a utilizar este tipo de notificaciones en nuestras aplicaciones. Vamos a construir para ello
una aplicacin de ejemplo, como siempre lo ms sencilla posible para centrar la atencin en lo realmente
importante. En este caso, el ejemplo va a consistir en un nico botn que genere una notificacin de ejemplo en la
barra de estado, con todos los elementos comentados y con la posibilidad de dirigirnos a la propia aplicacin de
ejemplo cuando se pulse sobre ella.

Para generar notificaciones en la barra de estado del sistema vamos a utilizar una clase incluida en la librera de
compatibilidad android-support-v4.jar que ya hemos utilizado en otras ocasiones y que debe estar incluida por
defecto en vuestro proyecto si lo habis creado con alguna versin reciente del plugin de Eclipse. Esto es as para
asegurar la compatibilidad con versiones de Android antiguas, ya que las notificaciones son un elemento que han
sufrido bastantes cambios en las versiones ms recientes. La clase en cuestin se llama NotificationCompat.Builder y
lo que tendremos que hacer ser crear un nuevo objeto de este tipo pasndole el contexto de la aplicacin y asignar
todas las propiedades que queramos mediante sus mtodos set().

En primer lugar estableceremos los iconos a mostrar mediante los mtodos setSmallIcon() ysetLargeIcon() que se
corresponden con los iconos mostrados a la derecha y a la izquierda del contenido de la notificacin en versiones
recientes de Android. En versiones ms antiguas tan slo se mostrar el icono pequeo a la izquierda de la
notificacin. Adems, el icono pequeo tambin se mostrar en la barra de estado superior.

A continuacin estableceremos el ttulo y el texto de la notificacin, utilizando para ello los


mtodossetContentTitle() y setContentText().

Por ltimo, estableceremos el ticker (texto que aparece por unos segundos en la barra de estado al generarse una
nueva notificacin) mediante setTicker() y el texto auxiliar (opcional) que aparecer a la izquierda del icono pequeo
de la notificacin mediante setContentInfo().
La fecha/hora asociada a nuestra notificacin se tomar automticamente de la fecha/hora actual si no se establece
nada, o bien puede utilizarse el mtodo setWhen() para indicar otra marca de tiempo. Veamos cmo quedara
nuestro cdigo por el momento:

1 NotificationCompat.Builder mBuilder =

2 new NotificationCompat.Builder(MainActivity.this)

3 .setSmallIcon(android.R.drawable.stat_sys_warning)

4 .setLargeIcon((((BitmapDrawable)getResources()

5 .getDrawable(R.drawable.ic_launcher)).getBitmap()))

6 .setContentTitle("Mensaje de Alerta")

7 .setContentText("Ejemplo de notificacin.")

8 .setContentInfo("4")

9 .setTicker("Alerta!");

El segundo paso ser establecer la actividad a la cual debemos dirigir al usuario automticamente si ste pulsa sobre
la notificacin. Para ello debemos construir un objeto PendingIntent, que ser el que contenga la informacin de la
actividad asociada a la notificacin y que ser lanzado al pulsar sobre ella. Para ello definiremos en primer lugar un
objeto Intent, indicando la clase de la actividad concreta a lanzar, que en nuestro caso ser la propia actividad
principal de ejemplo (MainActivity.class). Este intent lo utilizaremos para construir el PendingIntent final mediante el
mtodoPendingIntent.getActivity(). Por ltimo asociaremos este objeto a la notificacin mediante el
mtodo setContentIntent() del Builder. Veamos cmo quedara esta ltima parte comentada:

1 Intent notIntent =

2 new Intent(MainActivity.this, MainActivity.class);

4 PendingIntent contIntent =

5 PendingIntent.getActivity(

6 MainActivity.this, 0, notIntent, 0);

8 mBuilder.setContentIntent(contIntent);

Por ltimo, una vez tenemos completamente configuradas las opciones de nuestra notificacin podemos generarla
llamando al mtodo notify() del Notification Manager,al cual podemos acceder mediante una llamada
a getSystemService() con la constante Context.NOTIFICATION_SERVICE. Por su parte al mtodo notify() le
pasaremos como parmetro un identificador nico definido por nosotros que identifique nuestra notificacin y el
resultado del builder que hemos construido antes, que obtenemos llamando a su mtodo build().

1 NotificationManager mNotificationManager =

2 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

4 mNotificationManager.notify(NOTIF_ALERTA_ID, mBuilder.build());

Ya estamos en disposicin de probar nuestra aplicacin de ejemplo. Para ello vamos a ejecutarla en el emulador de
Android y pulsamos el botn que hemos implementado con todo el cdigo anterior. Si todo va bien, debera
aparecer en ese mismo momento nuestra notificacin en la barra de estado, con el icono y texto definidos.
Si ahora salimos de la aplicacin y desplegamos la bandeja del sistema podremos verificar el resto de informacin de
la notificacin tal como muestra la siguiente imagen:

Por ltimo, si pulsamos sobre la notificacin se debera abrir de nuevo automticamente la aplicacin de ejemplo.

En definitiva, como podis comprobar es bastante sencillo generar notificaciones en la barra de estado de Android
desde nuestras aplicaciones. Os animo a utilizar este mecanismo para notificar determinados eventos al usuario de
forma bastante visual e intuitiva.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Notificaciones en Android (III): Dilogos
by Sgoliver on 05/11/2011 in Android, Programacin

Durante este curso ya hemos visto dos de las principales alternativas a la hora de mostrar notificaciones a los
usuarios de nuestra aplicaciones: los toast y las notificaciones de la barra de estado. En este ltimo artculo sobre
notificaciones vamos a comentar otro mecanismo que podemos utilizar para mostrar o solicitar informacin puntual
al usuario. El mecanismo del que hablamos son los cuadros de dilogo.

En principio, los dilogos de Android los podremos utilizar con distintos fines, en general:

Mostrar un mensaje.

Pedir una confirmacin rpida.

Solicitar al usuario una eleccin (simple o mltiple) entre varias alternativas.

De cualquier forma, veremos tambin cmo personalizar completamente un dilogo para adaptarlo a cualquier otra
necesidad.

El uso actual de los dilogos en Android se basa en fragmets, pero por suerte tenemos toda la funcionalidad
implementada una vez ms en la librera de compatibilidad android-support-v4.jar (que debe estar incluida por
defecto en tu proyecto si lo has creado con una versin reciente del pluin de Eclipse) por lo que no tendremos
problemas al ejecutar nuestra aplicacin en versiones antiguas de Android. En este caso nos vamos a basar en la
clase DialogFragment. Para crear un dilogo lo primero que haremos ser crear una nueva clase que herede
de DialogFragment y sobrescribiremos uno de sus mtodos onCreateDialog(), que ser el encargado de construir el
dilogo con las opciones que necesitemos.

La forma de construir cada dilogo depender de la informacin y funcionalidad que necesitemos. A continuacin
mostrar algunas de las formas ms habituales.

Dilogo de Alerta

Este tipo de dilogo se limita a mostrar un mensaje sencillo al usuario, y un nico botn de OK para confirmar su
lectura. Lo construiremos mediante la clase AlertDialog, y ms concretamente su subclase AlertDialog.Builder, de
forma similar a las notificaciones de barra de estado que ya hemos comentado en el captulo anterior. Su utilizacin
es muy sencilla, bastar con crear un objeto de tipo AlertDialog.Builder y establecer las propiedades del dilogo
mediante sus mtodos correspondientes: ttulo [setTitle()], mensaje [setMessage()] y el texto y comportamiento del
botn [setPositiveButton()]. Veamos un ejemplo:

1 public class DialogoAlerta extends DialogFragment {

2 @Override

3 public Dialog onCreateDialog(Bundle savedInstanceState) {

5 AlertDialog.Builder builder =

6 new AlertDialog.Builder(getActivity());

8 builder.setMessage("Esto es un mensaje de alerta.")

9 .setTitle("Informacin")

10 .setPositiveButton("OK", new DialogInterface.OnClickListener() {

11 public void onClick(DialogInterface dialog, int id) {


12 dialog.cancel();

13 }

14 });

15

16 return builder.create();

17 }

18 }

Como vemos, al mtodo setPositiveButton() le pasamos como argumentos el texto a mostrar en el botn, y la
implementacin del evento onClick en forma de objeto OnClickListener. Dentro de este evento, nos limitamos a
cerrar el dilogo mediante su mtodo cancel(), aunque podramos realizar cualquier otra accin.

Para lanzar este dilogo por ejemplo desde nuestra actividad principal, obtendramos una referencia alFragment
Manager mediante una llamada a getSupportFragmentManager(), creamos un nuevo objeto de tipo DialogoAlerta y
por ltimo mostramos el dilogo mediante el mtodo show()pasndole la referencia al fragment manager y una
etiqueta identificativa del dilogo.

1 btnAlerta.setOnClickListener(new View.OnClickListener() {

2 public void onClick(View v) {

3 FragmentManager fragmentManager = getSupportFragmentManager();

4 DialogoAlerta dialogo = new DialogoAlerta();

5 dialogo.show(fragmentManager, "tagAlerta");

6 }

7 });

El aspecto de nuestro dilogo de alerta sera el siguiente:

Dilogo de Confirmacin

Un dilogo de confirmacin es muy similar al anterior, con la diferencia de que lo utilizaremos para solicitar al
usuario que nos confirme una determinada accin, por lo que las posibles respuestas sern del tipo S/No.

La implementacin de estos dilogos ser prcticamente igual a la ya comentada para las alertas, salvo que en esta
ocasin aadiremos dos botones, uno de ellos para la respuesta afirmativa (setPositiveButton()), y el segundo para la
respuesta negativa (setNegativeButton()). Veamos un ejemplo:

1 public class DialogoConfirmacion extends DialogFragment {

2 @Override
3 public Dialog onCreateDialog(Bundle savedInstanceState) {

5 AlertDialog.Builder builder =

6 new AlertDialog.Builder(getActivity());

8 builder.setMessage("Confirma la accin seleccionada?")

9 .setTitle("Confirmacion")

10 .setPositiveButton("Aceptar", new DialogInterface.OnClickListener() {

11 public void onClick(DialogInterface dialog, int id) {

12 Log.i("Dialogos", "Confirmacion Aceptada.");

13 dialog.cancel();

14 }

15 })

16 .setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {

17 public void onClick(DialogInterface dialog, int id) {

18 Log.i("Dialogos", "Confirmacion Cancelada.");

19 dialog.cancel();

20 }

21 });

22

23 return builder.create();

24 }

25 }

En este caso, generamos a modo de ejemplo dos mensajes de log para poder verificar qu botn pulsamos en el
dilogo. El aspecto visual de nuestro dilogo de confirmacin sera el siguiente:

Dilogo de Seleccin

Cuando las opciones a seleccionar por el usuario no son slo dos, como en los dilogos de confirmacin, sino que el
conjunto es mayor podemos utilizar los dilogos de seleccin para mostrar una lista de opciones entre las que el
usuario pueda elegir.
Para ello tambin utilizaremos la clase AlertDialog, pero esta vez no asignaremos ningn mensaje ni definiremos las
acciones a realizar por cada botn individual, sino que directamente indicaremos la lista de opciones a mostrar
(mediante el mtodo setItems()) y proporcionaremos la implementacin del evento onClick() sobre dicha lista
(mediante un listener de tipoDialogInterface.OnClickListener), evento en el que realizaremos las acciones oportunas
segn la opcin elegida. La lista de opciones la definiremos como un array tradicional. Veamos cmo:

1 public class DialogoSeleccion extends DialogFragment {

2 @Override

3 public Dialog onCreateDialog(Bundle savedInstanceState) {

5 final String[] items = {"Espaol", "Ingls", "Francs"};

7 AlertDialog.Builder builder =

8 new AlertDialog.Builder(getActivity());

10 builder.setTitle("Seleccin")

11 .setItems(items, new DialogInterface.OnClickListener() {

12 public void onClick(DialogInterface dialog, int item) {

13 Log.i("Dialogos", "Opcin elegida: " + items[item]);

14 }

15 });

16

17 return builder.create();

18 }

19 }

En este caso el dilogo tendr un aspecto similar a la interfaz mostrada para los controles Spinner.

Este dilogo permite al usuario elegir entre las opciones disponibles cada vez que se muestra en pantalla. Pero, y si
quisiramos recordar cul es la opcin u opciones seleccionadas por el usuario para que aparezcan marcadas al
visualizar de nuevo el cuadro de dilogo? Para ello podemos utilizar los
mtodos setSingleChoiceItems() o setMultiChiceItems(), en vez de el setItems()utilizado anteriormente. La diferencia
entre ambos mtodos, tal como se puede suponer por su nombre, es que el primero de ellos permitir una seleccin
simple y el segundo una seleccin mltiple, es decir, de varias opciones al mismo tiempo, mediante
controles CheckBox.

La forma de utilizarlos es muy similar a la ya comentada. En el caso de setSingleChoiceItems(), el mtodo tan slo se
diferencia de setItems() en que recibe un segundo parmetro adicional que indica el ndice de la opcin marcada por
defecto. Si no queremos tener ninguna de ellas marcadas inicialmente pasaremos el valor -1.

1 builder.setTitle("Seleccin")

2 .setSingleChoiceItems(items, -1,

3 new DialogInterface.OnClickListener() {

4 public void onClick(DialogInterface dialog, int item) {

5 Log.i("Dialogos", "Opcin elegida: " + items[item]);

6 }

7 });

De esta forma conseguiramos un dilogo como el de la siguiente imagen:

Si por el contrario optamos por la opcin de seleccin mltiple, la diferencia principal estar en que tendremos que
implementar un listener del tipoDialogInterface.OnMultiChoiceClickListener. En este caso, en el
evento onClickrecibiremos tanto la opcin seleccionada (item) como el estado en el que ha quedado (isChecked).
Adems, en esta ocasin, el segundo parmetro adicional que indica el estado por defecto de las opciones ya no ser
un simple nmero entero, sino que tendr que ser un array de booleanos. En caso de no querer ninguna opcin
seleccionada por defecto pasaremos el valor null.

1 builder.setTitle("Seleccin")

2 .setMultiChoiceItems(items, null,

3 new DialogInterface.OnMultiChoiceClickListener() {

4 public void onClick(DialogInterface dialog, int item, boolean isChecked) {

5 Log.i("Dialogos", "Opcin elegida: " + items[item]);

6 }

7 });

Y el dilogo nos quedara de la siguiente forma:


Tanto si utilizamos la opcin de seleccin simple como la de seleccin mltiple, para salir del dilogo tendremos que
pulsar la tecla Atrs de nuestro dispositivo.

Dilogos Personalizados

Por ltimo, vamos a comentar cmo podemos establecer completamente el aspecto de un cuadro de dilogo. Para
esto vamos a actuar como si estuviramos definiendo la interfaz de una actividad, es decir, definiremos un layout
XML con los elementos a mostrar en el dilogo. En mi caso voy a definir un layout de ejemplo
llamado dialog_personal.xml que colocar como siempre en la carpetares/layout. Contendr por ejemplo una
imagen a la izquierda y dos lneas de texto a la derecha:

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

2 android:layout_width="match_parent"

3 android:layout_height="match_parent"

4 android:orientation="horizontal"

5 android:padding="3dp" >

7 <ImageView

8 android:id="@+id/imageView1"

9 android:layout_width="wrap_content"

10 android:layout_height="wrap_content"

11 android:src="@drawable/ic_launcher" />

12

13 <LinearLayout

14 android:layout_width="wrap_content"

15 android:layout_height="match_parent"

16 android:orientation="vertical"

17 android:padding="3dp">

18

19 <TextView

20 android:id="@+id/textView1"
21 android:layout_width="wrap_content"

22 android:layout_height="wrap_content"

23 android:text="@string/dialogo_linea_1" />

24

25 <TextView

26 android:id="@+id/textView2"

27 android:layout_width="wrap_content"

28 android:layout_height="wrap_content"

29 android:text="@string/dialogo_linea_2" />

30

31 </LinearLayout>

32

33 </LinearLayout>

Por su parte, en el mtodo onCreateDialog() correspondiente utilizaremos el mtodo setView()del builder para
asociarle nuestro layout personalizado, que previamente tendremos que inflar como ya hemos comentado otras
veces utilizando el mtodo inflate(). Finalmente podremos incluir botones tal como vimos para los dilogos de alerta
o confirmacin. En este caso de ejemplo incluiremos un botn de Aceptar.

1 public class DialogoPersonalizado extends DialogFragment {

2 @Override

3 public Dialog onCreateDialog(Bundle savedInstanceState) {

5 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

6 LayoutInflater inflater = getActivity().getLayoutInflater();

8 builder.setView(inflater.inflate(R.layout.dialog_personal, null))

9 .setPositiveButton("Aceptar", new DialogInterface.OnClickListener() {

10 public void onClick(DialogInterface dialog, int id) {

11 dialog.cancel();

12 }

13 });

14

15 return builder.create();

16 }

17 }

De esta forma, si ejecutamos de nuevo nuestra aplicacin de ejemplo y lanzamos el dilogo personalizado veremos
algo como lo siguiente:
Y con esto terminamos con el apartado dedicado a notificaciones en Android. Hemos comentado los tres principales
mecanismos de Android a la hora de mostrar mensajes y notificaciones al usuario (Toast, Barra de Estado, y
Dilogos), todos ellos muy tiles para cualquier tipo de aplicacin y que es importante conocer bien para poder
decidir entre ellos dependiendo de las necesidades que tengamos.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Depuracin en Android
Depuracin en Android: Logging
by Sgoliver on 28/04/2011 in Android, Programacin

Hacemos un pequeo alto en el camino en el Curso de Programacin Android para hablar de un tema que, si bien no
es especfico de Android, s nos va a resultar bastante til a la hora de explorar otras caractersticas de la plataforma.

Una de las tcnicas ms tiles a la hora de depurar y/o realizar el seguimiento de aplicaciones sobre cualquier
plataforma es la creacin de logs de ejecucin. Android por supuesto no se queda atrs y nos proporciona tambin
su propio servicio y API de logging a travs de la clase android.util.Log.

En Android, todos los mensajes de log llevarn asociada la siguiente informacin:

Fecha/Hora del mensaje.

Criticidad. Nivel de gravedad del mensaje (se detalla ms adelante).

PID. Cdigo interno del proceso que ha introducido el mensaje.

Tag. Etiqueta identificativa del mensaje (se detalla ms adelante).

Mensaje. El texto completo del mensaje.

De forma similar a como ocurre con otros frameworks de logging, en Android los mensajes de log se van a clasificar
por su criticidad, existiendo as varias categorias (ordenadas de mayor a menor criticidad):

1. Error

2. Warning

3. Info

4. Debug

5. Verbose

Para cada uno de estos tipos de mensaje existe un mtodo esttico independiente que permite aadirlo al log de la
aplicacin. As, para cada una de las categoras anteriores tenemos disponibles los
mtodose(), w(), i(), d() y v() respectivamente.

Cada uno de estos mtodos recibe como parmetros la etiqueta (tag) y el texto en s del mensaje. Como etiqueta de
los mensajes, aunque es un campo al que podemos pasar cualquier valor, suele utilizarse el nombre de la aplicacin
o de la actividad concreta que genera el mensaje. Esto nos permitir ms tarde crear filtros personalizados para
identificar y poder visualizar nicamente los mensajes de log que nos interesan, entre todos los generados por
Android [que son muchos] durante la ejecucin de la aplicacin.

Hagamos un miniprograma de ejemplo para ver cmo fuenciona esto. El programa ser tan simple como aadir
varios mensajes de log dentro del mismo onCreate de la actividad principal y ver qu ocurre. Os muestro el cdigo
completo:

1 public class LogsAndroid extends Activity {

3 private static final String LOGTAG = "LogsAndroid";

4
5 @Override

6 public void onCreate(Bundle savedInstanceState) {

7 super.onCreate(savedInstanceState);

8 setContentView(R.layout.main);

10 Log.e(LOGTAG, "Mensaje de error");

11 Log.w(LOGTAG, "Mensaje de warning");

12 Log.i(LOGTAG, "Mensaje de informacin");

13 Log.d(LOGTAG, "Mensaje de depuracin");

14 Log.v(LOGTAG, "Mensaje de verbose");

15 }

16 }

Si ejecutamos la aplicacin anterior en el emulador veremos cmo se abre la pantalla principal que crea Eclipse por
defecto y aparentemente no ocurre nada ms. Dnde podemos ver los mensajes que hemos aadido al log? Pues
para ver los mensajes de log nos tenemos que ir a la perspectiva de Eclipse llamada DDMS. Una vez en esta
perspectiva, podemos acceder a los mensajes de log en la parte inferior de la pantalla, en una vista llamada LogCat.
En esta ventana se muestran todos los mensajes de log que genera Android durante la ejecucin de la aplicacin,
que son muchos, pero si buscamos un poco en la lista encontraremos los generados por nuestra aplicacin, tal como
se muestra en la siguiente imagen (click para ampliar):

Como se puede observar, para cada mensaje se muestra toda la informacin que indicamos al principio del artculo,
adems de estar diferenciados por un color distinto segn su criticidad.

En este caso de ejemplo, los mensajes mostrados son pocos y fciles de localizar en el log, pero para una aplicacin
real, el nmero de estos mensajes puede ser mucho mayor y aparecer intercalados caticamente entre los dems
mensajes de Android. Para estos casos, la ventada de LogCat ofrece una serie de funcionalidades para facilitar la
visualizacin y bsqueda de determinados mensajes.

Por ejemplo, podemos restringir la lista para que slo muestre mensajes con una determinada criticidad mnima.
Esto se consigue pulsando alguno de los 5 primeros botones que se observan en la parte superior derecha de la
ventana de log. As, si por ejemplo pulsamos sobre el botn de la categora Info (en verde), en la lista slo se
mostrarn los mensajes con criticidad Error, Warning e Info.

Otro mtodo de filtrado ms interesante es la definicin de filtros personalizados (botn + verde), donde podemos
filtrar la lista para mostrar nicamente los mensajes con un PID o Tag determinado. Si hemos utilizado como
etiqueta de los mensajes el nombre de nuestra aplicacin o de nuestras actividades esto nos proporcionar una
forma sencilla de visualizar slo los mensajes generados por nuestra aplicacin.

As, para nuestro ejemplo, podramos crear un filtro indicando como Tag la cadena LogsAndroid, tal como se
muestra en la siguiente imagen:
Esto crear una nueva ventana de log con el nombre que hayamos especificado en el filtro, donde slo aparecern
nuestros 5 mensajes de log de ejemplo (click para ampliar):

Por ltimo, cabe mencionar que existe una variante de cada uno de los mtodos de la clase Log que recibe un
parmetro ms en el que podemos pasar un objeto de tipo excepcin. Con esto conseguimos que, adems del
mensaje de log indicado, se muestre tambin la traza de error generada con la excepcin.

Veamos esto con un ejemplo, y para ello vamos a forzar un error de divisin por cero, vamos a capturar la excepcin
y vamos a generar un mensaje de log con la variante indicada:

1 try

2 {

3 int a = 1/0;

4 }

5 catch(Exception ex)

6 {

7 Log.e(LOGTAG, "Divisin por cero!", ex);

8 }

Si volvemos a ejecutar la aplicacin y vemos el log generado, podermos comprobar cmo se muestra la traza de
error corespondiente generada con la excepcin (click para ampliar).

En definitiva, podemos comprobar como la generacin de mensajes de log puede ser una herramienta sencilla pero
muy efectiva a la hora de depurar aplicaciones que no se ajustan mucho a la depuracin paso a paso, o simplemente
para generar trazas de ejecucin de nuestras aplicaciones para comprobar de una forma sencilla cmo se
comportan.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Acceso a Servicios Web
Acceso a Servicios Web SOAP en Android (1/2)
by Sgoliver on 27/02/2012 in Android, Programacin

En este primer artculo que vamos a dedicar a los servicios web dentro del Curso de Programacin Android nos
vamos a centrar en los servicios web que utilizan el estndar SOAP como mecanismo de comunicacin.

A diferencia de otros tutoriales, no slo vamos describir cmo acceder a este tipo de servicios desde una aplicacin
Android, sino que tambin veremos como crear un servicio web SOAP mediante ASP.NET para acceder a una base de
datos SQL Server. De esta forma pretendo ilustrar la arquitectura completa de una aplicacin Android que acceda
a datos almacenados en un servidor de base de datos externo. Nota: Aunque intentar aportar el mximo nmero
de detalles, es imposible abarcarlo todo en un solo artculo, por lo que el texto supondr unos conocimientos
mnimos de Visual Studio y del lenguaje C#.

Como caso de ejemplo, vamos a crear una aplicacin sencilla capaz de gestionar un listado de clientes que
contendr el nombre y telfono de cada uno de ellos. Nuestra aplicacin ser capaz de consultar el listado actual de
clientes almacenados en el servidor externo y de insertar nuevos clientes en la base de datos. Como siempre, se
trata de un ejemplo muy sencillo pero creo que lo suficientemente completo como para que sirva de base para crear
otras aplicaciones ms complejas.

Como software necesario, en este caso utilizar Visual Studio 2010 y SQL Server 2008 R2 para crear el servicio web y
la base de datos respectivamente. Podis descargar de forma gratuita las versionesExpress de ambos productos
(ms que suficientes para crear una aplicacin como la que describiremos en este artculo) desde la web oficial de
Microsoft. Tambin es recomendable instalar SQL Server 2008 Management Studio Express, descargable tambin de
forma gratuita desde esta web. Esta aplicacin no es ms que un gestor grfico para acceder y manipular nuestras
bases de datos SQL Server con total comodidad.

Vamos comenzar por la base de todo el sistema, y esto es la base de datos a la que acceder el servicio web y, a
travs de ste, tambin la aplicacin Android que crearemos ms adelante. Para ello abrimos SQL Server
Management Studio, nos conectamos a nuestro servidor SQL Server local, y pulsamos sobre la seccin Databases
del rbol de objetos que aparece a la izquierda. Sobre esta carpeta podemos acceder a la opcin New Database
del men contextual para crear una nueva base de datos.

En el cuadro de dilogo que aparece tan slo indicaremos el nombre de la nueva base de datos, en mi caso la
llamar DBCLIENTES, y dejaremos el resto de opciones con sus valores por defecto.
Desplegamos el rbol de carpetas de nuestra recin creada base de datos DBCLIENTES y sobre la carpeta Tables
ejecutamos la opcin New table para crear una nueva tabla.

Vamos a aadir slo 3 campos a la tabla:

IdCliente, de tipo int, que ser un cdigo nico identificativo del cliente.

Nombre, de tipo nvarchar(50), que contendr el nombre del cliente.

Telefono, de tipo int, que contendr el telfono del cliente.

Marcaremos adems el campo IdCliente como clave principal de la tabla, y tambin como campo de identidad
autoincremental, de modo que se calcule automticamente cada vez que insertemos un nuevo cliente.
Con esto ya tenemos nuestra tabla finalizada, por lo que slo nos queda guardarla con el nombre que deseemos,
que para este ejemplo ser Clientes.

Hecho, ya tenemos nuestra base de datos SQL Server creada y una tabla preparada para almacenar los datos
asociados a nuestros clientes. El siguiente paso ser crear el servicio web que manipular los datos de esta tabla.

Para crear el servicio abriremos Visual Studio 2010 y crearemos un nuevo proyecto web en C# utilizando la plantilla
ASP.NET Empty Web Application. En un alarde de originalidad lo llamaremos ServicioWebSoap.

Una vez creado el proyecto, aadiremos a ste un nuevo servicio web mediante el men Project / Add new item.
Lo llamaremos ServicioClientes.asmx.

Una vez aadido aparecer en pantalla el cdigo fuente por defecto del nuevo servicio web, que contiene un nico
mtodo de ejemplo llamado HelloWorld(). Este mtodo podemos eliminarlo ya que no nos servir de nada, y
adems modificaremos el atributo WebService de la clase para indicar que el namespace ser http://sgoliver.net/
(en vuestro caso podis indicar otro valor). Con esto, nos quedara un cdigo base como ste:

1 namespace ServicioWebSoap

2 {
3 /// <summary>

4 /// Summary description for ServicioClientes

5 /// </summary>

6 [WebService(Namespace = "http://sgoliver.net/")]

7 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

8 [System.ComponentModel.ToolboxItem(false)]

10 public class ServicioClientes : System.Web.Services.WebService

11 {

12 //Mtodos del servicio web

13 //...

14 }

15 }

Pues bien, dentro de esta clase ServicioClientes es donde aadiremos todos los mtodos pblicos que queramos
tener accesibles a travs de nuestro servicio web, siempre precedidos por el atributo[WebMethod] como veremos
en breve. Para nuestro ejemplo vamos a crear tres mtodos, el primero para obtener el listado completo de clientes
almacenados en la base de datos, y los otros dos para insertar nuevos clientes (ms adelante explicar por qu dos,
aunque adelanto que es tan slo por motivos didcticos).

Antes de crear estos mtodos, vamos a crear una nueva clase sencilla que nos sirva para encapsular los datos de un
cliente. La aadiremos mediante la opcin Project / Add class de Visual Studio y la llamaremos Cliente.cs. Esta
clase contendr nicamente los 3 campos que ya comentamos al crear la base de datos y dos constructores, uno de
ellos por defecto que tan solo inicializar los campos y otro con parmetros para crear clientes a partir de sus datos
identificativos. El cdigo de la clase es muy sencillo, y tan solo cabe mencionar que definiremos sus tres atributos
como propiedades automticas de C# utilizando para ello la notacin abreviada {get; set;}

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.Web;

6 namespace ServicioWebSoap

7 {

8 public class Cliente

9 {

10 public int Id {get; set;}

11 public string Nombre {get; set;}

12 public int Telefono {get; set;}

13
14 public Cliente()

15 {

16 this.Id = 0;

17 this.Nombre = "";

18 this.Telefono = 0;

19 }

20

21 public Cliente(int id, string nombre, int telefono)

22 {

23 this.Id = id;

24 this.Nombre = nombre;

25 this.Telefono = telefono;

26 }

27 }

28 }

Vamos ahora a escribir el primero de los mtodos que haremos accesible a travs de nuestro servicio web. Lo
llamaremos NuevoCliente(), recibir como parmetros de entrada un nombre y un telfono, y se encargar de
insertar un nuevo registro en nuestra tabla de clientes con dichos datos. Recordemos que el ID del cliente no ser
necesario insertarlo de forma explcita ya que lo hemos definido en la base de datos como campo autoincremental.
Para el trabajo con la base de datos vamos a utilizar la API clsica de ADO.NET, aunque podramos utilizar cualquier
otro mcanismo de acceso a datos, como por ejemplo Entity Framework, NHibernate, etc.

De esta forma, el primer paso ser crear una conexin a SQL Server mediante la claseSQLConnection, pasando como
parmetro la cadena de conexin correspondiente (en vuestro caso tendris que modificarla para adaptarla a
vuestro entorno). Tras esto abriremos la conexin mediante una llamada al mtodo Open(), definiremos el comando
SQL que queremos ejecutar creando un objetoSQLCommand. Ejecutaremos el comando llamando al
mtodo ExecuteNonQuery() recogiendo el resultado en una variable, y finalmente cerraremos la conexin llamando
a Close(). Por ltimo devolveremos el resultado del comando SQL como valor de retorno del mtodo web.

Como podis ver en el cdigo siguiente, los valores a insertar en la base de datos los hemos especificado en la
consulta SQL como parmetros variable (precedidos por el carcter @). Los valores de estos parmetros los
definimos y aadimos al comando SQL mediante el mtodo Add() de su propiedad Parameters. Esta opcin es ms
recomendable que la opcin clsica de concatenar directamente la cadena de texto de la sentencia SQL con los
parmetros variables, ya que entre otras cosas servir para evitar [en gran medida] posibles ataques de inyeccin
SQL. El resultado devuelto por este mtodo ser el nmero de registros afectados por la sentencia SQL ejecutada,
por lo que para verificar si se ha ejecutado correctamente bastar con comprobar que el resultado es igual a 1.

1 [WebMethod]

2 public int NuevoClienteSimple(string nombre, int telefono)

3 {

4 SqlConnection con =

5 new SqlConnection(
6 @"Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBCLIENTES;Integrated Security=True");

8 con.Open();

10 string sql = "INSERT INTO Clientes (Nombre, Telefono) VALUES (@nombre, @telefono)";

11

12 SqlCommand cmd = new SqlCommand(sql, con);

13

14 cmd.Parameters.Add("@nombre", System.Data.SqlDbType.NVarChar).Value = nombre;

15 cmd.Parameters.Add("@telefono", System.Data.SqlDbType.Int).Value = telefono;

16

17 int res = cmd.ExecuteNonQuery();

18

19 con.Close();

20

21 return res;

22 }

En el cdigo anterior, podis ver que hemos precedido el mtodo con el atributo [WebMethod]. Con este atributo
estamos indicando que el mtodo ser accesible a travs de nuestro servicio web y podr ser llamado desde
cualquier aplicacin que se conecte con ste.

La siguiente operacin que vamos a aadir a nuestro servicio web ser la que nos permita obtener el listado
completo de clientes registrados en la base de datos. Llamaremos al mtodoListadoClientes() y devolver un array
de objetos de tipo Cliente. El cdigo del mtodo ser muy similar al ya comentado para la operacin de insercin,
con la nica diferencia de que en esta ocasin la sentencia SQL ser obviamente un SELECT y que utilizaremos un
objeto SqlDataReaderpara leer los resultados devueltos por la consulta. Los registros ledos los iremos aadiendo a
una lista de tipo List<Clientes> y una vez completada la lectura convertiremos esta lista en un array de clientes
llamando al mtodo ToArray(). Este ltimo array ser el que devolveremos como resultado del mtodo. Veamos el
cdigo completo del mtodo:

1 [WebMethod]

2 public Cliente[] ListadoClientes()

3 {

4 SqlConnection con =

5 new SqlConnection(

6 @"Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBCLIENTES;Integrated Security=True");

8 con.Open();

9
10 string sql = "SELECT IdCliente, Nombre, Telefono FROM Clientes";

11

12 SqlCommand cmd = new SqlCommand(sql, con);

13

14 SqlDataReader reader = cmd.ExecuteReader();

15

16 List<Cliente> lista = new List<Cliente>();

17

18 while (reader.Read())

19 {

20 lista.Add(

21 new Cliente(reader.GetInt32(0),

22 reader.GetString(1),

23 reader.GetInt32(2)));

24 }

25

26 con.Close();

27

28 return lista.ToArray();

29 }

Por ltimo, como dijimos al principio, vamos a aadir un tercer mtodo web con fines puramente didcticos. Si os
fijis en los dos mtodos anteriores, veris que en uno de los casos devolvemos como resultado un valor simple, un
nmero entero, y en el otro caso un objeto complejo, en concreto un array de objetos de tipo Cliente. Sin embargo,
ninguno de ellos recibe como parmetro un tipo complejo, tan slo valores simples (enteros y strings). Esto no tiene
mucha relevancia en el cdigo de nuestro servicio web, pero s tiene ciertas peculiaridades a la hora de realizar la
llamada al servicio desde la aplicacin Android. Por lo que para poder explicar esto ms adelante aadiremos un
nuevo mtodo de insercin de clientes que, en vez de recibir los parmetros de nombre y telfono por separado,
recibir como dato de entrada un objeto Cliente.

El cdigo de este mtodo, que llamaremos NuevoClienteObjeto(), ser exactamente igual al anterior mtodo de
insercin, con la nica diferencia de los parmetros de entrada, por lo que no nos detendremos en comentar nada
ms.

1 [WebMethod]

2 public int NuevoClienteObjeto(Cliente cliente)

3 {

4 SqlConnection con = new SqlConnection(@"Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBCLIENTES;Integ

6 con.Open();
7

8 string sql = "INSERT INTO Clientes (Nombre, Telefono) VALUES (@nombre, @telefono)";

10 SqlCommand cmd = new SqlCommand(sql, con);

11

12 cmd.Parameters.Add("@nombre", System.Data.SqlDbType.NVarChar).Value = cliente.Nombre;

13 cmd.Parameters.Add("@telefono", System.Data.SqlDbType.Int).Value = cliente.Telefono;

14

15 int res = cmd.ExecuteNonQuery();

16

17 con.Close();

18

19 return res;

20 }

Y con esto hemos finalizado nuestro servicio web. Podemos probar su funcionamiento con la pgina de prueba que
proporciona ASP.NET al ejecutar el proyecto en Visual Studio. Si ejecutamos el proyecto se abrir automticamente
un explorador web que mostrar una pgina con todas las operaciones que hemos definido en el servicio web.

Si pulsamos sobre cualquiera de ellas pasaremos a una nueva pgina que nos permitir dar valores a sus parmetros
y ejecutar el mtodo correspondiente para visualizar sus resultados. Si pulsamos por ejemplo en la
operacin NuevoCliente(string, int) llegaremos a esta pgina:

Aqu podemos dar valores a los dos parmetros y ejecutar el mtodo (botn Invoke), lo que nos devolver la
respuesta codificada en un XML segn el estandar SOAP.
Como podis comprobar, en principio el XML devuelto no es fcil de interpretar, pero esto es algo que no debe
preocuparnos demasiado ya que en principio ser transparente para nosotros, las libreras que utilizaremos ms
adelante en Android para la llamada a servicios SOAP se encargarn de parsearconvenientemente estas respuestas y
de darnos tan slo aquella parte que necesitamos.

En el siguiente artculo nos ocuparemos de la construccin de una aplicacin Android que sea capaz de conectarse a
este servicio web y de llamar a los mtodos que hemos definido para insertar y recuperar clientes de nuestra base
de datos. Veremos adems cmo podemos ejecutar y probar en local todo el sistema de forma que podamos
comprobar que todo funciona como esperamos.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Acceso a Servicios Web SOAP en Android (2/2)
by Sgoliver on 27/02/2012 in Android, Programacin

En el artculo anterior del curso vimos cmo construir un servicio web SOAP haciendo uso de ASP.NET y una base de
datos externa SQL Server. En este segundo artculo veremos cmo podemos acceder a este servicio web desde una
aplicacin Android y probaremos todo el sistema en local para verificar su correcto funcionamiento.

En primer lugar hay que empezar diciendo que Android no incluye de serie ningn tipo de soporte para el acceso a
servicios web de tipo SOAP. Es por esto por lo que vamos a utilizar una librera externa para hacernos ms fcil esta
tarea. Entre la oferta actual, la opcin ms popular y ms utilizada es la librera ksoap2-android. Esta librera es
un fork, especialmente adaptado para Android, de la antigua librera kSOAP2. Este framework nos permitir de
forma relativamente fcil y cmoda utilizar servicios web que utilicen el estndar SOAP. La ltima versin de esta
librera en el momento de escribir este artculo es la 2.6.0, que puede descargarse desde este enlace.

Agregar esta librera a nuestro proyecto Android es muy sencillo. Si tenemos una versin reciente del plugin de
Android para Eclipse, una vez tenemos creado el proyecto en Android bastar con copiar el archivo .jar en la
carpeta libs de nuestro proyecto. Si tu versin del plugin es ms antigua es posible que tengas que adems aadir la
librera al path del proyecto. Para ello accederemos al men Project / Properties y en la ventana de propiedades
accederemos a la seccin Java Build Path. En esta seccin accederemos a la solapa Libraries y pulsaremos el
botn Add External JARs. Aqu seleccionamos el fichero jar de la librera ksoap2-android (en este caso ksoap2-
android-assembly-3.6.0-jar-with-dependencies.jar) y listo, ya tenemos nuestro proyecto preparado para hacer uso
de la funcionalidad aportada por la librera.

Como aplicacin de ejemplo, vamos a crear una aplicacin sencilla que permita aadir un nuevo usuario a la base de
datos. Para ello aadiremos a la vista principal dos cuadros de texto para introducir el nombre y telfono del nuevo
cliente (en mi caso se llamarn txtNombre y txtTelefonorespectivamente) y un botn (en mi caso btnEnviar) que
realice la llamada al mtodo NuevoClientedel servicio web pasndole como parmetros los datos introducidos en los
cuadros de texto anteriores.

No voy a mostrar todo el cdigo necesario para crear esta vista y obtener las referencias a cada control porque no
tiene ninguna particularidad sobre lo ya visto en multitud de ocasiones en artculos anteriores del curso (en
cualquier caso al final del artculo podis descargar todo el cdigo fuente para su consulta). Lo que nos interesa en
este caso es la implementacin del evento onClick del botnbtnEnviar, que ser el encargado de comunicarse con el
servicio web y procesar el resultado.

Lo primero que vamos a hacer en este evento es definir, por comodidad, cuatro constantes que nos servirn en
varias ocasiones durante el cdigo:

NAMESPACE. Espacio de nombres utilizado en nuestro servicio web.

URL. Direccin URL para realizar la conexin con el servicio web.

METHOD_NAME. Nombre del mtodo web concreto que vamos a ejecutar.

SOAP_ACTION. Equivalente al anterior, pero en la notacin definida por SOAP.

Aunque los valores se podran ms o menos intuir, para conocer exactamente los valores que debemos asignar a
estas constantes vamos a ejecutar una vez ms el proyecto de Visual Studio que construimos en el artculo anterior y
vamos a acceder a la pgina de prueba del mtodo NuevoCliente. Veremos algo parecido a lo siguiente:
En la imagen anterior se muestran resaltados en rojo los valores de las cuatro constantes a definir, que en nuestro
caso concreto quedaran de la siguiente forma:

1 String NAMESPACE = "http://sgoliver.net/";

2 String URL="http://10.0.2.2:1473/ServicioClientes.asmx";

3 String METHOD_NAME = "NuevoClienteSimple";

4 String SOAP_ACTION = "http://sgoliver.net/NuevoClienteSimple";

Como podis comprobar, y esto es algo importante, en la URL he sustituido el nombre de mquinalocalhost por su
direccin IP equivalente, que en el caso de aplicaciones Android ejecutadas en el emulador se corresponde con la
direccin 10.0.2.2, en vez de la clsica 127.0.0.1. Adems debes verificar que el puerto coincide con el que ests
utilizando en tu mquina. En mi caso el servicio se ejecuta sobre el puerto 1473, pero es posible que en tu caso el
nmero sea distinto.

Los siguientes pasos del proceso sern crear la peticin SOAP al servicio web, enviarla al servidor y recibir la
respuesta. Aunque ya dijimos que todo este proceso sera casi transparente para el programador, por ser sta la
primera vez que hablamos del tema me voy a detener un poco ms para intentar que entendamos lo que estamos
haciendo y no solo nos limitemos a copiar/pegar trozos de cdigo que no sabemos lo que hacen.

Volvamos a la pgina de prueba del mtodo web NuevoCliente. Justo debajo de la seccin donde se solicitan los
parmetros a pasar al mtodo se incluye tambin un XML de muestra de cmo tendra que ser nuestra peticin al
servidor si tuviramos que construirla a mano. Echmosle un vistazo:

Una vez ms he marcado varias zonas sobre la imagen, correspondientes a las tres partes principales de una peticin
de tipo SOAP. Empezando por la parte interna del XML, en primer lugar encontramos los datos de la peticin en s
(Request) que contiene el nombre del mtodo al que queremos llamar, y los nombres y valores de los parmetros en
entrada. Rodeando a esta informacin se aaden otra serie de etiquetas y datos a modo de contenedor estndar que
suele recibir el nombre de Envelope. La informacin indicada en este contenedor no es especfica de nuestra llamada
al servicio, pero s contiene informacin sobre formatos y esquemas de validacin del estndar SOAP. Por ltimo,
durante el envo de esta peticin SOAP al servidor mediante el protocolo HTTP se aaden determinados
encabezados como los que veis en la imagen. Todo esto junto har que el servidor sea capaz de interpretar
correctamente nuestra peticin SOAP, se llame al mtodo web correcto, y se devuelva el resultado en un formato
similar al anterior que ya veremos ms adelante. Aclarada un poco la estructura y funcionamiento general de una
peticin SOAP veamos lo sencillo que resulta realizarla desde nuestra aplicacin Android.

En primer lugar crearemos la peticin (request) a nuestro mtodo NuevoCliente. Para ello crearemos un nuevo
objeto SoapObject pasndole el namespace y el nombre del mtodo web. A esta peticin tendremos que asociar los
parmetros de entrada mediante el mtodo addProperty() al que pasaremos los nombres y valores de los
parmetros (que en nuestro caso se obtendrn de los cuadros de texto de la vista principal).

1 SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

3 request.addProperty("nombre", txtNombre.getText().toString());

4 request.addProperty("telefono", txtTelefono.getText().toString());

El segundo paso ser crear el contenedor SOAP (envelope) y asociarle nuestra peticin. Para ello crearemos un
nuevo objeto SoapSerializationEnvelope indicando la versin de SOAP que vamos a usar (versin 1.1 en nuestro
caso, como puede verse en la imagen anterior). Indicaremos adems que se trata de un servicio web .NET activando
su propiedad dotNet. Por ltimo, asociaremos la peticin antes creada a nuestro contenedor llamando al
mtodo setOutputSoapObject().

1 SoapSerializationEnvelope envelope =

2 new SoapSerializationEnvelope(SoapEnvelope.VER11);

4 envelope.dotNet = true;

6 envelope.setOutputSoapObject(request);

Como tercer paso crearemos el objeto que se encargar de realizar la comunicacin HTTP con el servidor, de
tipo HttpTransportSE, al que pasaremos la URL de conexin a nuestro servicio web. Por ltimo, completaremos el
proceso realizando la llamada al servicio web mediante el mtodo call().

1 HttpTransportSE transporte = new HttpTransportSE(URL);

3 try

4 {

5 transporte.call(SOAP_ACTION, envelope);

7 //Se procesa el resultado devuelto

8 //...

9 }

10 catch (Exception e)

11 {

12 txtResultado.setText("Error!");
13 }

Tras la llamada al servicio ya estamos en disposicin de obtener el resultado devuelto por el mtodo web llamado.
Esto lo conseguimos mediante el mtodo getResponse(). Dependiendo del tipo de resultado que esperemos recibir
deberemos convertir esta respuesta a un tipo u otro. En este caso, como el resultado que esperamos es un valor
simple (un nmero entero) convertiremos la respuesta a un objeto SoapPrimitive, que directamente podremos
convertir a una cadena de caracteres llamado a toString(). Ms adelante veremos cmo tratar valores de retorno
ms complejos.

1 SoapPrimitive resultado_xml =(SoapPrimitive)envelope.getResponse();

2 String res = resultado_xml.toString();

4 if(res.equals("1"))

5 txtResultado.setText("Insertado OK");

Y listo, con esto ya tenemos preparada la llamada a nuestro servicio web y el tratamiento de la respuesta recibida.

Un detalle ms antes de poder probar todo el sistema. Debemos acordarnos de conceder permiso de acceso a
internet a nuestra aplicacin, aadiendo la linea correspondiente al Android Manifest:

1 <uses-permission android:name="android.permission.INTERNET"/>

Pues bien, para probar lo que llevamos hasta ahora podemos ejecutar ambos proyectos simultneamente, en primer
lugar el de Visual Studio para iniciar la ejecucin del servidor local que alberga nuestro servicio web (hay que dejar
abierto el explorador una vez que se abra), y posteriormente el de Eclipse para iniciar nuestra aplicacin Android en
el Emulador. Una vez estn los dos proyectos en ejecucin, podemos rellenar los datos de nuestro cliente en la
aplicacin Android y pulsar el botn Enviar para realizar la llamada al servicio web e insertar el cliente en la base
de datos (que por supuesto tambin deber estar iniciada). Si todo va bien y no se produce ningn error, deberamos
poder consultar la tabla de Clientes a travs del SQL Server Management Studio para verificar que el cliente se ha
insertado correctamente.

En la imagen vemos cmo hemos insertado un nuevo cliente llamada cliente7 con nmero de telfono 7777. Si
consultamos ahora nuestra base de datos Sql Server podremos comprobar si el registro efectivamente se ha
insertado correctamente.
Algo importante que quiero remarcar llegados a este punto. El cdigo anterior debe funcionar correctamente sobre
un dispositivo o emulador con versin de Android anterior a la 3.0. Sin embargo, si intentamos ejecutar la aplicacin
sobre una versin posterior obtendremos una excepcin de tipoNetworkOnMainThread. Esto es debido a que en
versiones recientes de Android no se permite realizar operaciones de larga duracin directamente en el hilo principal
de la aplicacin. Para solucionar esto y que nuestra aplicacin funcione bajo cualquier versin de Android ser
necesario trasladar el cdigo que hemos escrito para llamar al servicio web a una AsyncTask que realice las
operaciones en segundo plano utilizando un hilo secundario. El curso contiene un capitulo dedicado a describir con
ms detalle las tareas asncronas o AsyncTask, por lo que en este caso me limitar a poner cmo quedara nuestro
cdigo dentro de la AsyncTask.

1 //Tarea Asncrona para llamar al WS de consulta en segundo plano

2 private class TareaWSConsulta extends AsyncTask<String,Integer,Boolean> {

4 private Cliente[] listaClientes;

6 protected Boolean doInBackground(String... params) {

8 boolean resul = true;

10 final String NAMESPACE = "http://sgoliver.net/";

11 final String URL="http://10.0.2.2:1473/ServicioClientes.asmx";

12 final String METHOD_NAME = "ListadoClientes";

13 final String SOAP_ACTION = "http://sgoliver.net/ListadoClientes";

14

15 SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

16

17 SoapSerializationEnvelope envelope =

18 new SoapSerializationEnvelope(SoapEnvelope.VER11);

19 envelope.dotNet = true;

20

21 envelope.setOutputSoapObject(request);

22

23 HttpTransportSE transporte = new HttpTransportSE(URL);

24

25 try

26 {

27 transporte.call(SOAP_ACTION, envelope);

28
29 SoapObject resSoap =(SoapObject)envelope.getResponse();

30

31 listaClientes = new Cliente[resSoap.getPropertyCount()];

32

33 for (int i = 0; i < listaClientes.length; i++)

34 {

35 SoapObject ic = (SoapObject)resSoap.getProperty(i);

36

37 Cliente cli = new Cliente();

38 cli.id = Integer.parseInt(ic.getProperty(0).toString());

39 cli.nombre = ic.getProperty(1).toString();

40 cli.telefono =

41 Integer.parseInt(ic.getProperty(2).toString());

42

43 listaClientes[i] = cli;

44 }

45 }

46 catch (Exception e)

47 {

48 resul = false;

49 }

50

51 return resul;

52 }

53

54 protected void onPostExecute(Boolean result) {

55

56 if (result)

57 {

58 //Rellenamos la lista con los nombres de los clientes

59 final String[] datos = new String[listaClientes.length];

60

61 for(int i=0; i<listaClientes.length; i++)

62 datos[i] = listaClientes[i].nombre;
63

64 ArrayAdapter<String> adaptador =

65 new ArrayAdapter<String>(MainActivity.this,

66 android.R.layout.simple_list_item_1, datos);

67

68 lstClientes.setAdapter(adaptador);

69 }

70 else

71 {

72 txtResultado.setText("Error!");

73 }

74 }

75 }

Como podemos ver, prcticamente todo el cdigo se ha trasladado al mtodo doInBackground() de la tarea, salvo la
parte en la que debemos actualizar la interfaz de usuario tras la llamada que debe ir al mtodo onPostExecute().

Por su parte, una vez creada la tarea asncrona, en el evento click del botn nos limitaremos a instanciar la tarea y
ejecutarla llamando a su mtodo execute().

1 btnConsultar.setOnClickListener(new OnClickListener() {

3 @Override

4 public void onClick(View v) {

5 TareaWSConsulta tarea = new TareaWSConsulta();

6 tarea.execute();

7 }

8 });

Con esto, ya sabemos realizar una llamada a un servicio web SOAP que devuelve un valor de retorno sencillo, en este
caso un simple nmero entero. Lo siguiente que vamos a ver ser como implementar la llamada a un mtodo del
servicio web que nos devuelva un valor algo ms complejo. Y esto lo vamos a ver con la llamada al mtodo
web ListadoClientes() que recordemos devolva un array de objetos de tipo Cliente.

En este caso, la llamada al mtodo web se realizar de forma totalmente anloga a la ya comentada. Donde llegarn
las diferencias ser a la hora de tratar el resultado devuelto por el servicio, comenzando por el resultado del
mtodo getResponse() de ksoap. En esta ocasin, dado que el resultado esperado no es ya un valor simple sino un
objeto ms complejo, convertiremos el resultado degetResponse() al tipo SoapObject, en vez de SoapPrimitive como
hicimos anteriormente. Nuestro objetivo ser generar un array de objetos Cliente (lo llamaremos listaClientes) a
partir del resultado devuelto por la llamada al servicio.

Como sabemos que el resultado devuelto por el servicio es tambin un array, lo primero que haremos ser crear un
array local con la misma longitud que el devuelto, lo que conseguiremos mediante el
mtodo getPropertyCount(). Tras esto, iteraremos por los distintos elementos del array devuelto mediante el
mtodo getProperty(ind), donde ind ser el ndice de cada ocurrencia. Cada uno de estos elementos ser a su vez
otro objeto de tipo SoapObject, que representar a un Cliente. Adicionalmente, para cada elemento accederemos a
sus propiedades (Id, Nombre, y Telefono) una vez ms mediante llamadas a getProperty(), con el ndice de cada
atributo, que seguir el mismo orden en que se definieron. As, getProperty(0) recuperar el Id del
cliente, getProperty(1) el nombre, y getProperty(2) el telfono. De esta forma podremos crear nuestros
objetos Cliente locales a partir de estos datos. Al final de cada iteracin aadimos el nuevo cliente recuperado a
nuestro array. Veamos como quedara todo esto en el cdigo, donde seguro que se entiende mejor:

1 SoapObject resSoap =(SoapObject)envelope.getResponse();

3 Cliente[] listaClientes = new Cliente[resSoap.getPropertyCount()];

5 for (int i = 0; i < listaClientes.length; i++)

6 {

7 SoapObject ic = (SoapObject)resSoap.getProperty(i);

9 Cliente cli = new Cliente();

10 cli.id = Integer.parseInt(ic.getProperty(0).toString());

11 cli.nombre = ic.getProperty(1).toString();

12 cli.telefono = Integer.parseInt(ic.getProperty(2).toString());

13

14 listaClientes[i] = cli;

15 }

En nuestra aplicacin de ejemplo aadimos un nuevo botn y un control tipo lista (lo llamolstClientes), de forma que
al pulsar dicho botn rellenemos la lista con los nombres de todos los clientes recuperados. La forma de rellenar una
lista con un array de elementos ya la vimos en los artculos dedicados a los controles de seleccin, por lo que no nos
pararemos a comentarlo. El cdigo sera el siguiente (Nota: s que todo esto se podra realizar de forma ms
eficiente sin necesidad de crear distintos arrays para los clientes y para el adaptador de la lista, pero lo dejo as para
no complicar el tutorial con temas ya discutidos en otros artculos):

1 //Rellenamos la lista con los nombres de los clientes

2 final String[] datos = new String[listaClientes.length];

4 for(int i=0; i<listaClientes.length; i++)

5 datos[i] = listaClientes[i].nombre;

7 ArrayAdapter<String> adaptador =

8 new ArrayAdapter<String>(ServicioWebSoap.this,

9 android.R.layout.simple_list_item_1, datos);

10
11 lstClientes.setAdapter(adaptador);

Por ltimo, vamos a ver cmo llamar a un mtodo web que recibe como parmetro algn objeto complejo. Para
ilustrarlo haremos una llamada al segundo mtodo de insercin de clientes que implementamos en el
servicio, NuevoClienteObjeto(). Recordemos que este mtodo reciba como parmetro de entrada un objeto de
tipo Cliente.

Para poder hacer esto, lo primero que tendremos que hacer ser modificar un poco nuestra claseCliente, de forma
que ksoap sepa cmo serializar nuestros objetos Cliente a la hora de generar las peticiones SOAP correspondientes.
Y para esto, lo que haremos ser implementar la interfazKvmSerializable en nuestra clase Cliente. Para ello, adems
de aadir la clusula implementscorrespondiente tendremos que implementar los siguientes mtodos:

getProperty(int indice)

getPropertyCount()

getPropertyInfo(int indice, HashTable ht, PropertyInfo info)

setProperty(int indice, Object valor)

El primero de ellos deber devolver el valor de cada atributo de la clase a partir de su ndice de orden. As, para el
ndice 0 se devolver el valor del atributo Id, para el ndice 1 el del atributo Nombre, y para el 2 el atributo Telfono.

1 @Override

2 public Object getProperty(int arg0) {

4 switch(arg0)

5 {

6 case 0:

7 return id;

8 case 1:

9 return nombre;

10 case 2:

11 return telefono;

12 }

13

14 return null;

15 }

El segundo de los mtodos, deber devolver simplemente el nmero de atributos de nuestra clase, que en nuestro
caso ser 3 (Id, Nombre y Telefono):

1 @Override

2 public int getPropertyCount() {

3 return 3;

4 }
El objetivo del tercero ser informar, segn el ndice recibido como parmetro, el tipo y nombre del atributo
correspondiente. El tipo de cada atributo se devolver como un valor de la clasePropertyInfo.

1 @Override

2 public void getPropertyInfo(int ind, Hashtable ht, PropertyInfo info) {

3 switch(ind)

4 {

5 case 0:

6 info.type = PropertyInfo.INTEGER_CLASS;

7 info.name = "Id";

8 break;

9 case 1:

10 info.type = PropertyInfo.STRING_CLASS;

11 info.name = "Nombre";

12 break;

13 case 2:

14 info.type = PropertyInfo.INTEGER_CLASS;

15 info.name = "Telefono";

16 break;

17 default:break;

18 }

19 }

Por ltimo, el mtodo setProperty() ser el encargado de asignar el valor de cada atributo segn su ndice y el valor
recibido como parmetro.

1 @Override

2 public void setProperty(int ind, Object val) {

3 switch(ind)

4 {

5 case 0:

6 id = Integer.parseInt(val.toString());

7 break;

8 case 1:

9 nombre = val.toString();

10 break;

11 case 2:
12 telefono = Integer.parseInt(val.toString());

13 break;

14 default:

15 break;

16 }

17 }

Mediante estos mtodos, aunque de forma transparente para el programados, ksoap ser capaz de transformar
nuestros objetos Cliente al formato XML correcto de forma que pueda pasarlos como parmetro en las peticiones
SOAP a nuestro servicio.

Por su parte, la llamada al servicio tambin difiere un poco de lo ya comentado a la hora de asociar los parmetros
de entrada del mtodo web. En este caso, construiremos en primer lugar el objeto Clienteque queremos insertar en
la base de datos a partir de los datos introducidos en la pantalla de nuestra aplicacin de ejemplo. Tras esto
crearemos un nuevo objeto PropertyInfo, al que asociaremos el nombre, valor y tipo de nuestro cliente mediante sus
mtodos setName(), setValue() ysetClass() respectivamente. Por ltimo, asociaremos este cliente como parmetro
de entrada al servicio llamando al metodo addProperty() igual que hemos hecho en las anteriores ocasiones, con la
diferencia de que esta vez lo llamaremos pasndole el objeto PropertyInfo que acabamos de crear. Adems de esto,
tendremos tambin que llamar finalmente al mtodo addMapping() para asociar de alguna forma nuestro espacio
de nombres y nombre de clase Cliente con la clase real java. Veamos el cdigo para entenderlo mejor:

1 Cliente cli = new Cliente();

2 cli.nombre = txtNombre.getText().toString();

3 cli.telefono = Integer.parseInt(txtTelefono.getText().toString());

5 PropertyInfo pi = new PropertyInfo();

6 pi.setName("cliente");

7 pi.setValue(cli);

8 pi.setType(cli.getClass());

10 request.addProperty(pi);

11

12 SoapSerializationEnvelope envelope =

13 new SoapSerializationEnvelope(SoapEnvelope.VER11);

14 envelope.dotNet = true;

15

16 envelope.setOutputSoapObject(request);

17

18 envelope.addMapping(NAMESPACE, "Cliente", cli.getClass());


Todo esto lo haremos en un nuevo botn aadido a la aplicacin de ejemplo (Enviar2), cuyo efecto tendr que ser
idntico al que ya creamos para la llamada al mtodo web NuevoClienteSimple(), aunque como acabamos de ver su
implementacin es algo diferente debido a los distintos parmetros de entrada utilizados.

Como imagen final veamos una captura de la pantalla final de nuestra aplicacin de ejemplo, donde vemos los tres
botones implementados, junto al resultado de la ejecucin de cada uno, el mensaje Insertado OK de los mtodos
de insercin, y la lista de clientes recuperada por el mtodo de consulta.

Espero que estos dos ltimos artculos sobre servicios web SOAP y Android os sirvan para tener un ejemplo
completo, tanto de la parte servidor como de la parte cliente, que os sirva de base para crear nuevos sistemas
adaptados a vuestras necesidades.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Acceso a Servicios Web REST en Android (1/2)
by Sgoliver on 04/03/2012 in Android, Programacin

En los dos artculos anteriores (ste y ste) del Curso de Programacin Android nos hemos ocupado de describir la
forma de construir un sistema formado por un servicio web SOAP que accede a una base de datos externa y una
aplicacin Android que, a travs de este servicio, es capaz de manipular dichos datos.

En este nuevo artculo vamos a crear un sistema similar, pero esta vez haciendo uso de la otra alternativa por
excelencia a la hora de crear servicios web, y no es otra de utilizar servicios web tipoREST. Las famosas APIs que
publican muchos de los sitios web actualmente no son ms que servicios web de este tipo, aunque en la mayora de
los casos con medidas de seguridad adicionales tales como autenticacin OAuth o similares.

REST tambin se asienta sobre el protocolo HTTP como mecanismo de transporte entre cliente y servidor, ya
veremos despus en qu medida. Y en cuanto al formato de los datos transmitidos, a diferencia de SOAP, no se
impone ninguno en concreto, aunque lo ms habitual actualmente es intercambiar la informacin en formato XML o
JSON. Ya que en el caso de SOAP utilizamos XML, en este nuevo artculo utilizaremos JSON para construir nuestro
ejemplo.

Tambin vamos a utilizar un framework distinto para construir el servicio, aunque seguiremos hacindolo en Visual
Studio y en lenguaje C#. En este caso, en vez de utilizar ASP.NET a secas, vamos a utilizar el framework
especfico ASP.NET MVC 3, cuyo sistema de direccionamiento se ajusta mejor a los principios de REST, donde
cada recurso [en nuestro caso cada cliente] debera ser accesible mediante su propia URL nica. Podis descargar
MVC3 desde su pgina oficial de Microsoft.

En este primer artculo sobre servicios REST vamos a describir la construccin del servicio web en s, y dedicaremos
un segundo artculo a explicar cmo podemos acceder a este servicio desde una aplicacin Android.

Empezamos. Lo primero que vamos a hacer ser crear un nuevo proyecto en Visual Studio utilizando esta vez la
plantilla llamada ASP.NET MVC 3 Web Application, lo llamaremos ServicioWebRest.

En la ventana de opciones del proyecto dejaremos todos los datos que aparecen por defecto y seleccionaremos
como plantilla Empty para crear una aplicacin vaca.
Esto debera crearnos el nuevo proyecto con la estructura de carpetas necesaria, que como veris es bastante
elaborada. En nuestro caso vamos a crear el servicio web de forma aislada del resto de la aplicacin web, y para ello
lo primero que vamos a hacer es aadir una nueva Area al proyecto, a la que llamaremos por ejemplo Api, lo que
nos crear una estructura de carpetas similar a la de la aplicacin principal pero dentro de una carpeta
independiente. Esto nos permite aislar todo el cdigo y recursos de nuestro servicio web del resto de la aplicacin
web (que en nuestro caso no existir porque no es el objetivo de este artculo, pero que podramos crear sin
problemas si lo necesitramos).

Con esto, la estructura de nuestro proyecto ser la siguiente:

Una vez que ya tenemos preparada toda la estructura de nuestro proyecto empecemos a aadir los elementos
necesarios. Lo primero que vamos a crear ser una nueva clase Cliente, igual que hicimos en el ejemplo anterior con
SOAP. La colocaremos en la carpeta Api/Models y el cdigo es el mismo que ya vimos:

1 namespace ServicioWebRest.Areas.Api.Models

2 {

3 public class Cliente

4 {

5 public int Id { get; set; }

6 public string Nombre { get; set; }

7 public int Telefono { get; set; }

8 }

9 }

El siguiente elemento a aadir ser una nueva clase que contenga todas las operaciones que queramos realizar
sobre nuestra base de datos de clientes. Llamaremos a la clase ClienteManager. En este caso s vamos a aadir las
cuatro operaciones bsicas sobre clientes, y una adicional para obtener el listado completo, de forma que ms tarde
podamos mostrar la implementacin en Android de todos los posibles tipos de llamada al servicio. Los mtodos que
aadiremos sern los siguientes:
Cliente ObtenerCliente(int id)

List<Clientes> ObtenerClientes()

bool InsertarCliente(Cliente c)

bool ActualizarCliente(Cliente c)

bool EliminarCliente(int id)

Los dos primeros mtodos nos servirn para recuperar clientes de la base de datos, tanto por su ID para obtener un
cliente concreto, como el listado completo que devolver una lista de clientes. Los otros tres mtodos permitirn
insertar, actualizar y eliminar clientes a partir de su ID y los datos de entrada (si aplica). El cdigo de todos estos
mtodos es anlogo a los ya implementados en el caso de SOAP, por lo que no nos vamos a parar en volverlos a
comentar, tan slo decir que utilizan la api clsica de ADO.NET para el acceso a SQL Server. En cualquier caso, al final
del artculo tenis como siempre el cdigo fuente completo para poder consultar lo que necesitis. A modo de
ejemplo veamos la implementacin de los mtodos ObtenerClientes() e InsertarCliente().

1 public bool InsertarCliente(Cliente cli)

2 {

3 SqlConnection con = new SqlConnection(cadenaConexion);

5 con.Open();

7 string sql = "INSERT INTO Clientes (Nombre, Telefono) VALUES (@nombre, @telefono)";

9 SqlCommand cmd = new SqlCommand(sql,con);

10

11 cmd.Parameters.Add("@nombre", System.Data.SqlDbType.NVarChar).Value = cli.Nombre;

12 cmd.Parameters.Add("@telefono", System.Data.SqlDbType.Int).Value = cli.Telefono;

13

14 int res = cmd.ExecuteNonQuery();

15

16 con.Close();

17

18 return (res == 1);

19 }

20

21 public List<Cliente> ObtenerClientes()

22 {

23 List<Cliente> lista = new List<Cliente>();

24
25 SqlConnection con = new SqlConnection(cadenaConexion);

26

27 con.Open();

28

29 string sql = "SELECT IdCliente, Nombre, Telefono FROM Clientes";

30

31 SqlCommand cmd = new SqlCommand(sql,con);

32

33 SqlDataReader reader =

34 cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);

35

36 while (reader.Read())

37 {

38 Cliente cli = new Cliente();

39

40 cli = new Cliente();

41 cli.Id = reader.GetInt32(0);

42 cli.Nombre = reader.GetString(1);

43 cli.Telefono = reader.GetInt32(2);

44

45 lista.Add(cli);

46 }

47

48 reader.Close();

49

50 return lista;

51 }

Hasta ahora, todo el cdigo que hemos escrito es bastante genrico y nada tiene que ver con que nuestro proyecto
sea de tipo MVC. Sin embargo, los dos siguientes elementos s que estn directamente relacionados con el tipo de
proyecto que tenemos entre manos.

Lo siguiente que vamos a aadir ser un controlador a nuestro servicio web. Este controlador
(claseClientesController) ser el encargado de contener las diferentes acciones que se podrn llamar segn la URL y
datos HTTP que recibamos como peticin de entrada al servicio. Para nuestro ejemplo, aadiremos tan slo dos
acciones, una primera dirigida a gestionar todas las peticiones que afecten a un nico cliente (insertar, actualizar,
eliminar y obtener por ID), y otra que trate la peticin del listado completo de clientes. Las
llamaremos Clientes() y Cliente() respectivamente. Estas acciones harn uso de una instancia de la
clase ClienteManager creada anteriormente para realizar las acciones necesarias contra la base de datos. Cada
accin ser tambin responsable de formatear sus resultados al formato de comunicacin que hayamos elegido, en
nuestro caso JSON.

La accin Clientes es muy sencilla, se limitar a llamar al mtodo ObtenerClientes() y formatear los resultados como
JSON. Para hacer esto ltimo basta con crear directamente un objeto JsonResultllamado al mtodo Json() pasndole
como parmetro de entrada el objeto a formatear. Todo esto se reduce a una sola linea de cdigo:

1 [HttpGet]

2 public JsonResult Clientes()

3 {

4 return Json(this.clientesManager.ObtenerClientes(),

5 JsonRequestBehavior.AllowGet);

6 }

Habris notado tambin que hemos precedido el mtodo con el atributo [HttpGet]. Para intentar explicar esto me
hace falta seguir hablando de los principios de diseo de REST. Este tipo de servicios utiliza los propios tipos de
peticin definidos por el protocolo HTTP para diferenciar entre las operaciones a realizar por el servicio web. As, el
propio tipo de peticin HTTP realizada (GET, POST,PUT o DELETE), junto con la direccin URL especificada en la
llamada, nos determinar la operacin a ejecutar por el servicio web. En el caso ya visto, el atributo [HttpGet] nos
indica que dicho mtodo se podr ejecutar al recibirse una peticin de tipo GET.

Entenderemos todo esto mejor ahora cuando veamos el cdigo de la accin Cliente(). En esta accin, dependiente
del tipo de peticin HTTP recibida, tendremos que llamar a un mtodo u otro del servicio web. As,
usaremos POST para las inserciones de clientes, PUT para las actualizaciones, GETpara la consulta por ID
y DELETE para las eliminaciones. En este caso no precedemos el mtodo por ningn atributo, ya que la misma accin
se encargar de tratar diferentes tipos de peticin.

1 public JsonResult Cliente(int? id, Cliente item)

2 {

3 switch (Request.HttpMethod)

4 {

5 case "POST":

6 return Json(clientesManager.InsertarCliente(item));

7 case "PUT":

8 return Json(clientesManager.ActualizarCliente(item));

9 case "GET":

10 return Json(clientesManager.ObtenerCliente(id.GetValueOrDefault()),

11 JsonRequestBehavior.AllowGet);

12 case "DELETE":

13 return Json(clientesManager.EliminarCliente(id.GetValueOrDefault()));

14 }

15

16 return Json(new { Error = true, Message = "Operacin HTTP desconocida" });


17 }

Algunos de vosotros seguro que os estis preguntando cmo distinguir el servicio cundo llamar a la
accin Clientes() para obtener el listado completo, o a la accin Cliente() para obtener un nico cliente por su ID, ya
que para ambas operaciones hemos indicado que se recibir el tipo de peticin http GET.

Pues bien, aqu es donde nos va a ayudar el ltimo elemento a aadir al servicio web. Realmente no lo aadiremos,
sino que lo modificaremos, ya que es un fichero que ya ha creado Visual Studio por nosotros. Se trata de la
clase ApiAreaRegistration. La funcin de esta clase ser la de dirigir las peticiones recibidas hacia una accin u otra
del controlador segn la URL utilizada al realizarse la llamada al servicio web.

En nuestro caso de ejemplo, vamos a reconocer dos tipos de URL. Una de ellas para acceder a la lista completa de
cliente, y otra para realizar cualquier accin sobre un cliente en concreto:

Lista de clientes: http://servidor/Api/Clientes

Operacin sobre cliente: http://servidor/Api/Clientes/Cliente/id_del_cliente

Cada uno de estos patrones tendremos que registrarlos mediante el mtodo MapRoute() dentro del
mtodo RegisterArea() que ya tendremos creado dentro de la clase ApiAreaRegistration. As, para registrar el primer
tipo de URL haremos lo siguiente:

1 context.MapRoute(

2 "AccesoClientes",

3 "Api/Clientes",

4 new

5 {

6 controller = "Clientes",

7 action = "Clientes"

8 }

9 );

Como primer parmetro de MapRoute() indicamos un nombre descriptivo para el patrn de URL. El segundo
parmetro es el patrn en s, que en este caso no tiene partes variables. Por ltimo indicamos el controlador al que
se dirigirn las peticiones que sigan este patrn eliminando el sufijo Controller (en nuestro caso ser el
controlador ClientesController) y la accin concreta a ejecutar dentro de dicho controlador (en nuestro caso la
accin Clientes()).

Para el segundo tipo de URL ser muy similar, con la nica diferencia de que ahora habr una parte final variable que
se corresponder con el ID del cliente y que asignaremos al parmetro id de la accin. En este caso adems,
dirigiremos la peticin hacia la accin Cliente(), en vez deClientes().

1 context.MapRoute(

2 "AccesoCliente",

3 "Api/Clientes/Cliente/{id}",

4 new

5 {

6 controller = "Clientes",

7 action = "Cliente",
8 id = UrlParameter.Optional

9 }

10 );

Como todo esto en cuenta, y por recapitular un poco, las posibles llamadas a nuestro servicio sern las siguientes:

GET /Api/Clientes

Recuperar el listado completo de clientes y lo devolver en formato JSON.

GET /Api/Clientes/Cliente/3

Recuperar el cliente con el ID indicado en la URL y lo devolver en formato JSON.

POST /Api/Clientes/Cliente { Nombre:nombre, Telefono:1234 }

Insertar un nuevo cliente con los datos aportados en la peticin en formato JSON.

PUT /Api/Clientes/Cliente/3 { Id:3, Nombre:nombre, Telefono:1234 }

Actualizar el cliente con el ID indicado en la URL con los datos aportados en la peticin en formato JSON.

DELETE /Api/Clientes/Cliente/3

Eliminar el cliente con el ID indicado en la URL.

Llegados aqu, tan slo tenemos que ejecutar nuestro proyecto y esperar a que se abra el navegador web. En
principio no se mostrar un error por no encontrar la pgina principal de la aplicacin, ya que no hemos creado
ninguna, pero nos asegurar que el servidor de prueba est funcionando, por lo que nuestro servicio debera
responder a peticiones.

As, si escribimos en la barra de direcciones por ejemplo la siguiente direccin (el puerto puede variar):

http://localhost:1234/Api/Clientes/Cliente/4

deberamos recibir un fichero en formato JSON que contuviera los datos del cliente con ID = 4 de nuestra base de
datos. Sera un fichero con contenido similar al siguiente:

{"Id":4,"Nombre":"cliente4","Telefono":4444}

En el siguiente artculo veremos cmo construir una aplicacin Android capaz de acceder a este servicio y procesar
los resultados recibidos.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Acceso a Servicios Web REST en Android (2/2)
by Sgoliver on 04/03/2012 in Android, Programacin

En el artculo anterior dedicado a los servicios web REST hemos visto cmo crear fcilmente un servicio de este tipo
utilizando el framework ASP.NET MVC 3. En esta segunda parte vamos a describir cmo podemos construir una
aplicacin Android que acceda a este servicio web REST.

Y tal como hicimos en el caso de SOAP, vamos a crear una aplicacin de ejemplo que llame a las distintas funciones
de nuestro servicio web. En este caso la aplicacin se compondr de 5 botones, uno por cada una de las acciones
que hemos implementado en el servicio web (insertar, actualizar, eliminar, recuperar un cliente, y listar todos los
clientes).

A diferencia del caso de SOAP, en esta ocasin no vamos a utilizar ninguna librera externa para acceder al servicio
web, ya que Android incluye todo lo necesario para realizar la conexin y llamada a los mtodos del servicio, y
tratamiento de resultados en formato JSON.

Como ya hemos comentado, al trabajar con servicios web de tipo REST, las llamadas al servicio no se harn a travs
de una nica URL, sino que se determinar la accin a realizar segn la URL accedida y la accin HTTP utilizada para
realizar la peticin (GET, POST, PUT o DELETE). En los siguientes apartados veremos uno a uno la implementacin de
estos botones.

Insertar un nuevo cliente

Como ya comentamos en el artculo anterior, la insercin de un nuevo cliente la realizaremos a travs de la siguiente
URL:

http://10.0.2.2:2731/Api/Clientes/Cliente

Utilizaremos la accin http POST y tendremos que incluir en la peticin un objeto en formato JSON que contenga los
datos del nuevo cliente (tan slo Nombre y Telfono, ya que el ID se calcular automticamente). El formato de este
objeto de entrada ser anlogo al siguiente:

{Nombre:cccc, Telefono:12345678}

Pues bien, para conseguir esto comenzaremos por crear un nuevo objeto HttpClient, que ser el encargado de
realizar la comunicacin HTTP con el servidor a partir de los datos que nosotros le proporcionemos. Tras esto
crearemos la peticin POST creando un nuevo objeto HttpPost e indicando la URL de llamada al servicio.
Modificaremos mediante setHeader() el atributo http content-typepara indicar que el formato de los datos que
utilizaremos en la comunicacin, que como ya indicamos ser JSON (cuyo MIME-Type correspondiente es
application/json).

1 HttpClient httpClient = new DefaultHttpClient();

2
3 HttpPost post =

4 new HttpPost("http://10.0.2.2:2731/Api/Clientes/Cliente");

6 post.setHeader("content-type", "application/json");

El siguiente paso ser crear el objeto JSON a incluir con la peticin, que deber contener los datos del nuevo cliente
a insertar. Para ello creamos un nuevo objeto JSONObject y le aadimos mediante el mtodo put() los dos atributos
necesarios (nombre y telfono) con sus valores correspondientes, que los obtenemos de los cuadros de texto de la
interfaz, llamados txtNombre y txtTelefono.

Por ltimo asociaremos este objeto JSON a nuestra peticin HTTP convirtindolo primero al tipoStringEntity e
incluyndolo finalmente en la peticin mediante el mtodo setEntity().

1 //Construimos el objeto cliente en formato JSON

2 JSONObject dato = new JSONObject();

4 dato.put("Nombre", txtNombre.getText().toString());

5 dato.put("Telefono", Integer.parseInt(txtTelefono.getText().toString()));

7 StringEntity entity = new StringEntity(dato.toString());

8 post.setEntity(entity);

Una vez creada nuestra peticin HTTP y asociado el dato de entrada, tan slo nos queda realizar la llamada al
servicio mediante el mtodo execute() del objeto HttpClient y recuperar el resultado mediante getEntity(). Este
resultado lo recibimos en forma de objeto HttpEntity, pero lo podemos convertir fcilmente en una cadena de texto
mediante el mtodo esttico EntityUtils.toString().

1 HttpResponse resp = httpClient.execute(post);

2 String respStr = EntityUtils.toString(resp.getEntity());

4 if(respStr.equals("true"))

5 lblResultado.setText("Insertado OK.");

En nuestro caso, el mtodo de insercin devuelve nicamente un valor booleano indicando si el registro se ha
insertado correctamente en la base de datos, por lo que tan slo tendremos que verificar el valor de
este booleano (true o false) para conocer el resultado de la operacin, que mostraremos en la interfaz en una
etiqueta de texto llamada lblResultado.

Como ya dijimos en el apartado anterior sobre servicios SOAP, a partir de la versin 3 de Android no se permite
realizar operaciones de larga duracin dentro del hilo principal de la aplicacin, entre ellas conexiones a internet
como estamos haciendo en esta ocasin. Para solucionar este problema y que la aplicacin funcione correctamente
en todas las versiones de Android debemos hacer la llamada al servicio mediante una tarea asncrona, o AsynTask,
que se ejecute en segundo plano. Una vez ms no entrar en detalles porque el curso contiene un captulo dedicado
exclusivamente a este tema. A modo de ejemplo, el cdigo anterior trasladado a una AsyncTask quedara de la
siguiente forma:

1 private class TareaWSInsertar extends AsyncTask<String,Integer,Boolean> {


2

3 protected Boolean doInBackground(String... params) {

5 boolean resul = true;

7 HttpClient httpClient = new DefaultHttpClient();

9 HttpPost post = new

10 HttpPost("http://10.0.2.2:2731/Api/Clientes/Cliente");

11

12 post.setHeader("content-type", "application/json");

13

14 try

15 {

16 //Construimos el objeto cliente en formato JSON

17 JSONObject dato = new JSONObject();

18

19 dato.put("Nombre", params[0]);

20 dato.put("Telefono", Integer.parseInt(params[1]));

21

22 StringEntity entity = new StringEntity(dato.toString());

23 post.setEntity(entity);

24

25 HttpResponse resp = httpClient.execute(post);

26 String respStr = EntityUtils.toString(resp.getEntity());

27

28 if(!respStr.equals("true"))

29 resul = false;

30 }

31 catch(Exception ex)

32 {

33 Log.e("ServicioRest","Error!", ex);

34 resul = false;

35 }
36

37 return resul;

38 }

39

40 protected void onPostExecute(Boolean result) {

41

42 if (result)

43 {

44 lblResultado.setText("Insertado OK.");

45 }

46 }

47 }

Y la llamada a la tarea asncrona desde el evento onClick del botn de Insertar sera tan sencilla como sta:

1 btnInsertar.setOnClickListener(new OnClickListener() {

3 @Override

4 public void onClick(View v) {

5 TareaWSInsertar tarea = new TareaWSInsertar();

6 tarea.execute(

7 txtNombre.getText().toString(),

8 txtTelefono.getText().toString());

9 }

10 });

Actualizar un cliente existente

La URL utilizada para la actualizacin de clientes ser la misma que la anterior:

http://10.0.2.2:2731/Api/Clientes/Cliente

Pero en este caso, el objeto JSON a enviar como entrada deber contener no slo los nuevos valores de nombre y
telfono sino tambin el ID del cliente a actualizar, por lo que tendra una estructura anloga a la siguiente:

{Id:123, Nombre:cccc, Telefono:12345678}

Para actualizar el cliente procederemos de una forma muy similar a la ya comentada para la insercin, con las nicas
diferencias de que en este caso la accin HTTP utilizada ser PUT (objeto HttpPut) y que el objeto JSON de entrada
tendr el campo ID adicional.

1 HttpClient httpClient = new DefaultHttpClient();

3 HttpPut put = new HttpPut("http://10.0.2.2:2731/Api/Clientes/Cliente");


4 put.setHeader("content-type", "application/json");

6 try

7 {

8 //Construimos el objeto cliente en formato JSON

9 JSONObject dato = new JSONObject();

10

11 dato.put("Id", Integer.parseInt(txtId.getText().toString()));

12 dato.put("Nombre", txtNombre.getText().toString());

13 dato.put("Telefono", Integer.parseInt(txtTelefono.getText().toString()));

14

15 StringEntity entity = new StringEntity(dato.toString());

16 put.setEntity(entity);

17

18 HttpResponse resp = httpClient.execute(put);

19 String respStr = EntityUtils.toString(resp.getEntity());

20

21 if(respStr.equals("true"))

22 lblResultado.setText("Actualizado OK.");

23 }

24 catch(Exception ex)

25 {

26 Log.e("ServicioRest","Error!", ex);

27 }

Eliminacin de un cliente

La eliminacin de un cliente la realizaremos a travs de la URL siguiente:

http://10.0.2.2:2731/Api/Clientes/Cliente/id_cliente

donde id_cliente ser el ID del cliente a eliminar. Adems, utilizaremos la accin httpDELETE (objeto HttpDelete)
para identificar la operacin que queremos realizar. En este caso no ser necesario pasar ningn objeto de entrada
junto con la peticin, por lo que el cdigo quedar an ms sencillo que los dos casos anteriores.

1 HttpClient httpClient = new DefaultHttpClient();

3 String id = txtId.getText().toString();

4
5 HttpDelete del =

6 new HttpDelete("http://10.0.2.2:2731/Api/Clientes/Cliente/" + id);

8 del.setHeader("content-type", "application/json");

10 try

11 {

12 HttpResponse resp = httpClient.execute(del);

13 String respStr = EntityUtils.toString(resp.getEntity());

14

15 if(respStr.equals("true"))

16 lblResultado.setText("Eliminado OK.");

17 }

18 catch(Exception ex)

19 {

20 Log.e("ServicioRest","Error!", ex);

21 }

Como podis ver, al principio del mtodo obtenemos el ID del cliente desde la interfaz de la aplicacin y lo
concatenamos con la URL base para formar la URL completa de llamada al servicio.

Obtener un cliente

Esta operacin es un poco distinta a las anteriores, ya que en este caso el resultado devuelto por el servicio ser un
objeto JSON y no un valor simple como en los casos anteriores. Al igual que en el caso de eliminacin de clientes, la
URL a utilizar ser del tipo:

http://10.0.2.2:2731/Api/Clientes/Cliente/id_cliente

En este caso utilizaremos un tipo de peticin http GET (objeto HttpGet) y la forma de realizar la llamada ser anloga
a las anteriores. Donde aparecern las diferencias ser a la hora de tratar el resultado devuelto por el servicio tras
llamar al mtodo getEntity(). Lo que haremos ser crear un nuevo objeto JSONObject a partir del resultado textual
de getEntity(). Hecho esto, podremos acceder a los atributos del objeto utilizando para ello los
mtodos get() correspondientes, segn el tipo de cada atributo (getInt(), getString(), etc). Tras esto mostraremos los
datos del cliente recuperado en la etiqueta de resultados de la interfaz (lblResultados).

1 HttpClient httpClient = new DefaultHttpClient();

3 String id = txtId.getText().toString();

5 HttpGet del =

6 new HttpGet("http://10.0.2.2:2731/Api/Clientes/Cliente/" + id);

7
8 del.setHeader("content-type", "application/json");

10 try

11 {

12 HttpResponse resp = httpClient.execute(del);

13 String respStr = EntityUtils.toString(resp.getEntity());

14

15 JSONObject respJSON = new JSONObject(respStr);

16

17 int idCli = respJSON.getInt("Id");

18 String nombCli = respJSON.getString("Nombre");

19 int telefCli = respJSON.getInt("Telefono");

20

21 lblResultado.setText("" + idCli + "-" + nombCli + "-" + telefCli);

22 }

23 catch(Exception ex)

24 {

25 Log.e("ServicioRest","Error!", ex);

26 }

Una vez ms como podis comprobar el cdigo es muy similar al ya visto para el resto de operaciones.

Obtener listado completo de clientes

Por ltimo vamos a ver cmo podemos obtener el listado completo de clientes. El inters de esta operacin est en
que el resultado recuperado de la llamada al servicio ser un array de objetos de tipo cliente, por supuesto en
formato JSON. La accin http utilizada ser una vez ms la accin GET, y la URL para recuperar el listado de clientes
ser:

http://10.0.2.2:2731/Api/Clientes

De nuevo, la forma de llamar al servicio ser anloga a las anteriores hasta la llamada a getEntity()para recuperar los
resultados. En esta ocasin, dado que recibimos un array de elementos, convertiremos este resultado a un
objeto JSONArray, y hecho esto podremos acceder a cada uno de los elementos del array mediante una llamada
a getJSONObject(), al que iremos pasando el ndice de cada elemento. Para saber cuntos elementos contiene el
array podremos utilizar el mtodolength() del objeto JSONArray. Por ltimo, el acceso a los atributos de cada
elemento del array lo realizamos exactamente igual como ya lo hicimos en la operacin anterior de obtencin de
cliente por ID.

1 HttpClient httpClient = new DefaultHttpClient();

3 HttpGet del =

4 new HttpGet("http://10.0.2.2:2731/Api/Clientes");
5

6 del.setHeader("content-type", "application/json");

8 try

9 {

10 HttpResponse resp = httpClient.execute(del);

11 String respStr = EntityUtils.toString(resp.getEntity());

12

13 JSONArray respJSON = new JSONArray(respStr);

14

15 String[] clientes = new String[respJSON.length()];

16

17 for(int i=0; i<respJSON.length(); i++)

18 {

19 JSONObject obj = respJSON.getJSONObject(i);

20

21 int idCli = obj.getInt("Id");

22 String nombCli = obj.getString("Nombre");

23 int telefCli = obj.getInt("Telefono");

24

25 clientes[i] = "" + idCli + "-" + nombCli + "-" + telefCli;

26 }

27

28 //Rellenamos la lista con los resultados

29 ArrayAdapter<String> adaptador =

30 new ArrayAdapter<String>(ServicioWebRest.this,

31 android.R.layout.simple_list_item_1, clientes);

32

33 lstClientes.setAdapter(adaptador);

34 }

35 catch(Exception ex)

36 {

37 Log.e("ServicioRest","Error!", ex);

38 }
Tras obtener nuestro array de clientes, para mostrar los resultados hemos aadido a la interfas de nuestra aplicacin
de ejemplo un control tipo ListView (llamado lstClientes) que hemos rellenado a travs de su adaptador con los
datos de los clientes recuperados.

A modo de ejemplo, en la siguiente imagen puede verse el resultado de ejecutar la operacin de listado completo de
clientes:

Y con esto hemos terminado. Espero haber ilustrado con claridad en los dos ltimos artculos la forma de construir
servicios web tipo REST mediante ASP.NET y aplicaciones cliente Android capaces de acceder a dichos servicios.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Tareas en segundo plano en Android
Tareas en segundo plano en Android (I): Thread y AsyncTask
by Sgoliver on 29/07/2012 in Android, Programacin

Todos los componentes de una aplicacin Android, tanto las actividades, los servicios [s, tambin los servicios], o
los broadcast receivers se ejecutan en el mismo hilo de ejecucin, el llamado hilo principal, main thread o GUI
thread, que como ste ltimo nombre indica tambin es el hilo donde se ejecutan todas las operaciones que
gestionan la interfaz de usuario de la aplicacin. Es por ello, que cualquier operacin larga o costosa que realicemos
en este hilo va a bloquear la ejecucin del resto de componentes de la aplicacin y por supuesto tambin la interfaz,
produciendo al usuario un efecto evidente de lentitud, bloqueo, o mal funcionamiento en general, algo que
deberamos evitar a toda costa. Incluso puede ser peor, dado que Android monitoriza las operaciones realizadas en
el hilo principal y detecta aquellas que superen los 5 segundos, en cuyo caso se muestra el famoso mensaje de
Application Not Responding (ANR) y el usuario debe decidir entre forzar el cierre de la aplicacin o esperar a que
termine.

Obviamente, stos son el tipo de errores que nadie quiere ver al utilizar su aplicacin, y en este artculo y los
siguientes vamos a ver varias formas de evitarlo utilizando procesos en segundo plano para ejecutar las operaciones
de larga duracin. En este primer artculo de la serie nos vamos a centrar en dos de las alternativas ms directas a la
hora de ejecutar tareas en segundo plano en Android:

1. Crear nosotros mismos de forma explcita un nuevo hilo para ejecutar nuestra tarea.

2. Utilizar la clase auxiliar AsyncTask proporcionada por Android.

Mi idea inicial para este artculo era obviar la primera opcin, ya que normalmente la segunda solucin nos es ms
que suficiente, y adems es mas sencilla y ms limpia de implementar. Sin embargo, si no comentamos al menos de
pasada la forma de crear a mano nuevos hilos y los problemas que surgen, quiz no se viera demasiado claro las
ventajas que tiene utilizar las AsyncTask. Por tanto, finalmente voy a pasar muy rpidamente por la primera opcin
para despus centrarnos un poco ms en la segunda. Adems, aprovechando el tema de la ejecucin de tareas en
segundo plano, vamos a ver tambin cmo utilizar un control (el ProgressBar) y un tipo de dilogo
(el ProgressDialog) que no vimos en los primeros temas del curso dedicados a la interfaz de usuario.

Y para ir paso a paso, vamso a empezar por crear una aplicacin de ejemplo en cuya actividad principal colocaremos
un control ProgressBar (en mi caso llamado pbarProgreso) y un botn (btnSinHilos) que ejecute una tarea de larga
duracin. Para simular una operacin de larga duracin vamos a ayudarnos de un mtodo auxiliar que lo nico que
haga sea esperar 1 segundo, mediante una llamada a Thread.sleep().

1 private void tareaLarga()

2 {

3 try {

4 Thread.sleep(1000);
5 } catch(InterruptedException e) {}

6 }

Haremos que nuestro botn ejecute este mtodo 10 veces, de forma que nos quedar una ejecucin de unos 10
segundos en total:

1 btnSinHilos.setOnClickListener(new OnClickListener() {

2 @Override

3 public void onClick(View v) {

4 pbarProgreso.setMax(100);

5 pbarProgreso.setProgress(0);

7 for(int i=1; i<=10; i++) {

8 tareaLarga();

9 pbarProgreso.incrementProgressBy(10);

10 }

11

12 Toast.makeText(MainHilos.this, "Tarea finalizada!",

13 Toast.LENGTH_SHORT).show();

14 }

15 });

Como veis, aqu todava no estamos utilizando nada especial, por lo que todo el cdigo se ejecutar en el hilo
principal de la aplicacin. En cuanto a la utilizacin del control ProgressBar vemos que es muy sencilla y no requiere
apenas configuracin. En nuestro caso tan slo hemos establecido el valor mximo que alcanzar (el valor en el que
la barra de progreso estar rellena al mximo) mediante el mtodo setMax(100), posteriormente la hemos
inicializado al valor mnimo mediante una llamada asetProgress(0) de forma que inicialmente aparezca
completamente vaca, y por ltimo en cada iteracin del bucle incrementamos su valor en 10 unidades llamando
a incrementProgressBy(10), de tal forma que tras la dcima iteracin la barra llegue al valor mximo de 100 que
establecimos antes. Finalmente mostramos un mensaje Toast para informar de la finalizacin de la tarea. Pues bien,
ejecutemos la aplicacin, pulsemos el botn, y veamos qu ocurre.

He colocado un pequeo vdeo al final del artculo donde puede verse el resultado final de todas las pruebas que
haremos durante este tutorial. En concreto esta primera prueba puede verse entre los segundos 00:00 00:12

No era eso lo que esperbamos verdad? Lo que ha ocurrido es que desde el momento que hemos pulsado el botn
para ejecutar la tarea, hemos bloqueado completamente el resto de la aplicacin, incluida la actualizacin de la
interfaz de usuario, que ha debido esperar a que sta termine mostrando directamente la barra de progreso
completamente llena. En definitiva, no hemos sido capaces de ver el progreso de la tarea. Pero como decamos, este
efecto puede empeorar. Probemos ahora a pulsar el botn de la tarea y mientras sta se ejecuta realicemos
cualquier accin sobre la pantalla, un simple click sobre el fondo nos basta. Veamos qu ocurre ahora.

Puedes verlo en el vdeo entre los segundos 00:13 00:28

Vemos cmo al intentar hacer cualquier accin sobre la aplicacin Android nos ha advertido con un mensaje de error
que la aplicacin no responde debido a que se est ejecutando una operacin de larga duracin en el hilo principal.
El usuario debe elegir entre esperar a que termine de ejecutarla o forzar el cierre de la aplicacin. Pues bien, estos
son los efectos que vamos a intentar evitar. La opcin ms inmediata que nos proporciona Android, al igual que
otras plataformas, es crear directamente hilos secundarios dentro de los cuales ejecutar nuestras operaciones
costosas. Esto lo conseguimos en Android instanciando un objeto de la clase Thread. El constructor de la
clase Thread recibe como parmetro un nuevo objeto Runnable que debemos construir implementando su
mtodo run(), dentro del cual vamos a realizar nuestra tarea de larga duracin. Hecho esto, debemos llamar al
mtodostart() del objeto Threaddefinido para comenzar la ejecucin de la tarea en segundo plano.

1 new Thread(new Runnable() {

2 public void run() {

3 //Aqu ejecutamos nuestras tareas costosas

4 }

5 }).start();

Hasta aqu todo sencillo y relativamente limpio. Los problemas aparecen cuando nos damos cuenta que desde este
hilo secundario que hemos creado no podemos hacer referencia directa a componentes que se ejecuten en el hilo
principal, entre ellos los controles que forman nuestra interfaz de usuario, es decir, que desde el mtodo run() no
podramos ir actualizando directamente nuestra barra de progreso de la misma forma que lo hacamos antes. Para
solucionar esto, Android proporciona varias alternativas, entre ellas la utilizacin del mtodo post() para actuar
sobre cada control de la interfaz, o la llamada al mtodo runOnUiThread() para enviar operaciones al hilo principal
desde el hilo secundario [Nota: S, vale, s que no he nombrado la opcin de los Handler, pero no quera complicar
ms el tema por el momento]. Ambas opciones requieren como parmetro un nuevo objeto Runnable del que
nuevamente habr que implementar su mtodo run() donde se acte sobre los elementos de la interfaz. Por ver
algn ejemplo, en nuestro caso hemos utilizado el mtodo post() para actuar sobre el controlProgressBar, y el
mtodo runOnUiThread()para mostrar el mensaje toast.

1 new Thread(new Runnable() {

2 public void run() {

3 pbarProgreso.post(new Runnable() {

4 public void run() {

5 pbarProgreso.setProgress(0);

6 }

7 });

9 for(int i=1; i<=10; i++) {

10 tareaLarga();

11 pbarProgreso.post(new Runnable() {

12 public void run() {

13 pbarProgreso.incrementProgressBy(10);

14 }

15 });

16 }

17

18 runOnUiThread(new Runnable() {
19 public void run() {

20 Toast.makeText(MainHilos.this, "Tarea finalizada!",

21 Toast.LENGTH_SHORT).show();

22 }

23 });

24 }

25 }).start();

Utilicemos este cdigo dentro de un nuevo botn de nuestra aplicacin de ejemplo y vamos a probarlo en el
emulador.

Puedes verlo en el vdeo entre los segundos 00:29 00:43

Ahora s podemos ver el progreso de nuestra tarea reflejado en la barra de progreso. La creacin de un hilo
secundario nos ha permitido mantener el hilo principal libre de forma que nuestra interfaz de usuario de actualiza
sin problemas durante la ejecucin de la tarea en segundo plano. Sin embargo miremos de nuevo el cdigo anterior.
Complicado de leer, verdad? Y eso considerando que tan slo estamos actualizando un control de nuestra interfaz.
Si el nmero de controles fuera mayor, o necesitramos una mayor interaccin con la interfaz el cdigo empezara a
ser inmanejable, difcil de leer y mantener, y por tanto tambin ms propenso a errores. Pues bien, aqu es donde
Android llega en nuestra ayuda y nos ofrece la clase AsyncTask, que nos va a permitir realizar esto mismo pero con la
ventaja de no tener que utilizar artefactos del tipo runOnUiThread() y de una forma mucho ms organizada y legible.
La forma bsica de utilizar la clase AsyncTaskconsiste en crear una nueva clase que extienda de ella y sobrescribir
varios de sus mtodos entre los que repartiremos la funcionalidad de nuestra tarea. Estos mtodos son los
siguientes:

onPreExecute(). Se ejecutar antes del cdigo principal de nuestra tarea. Se suele utilizar para preparar la
ejecucin de la tarea, inicializar la interfaz, etc.

doInBackground(). Contendr el cdigo principal de nuestra tarea.

onProgressUpdate(). Se ejecutar cada vez que llamemos al mtodo publishProgress()desde el


mtodo doInBackground().

onPostExecute(). Se ejecutar cuando finalice nuestra tarea, o dicho de otra forma, tras la finalizacin del
mtodo doInBackground().

onCancelled(). Se ejecutar cuando se cancele la ejecucin de la tarea antes de su finalizacin normal.

Estos mtodos tienen una particularidad esencial para nuestros intereses. El mtododoInBackground() se ejecuta en
un hilo secundario (por tanto no podremos interactuar con la interfaz), pero sin embargo todos los dems se
ejecutan en el hilo principal, lo que quiere decir que dentro de ellos podremos hacer referencia directa a nuestros
controles de usuario para actualizar la interfaz. Por su parte, dentro de doInBackground() tendremos la posibilidad
de llamar peridicamente al mtodo publishProgress() para que automticamente desde el
mtodoonProgressUpdate() se actualice la interfaz si es necesario. Al extender una nueva clase
deAsyncTaskindicaremos tres parmetros de tipo:

1. El tipo de datos que recibiremos como entrada de la tarea en el mtodo doInBackground().

2. El tipo de datos con el que actualizaremos el progreso de la tarea, y que recibiremos como parmetro del
mtodo onProgressUpdate() y que a su vez tendremos que incluir como parmetro del
mtodo publishProgress().

3. El tipo de datos que devolveremos como resultado de nuestra tarea, que ser el tipo de retorno del
mtodo doInBackground() y el tipo del parmetro recibido en el mtodoonPostExecute().
En nuestro caso de ejemplo, extenderemos de AsyncTask indicando los
tipos Void, Integer yBooleanrespectivamente, lo que se traducir en que:

doInBackground() no recibir ningn parmetro de entrada (Void).

publishProgress() y onProgressUpdate() recibirn como parmetros datos de tipo entero (Integer).

doInBackground() devolver como retorno un dato de tipo booleano y onPostExecute()tambin recibir


como parmetro un dato del dicho tipo (Boolean).

Dicho esto, cmo repartiremos la funcionalidad de nuestra tarea entre los distintos mtodos. Pues sencillo,
en onPreExecute() inicializaremos la barra de progreso estableciendo su valor mximo y ponindola a cero para
comenzar. En doInBackground() ejecutaremos nuestro bucle habitual llamando a publishProgress() tras cada
iteracin indicando el progreso actual. EnonProgressUpdate() actualizaremos el estado de la barra de progreso con
el valor recibido como parmetro. y por ltimo en onPostExecute() mostraremos el mensaje Toast de finalizacin de
la tarea. Veamos el cdigo completo:

1 private class MiTareaAsincrona extends AsyncTask<Void, Integer, Boolean> {

3 @Override

4 protected Boolean doInBackground(Void... params) {

6 for(int i=1; i<=10; i++) {

7 tareaLarga();

9 publishProgress(i*10);

10

11 if(isCancelled())

12 break;

13 }

14

15 return true;

16 }

17

18 @Override

19 protected void onProgressUpdate(Integer... values) {

20 int progreso = values[0].intValue();

21

22 pbarProgreso.setProgress(progreso);

23 }

24
25 @Override

26 protected void onPreExecute() {

27 pbarProgreso.setMax(100);

28 pbarProgreso.setProgress(0);

29 }

30

31 @Override

32 protected void onPostExecute(Boolean result) {

33 if(result)

34 Toast.makeText(MainHilos.this, "Tarea finalizada!",

35 Toast.LENGTH_SHORT).show();

36 }

37

38 @Override

39 protected void onCancelled() {

40 Toast.makeText(MainHilos.this, "Tarea cancelada!",

41 Toast.LENGTH_SHORT).show();

42 }

43 }

Si observamos con detenimiento el cdigo, la nica novedad que hemos introducido es la posibilidad de cancelar la
tarea en medio de su ejecucin. Esto se realiza llamando al mtodo cancel() de nuestraAsyncTask (para lo cual
aadiremos un botn ms a nuestra aplicacin de ejemplo, adems del nuevo que aadiremos para comenzar la
tarea). Dentro de la ejecucin de nuestra tarea endoInBackground() tendremos adems que consultar
periodicamente el resultado del mtodoisCancelled() que nos dir si el usuario ha cancelado la tarea (es decir, si se
ha llamado al mtodocancel()), en cuyo caso deberemos de terminar la ejecucin lo antes posible, en nuestro caso
de ejemplo simplemente saldremos del bucle con la instruccin break. Adems, tendremos en cuenta que en los
casos que se cancela la tarea, tras el mtodo doInBackground() no se llamar aonPostExecute() sino al
mtodo onCancelled(), dentro del cual podremos realizar cualquier accin para confirma la cancelacin de la tarea.
En nuestro caso mostraremos un mensaje Toast informando de ello.

Puedes verlo en el vdeo entre los segundos 00:44 01:06

Mucho mejor que las alternativas anteriores, verdad? Pero vamos a mostrar una opcin ms. Si queremos que el
usuario pueda ver el progreso de nuestra tarea en segundo plano, pero no queremos que interacte mientras tanto
con la aplicacin tenemos la opcin de mostrar la barra de progreso dentro de un dilogo. Android nos proporciona
directamente un componente de este tipo en forma de un tipo especial de dilogo llamado ProgressDialog.

Configurar un cuadro de dilogo de este tipo es muy sencillo. Para ello vamos a aadir un botn ms a nuestra
aplicacin de ejemplo, donde inicializaremos el dilogo y lanzaremos la tarea en segundo plano. Para inicializar el
dilogo comenzaremos por crear un nuevo objeto ProgressDialog pasndole como parmetro el contexto actual.
Tras esto estableceremos su estilo: STYLE_HORIZONTAL para una barra de progreso tradicional,
o STYLE_SPINNER para un indicador de progreso de tipo indeterminado.
ProgressDialog horizontal

ProgressDialog spinner

Lo siguiente ser especificar el texto a mostrar en el dilogo, en nuestro caso el mensaje Procesando, y el valor
mximo de nuestro progreso, que lo mantendremos en 100. Por ltimo indicaremos si deseamos que el dilogo
sea cancelable, es decir, que el usuario pueda cerrarlo pulsando el botn Atrs del telfono. Para nuestro ejemplo
activaremos esta propiedad para ver cmo podemos cancelar tambin nuestra tarea en segundo plano cuando el
usuario cierra el dilogo. Tras la configuracin del dilogo lanzaremos la AsyncTask del ejemplo anterior, que
tendremos que modificar ligeramente para adaptarla al nuevo dilogo. Veamos el cdigo por ahora:

1 btnAsyncDialog.setOnClickListener(new OnClickListener() {

3 @Override

4 public void onClick(View v) {

6 pDialog = new ProgressDialog(MainHilos.this);

7 pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);

8 pDialog.setMessage("Procesando...");

9 pDialog.setCancelable(true);

10 pDialog.setMax(100);

11

12 tarea2 = new MiTareaAsincronaDialog();

13 tarea2.execute();

14 }

15 });

La AsyncTask ser muy similar a la que ya implementamos. De hecho el mtodo doInBackground()no sufrir cambios.

En onProgressUpdate() la nica diferencia ser que actualizaremos el progreso llamando al


mtodosetProgress() del ProgressDialog en vez de la ProgressBar.

El cdigo de onPreExecute() s tendr algn cambio ms. Aprovecharemos este mtodo para implementar el
evento onCancel del dilogo, dentro del cual cancelaremos tambin la tarea en segundo plano llamando al
mtodo cancel(). Adems, inicializaremos el progreso del dilogo a 0 y lo mostraremos al usuario mediante el
mtodo show().
Por ltimo, en el mtodo onPostExecute() adems de mostrar el Toast de finalizacin, tendremos que cerrar
previamente el dilogo llamando a su mtodo dismiss().

Veamos el cdigo completo de la AsyncTask modificada para usar el nuevo ProgressDialog.

1 private class MiTareaAsincronaDialog extends AsyncTask<Void, Integer, Boolean> {

3 @Override

4 protected Boolean doInBackground(Void... params) {

6 for(int i=1; i<=10; i++) {

7 tareaLarga();

9 publishProgress(i*10);

10

11 if(isCancelled())

12 break;

13 }

14

15 return true;

16 }

17

18 @Override

19 protected void onProgressUpdate(Integer... values) {

20 int progreso = values[0].intValue();

21

22 pDialog.setProgress(progreso);

23 }

24

25 @Override

26 protected void onPreExecute() {

27

28 pDialog.setOnCancelListener(new OnCancelListener() {

29 @Override

30 public void onCancel(DialogInterface dialog) {

31 MiTareaAsincronaDialog.this.cancel(true);
32 }

33 });

34

35 pDialog.setProgress(0);

36 pDialog.show();

37 }

38

39 @Override

40 protected void onPostExecute(Boolean result) {

41 if(result)

42 {

43 pDialog.dismiss();

44 Toast.makeText(MainHilos.this, "Tarea finalizada!",

45 Toast.LENGTH_SHORT).show();

46 }

47 }

48

49 @Override

50 protected void onCancelled() {

51 Toast.makeText(MainHilos.this, "Tarea cancelada!",

52 Toast.LENGTH_SHORT).show();

53 }

54 }

Si ahora ejecutamos nuestro proyecto y pulsamos sobre el ltimo botn incluido veremos cmo el dilogo aparece
por encima de nuestra actividad mostrando el progreso de la tarea asncrona. Tras finalizar, el dilogo desaparece y
se muestra el mensaje toast de finalizacin. Si en cambio, se pulsa el botn Atrs del dispositivo antes de que la
tarea termine el dilogo se cerrar y se mostrar el mensaje de cancelacin.

Puedes verlo en el vdeo entre los segundos 01:07 01:35

Y con esto habramos concluido este primer artculo sobre hilos y tareas en segundo plano. Os dejo a continuacin el
vdeo de demostracin de la aplicacin de ejemplo construida durante el tema.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.

Demo Hilos y AsyncTask en Android

http://www.youtube.com/watch?v=tSia91rGaCo
Tareas en segundo plano en Android (II): IntentService
by Sgoliver on 05/08/2012 in Android, Programacin

En el artculo anterior del Curso de Programacin Android vimos cmo ejecutar tareas en segundo plano haciendo
uso de hilos (Thread) y tareas asncronas (AsyncTask). En este nuevo artculo nos vamos a centrar en una alternativa
menos conocida, aunque tanto o ms interesante, para conseguir el mismo objetivo: ejecutar una determinada tarea
en un hilo independiente al hilo principal de la aplicacin. Esta opcin se llama IntentService, y no es ms que un tipo
particular de servicio Android que se preocupar por nosotros de la creacin y gestin del nuevo hilo de ejecucin y
de detenerse a s mismo una vez concluida su tarea asociada.

Como en el caso de las AsyncTask, la utilizacin de un IntentService va a ser tan sencilla como extender una nueva
clase de IntentService e implementar su mtodo onHandleIntent(). Este mtodo recibe como parmetro un Intent,
que podremos utilizar para pasar al servicio los datos de entrada necesarios.

A diferencia de las AsyncTask, un IntentService no proporciona mtodos que se ejecuten en el hilo principal de la
aplicacin y que podamos aprovechar para comunicarnos con nuestra interfaz durante la ejecucin. ste es el
motivo principal de que los IntentService sean una opcin menos utilizada a la hora de ejecutar tareas que requieran
cierta vinculacin con la interfaz de la aplicacin. Sin embargo tampoco es imposible su uso en este tipo de
escenarios ya que podremos utilizar por ejemplo mensajesbroadcast (y por supuesto su BroadcastReceiver asociado
capaz de procesar los mensajes) para comunicar eventos al hilo principal, como por ejemplo la necesidad de
actualizar controles de la interfaz o simplemente para comunicar la finalizacin de la tarea ejecutada. En este
artculo veremos cmo implementar este mtodo para conseguir una aplicacin de ejemplo similar a la que
construimos en el artculo anterior utilizando AsyncTask.

Empezaremos extendiendo una nueva clase derivada de IntentService, que para ser originales
llamaremos MiIntentService. Lo primero que tendremos que hacer ser implementar un constructor por defecto.
Este constructor lo nico que har ser llamar al constructor de la clase padre pasndole el nombre de nuestra
nueva clase.

A continuacin implementaremos el mtodo onHandleIntent(). Como ya hemos indicado, este mtodo ser el que
contenga el cdigo de la tarea a ejecutar en segundo plano. Para simular una tarea de larga duracin utilizaremos el
mismo bucle que ya vimos en el artculo anterior con la novedad de que esta vez el nmero de iteraciones ser
variable, de forma que podamos experimentar con cmo pasar datos de entrada a travs del Intent recibido como
parmetro en onHandleIntent(). En nuestro caso de ejemplo pasaremos un slo dato de entrada que indique el
nmero de iteraciones. Por tanto, lo primero que haremos ser obtener este dato a partir del Intent mediante el
mtodogetIntExtra(). Una vez conocemos el nmero de iteraciones, tan slo nos queda ejecutar el bucle y comunicar
el progreso tras cada iteracin.

Como ya hemos comentado, para comunicar este progreso vamos a hacer uso de mensajes broadcast. Para enviar
este tipo de mensajes necesitamos construir un Intent, asociarle un nombre de accin determinada que lo
identifique mediante setAction(), e incluir los datos que necesitemos comunicar aadiendo tantos extras como sean
necesarios mediante el mtodo putExtra(). Los nombres de las acciones se suelen preceder con el paquete java de
nuestra aplicacin de forma que nos aseguremos que es un identificador nico. En nuestro caso lo llamaremos
net.sgoliver.intent.action.PROGRESO y lo definiremos como atributo esttico de la clase para mayor comodidad,
llamado ACTION_PROGRESO. Por su parte, los datos a comunicar en nuestro ejemplo ser solo el nivel de progreso,
por lo que slo aadiremos un extra a nuestro intent con dicho dato. Por ltimo enviaremos el mensaje llamando al
mtodo sendBroadcast() pasndole como parmetro el intent recin creado. Veamos cmo quedara el cdigo
completo.

1 public class MiIntentService extends IntentService {

3 public static final String ACTION_PROGRESO =


4 "net.sgoliver.intent.action.PROGRESO";

5 public static final String ACTION_FIN =

6 "net.sgoliver.intent.action.FIN";

8 public MiIntentService() {

9 super("MiIntentService");

10 }

11

12 @Override

13 protected void onHandleIntent(Intent intent)

14 {

15 int iter = intent.getIntExtra("iteraciones", 0);

16

17 for(int i=1; i<=iter; i++) {

18 tareaLarga();

19

20 //Comunicamos el progreso

21 Intent bcIntent = new Intent();

22 bcIntent.setAction(ACTION_PROGRESO);

23 bcIntent.putExtra("progreso", i*10);

24 sendBroadcast(bcIntent);

25 }

26

27 Intent bcIntent = new Intent();

28 bcIntent.setAction(ACTION_FIN);

29 sendBroadcast(bcIntent);

30 }

31

32 private void tareaLarga()

33 {

34 try {

35 Thread.sleep(1000);

36 } catch(InterruptedException e) {}

37 }
38 }

Como podis comprobar tambin he aadido un nuevo tipo de mensaje broadcast (ACTION_FIN), esta vez sin datos
adicionales, para comunicar a la aplicacin principal la finalizacin de la tarea en segundo plano.

Adems de la implementacin del servicio, recordemos que tambin tendremos que declararlo en
elAndroidManifest.xml, dentro de la seccin <application>:

1 <service android:name=".MiIntentService"></service>

Y con esto ya tendramos implementado nuestro servicio. El siguiente paso ser llamar al servicio para comenzar su
ejecucin. Esto lo haremos desde una actividad principal de ejemplo en la que tan slo colocaremos una barra de
progreso y un botn para lanzar el servicio. El cdigo del botn para ejecutar el servicio ser muy sencillo, tan slo
tendremos que crear un nuevo intent asociado a la claseMiIntentService, aadir los datos de entrada necesarios
mediante putExtra() y ejecutar el servicio llamando a startService() pasando como parmetro el intent de entrada.
Como ya dijimos, el nico dato de entrada que pasaremos ser el nmero de iteraciones a ejecutar.

1 btnEjecutar.setOnClickListener(new OnClickListener() {

3 @Override

4 public void onClick(View v) {

5 Intent msgIntent = new Intent(MainActivity.this, MiIntentService.class);

6 msgIntent.putExtra("iteraciones", 10);

7 startService(msgIntent);

8 }

9 });

Con esto ya podramos ejecutar nuestra aplicacin y lanzar la tarea, pero no podramos ver el progreso de sta ni
saber cundo ha terminado porque an no hemos creado el BroadcastReceiver necesario para capturar los mensajes
broadcast que enva el servicio durante su ejecucin.

Para ello, como clase interna a nuestra actividad principal definiremos una nueva clase que extienda
aBroadcastReceiver y que implemente su mtodo onReceive() para gestionar los
mensajesACTION_PROGRESO y ACTION_FIN que definimos en nuestro IntentService. En el caso de
recibirseACTION_PROGRESO extraeremos el nivel de progreso del intent recibido y actualizaremos
consecuentemente la barra de progreso mediante setProgress(). En caso de recibirse ACTION_FINmostraremos un
mensaje Toast informando de la finalizacin de la tarea.

1 public class ProgressReceiver extends BroadcastReceiver {

3 @Override

4 public void onReceive(Context context, Intent intent) {

5 if(intent.getAction().equals(MiIntentService.ACTION_PROGRESO)) {

6 int prog = intent.getIntExtra("progreso", 0);

7 pbarProgreso.setProgress(prog);

8 }

9 else if(intent.getAction().equals(MiIntentService.ACTION_FIN)) {
10 Toast.makeText(MainActivity.this, "Tarea finalizada!", Toast.LENGTH_SHORT).show();

11 }

12 }

13 }

Pero an no habramos terminado dado, ya que aunque hayamos implementado nuestroBroadcastReceiver, ste no
tendr ningn efecto a menos que lo registremos con la aplicacin y lo asociemos a los tipos de mensaje que deber
tratar (mediante un IntentFilter). Para hacer esto, al final del mtodo onCreate() de nuestra actividad principal
crearemos un IntentFilter al que asociaremos mediante addAction() los dos tipos de mensaje broadcast que
queremos capturar, instanciaremos nuestro BroadcastReceiver y lo registraremos mediante registerReceiver(), al
que pasaremos la instancia creada y el filtro de mensajes.

1 IntentFilter filter = new IntentFilter();

2 filter.addAction(MiIntentService.ACTION_PROGRESO);

3 filter.addAction(MiIntentService.ACTION_FIN);

4 ProgressReceiver rcv = new ProgressReceiver();

5 registerReceiver(rcv, filter);

Y con esto s habramos concluido nuestra aplicacin de ejemplo. Si ejecutamos la aplicacin en el emulador y
pulsamos el botn de comenzar la tarea veremos cmo la barra de progreso comienza a avanzar hasta el final,
momento en el que deber aparecer el mensaje toast indicando la finalizacin de la tarea.

Android IntentService

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pgina del curso en GitHub.
Google Play Services
Google Play Services: Introduccin y Preparativos
by Sgoliver on 18/08/2013 in Android, Programacin

Desde hace algn tiempo, y de forma bastante acertada, Google est tendiendo a incorporar muchas de sus nuevas
APIs para desarrolladores Android (y actualizaciones de algunas ya existentes) dentro de los llamados Google Play
Services. As por ejemplo, la API de mapas, la de localizacin, o la de mensajera push, que antes existan de forma
independiente, han pasado a formar parte de estos servicios en los ltimos meses. Y por supuesto se han
incorporado otras nuevas, como la de integracin con Google+ o los famosos Game Services.

Los Google Play Services viven como una aplicacin ms en todos los dispositivos Android (versin 2.2 y superiores),
lo que nos aporta la ventaja de no tener que preocuparnos de ellos, ya que son actualizados automticamente por la
plataforma cuando existen novedades. Por decirlo de alguna forma, nosotros tan slo nos tendremos que preocupar
de conectarnos a ellos y utilizar las distintas funcionalidades como si fueran parte de nuestra propia aplicacin.

En este artculo inicial vamos a ver cmo podemos crear en Eclipse un proyecto capaz de hacer uso de los Google
Play Services. Una vez preparado el proyecto como veremos aqu cada servicio requerir de pasos adicionales para
su utilizacin, por ejemplo el uso de Google Maps requerir de la obtencin de una clave de acceso que nos permita
el uso de su API. Estos preparativos adicionales los veremos en cada captulo especfico de cada uno de los servicios.

Empecemos. Cuando queremos hacer uso de cualquiera de las APIs incluidas en los Google Play Services lo primero
que tendremos que hacer ser importar en Eclipse el proyecto de librera donde se implementan. Este proyecto se
puede descargar mediante el SDK Manager de Android, accediendo a la seccin Extras y marcando el paquete
llamado Google Play Services.

Si observis la captura anterior, veris que tambin existe un paquete llamado Google Play Services for Froyo. ste
ltimo slo deberamos utilizarlo (en sustitucin del primero) si nuestra aplicacin necesita ejecutarse en
dispositivos con Android 2.2, ya que esta versin de los Play Services probablemente no recibir las futuras
actualizaciones de los servicios. Por tanto, si la versin mnima sobre la que debe ejecutarse nuestra aplicacin es al
menos la 2.3 usaremos siempre el paquete Google Play Services.

Una vez descargado podremos encontrarlo en la siguiente ruta:

<ruta-sdk>\extras\google\google_play_services\libproject\google-play-services_lib

Para importarlo en Eclipse utilizaremos la opcin de men File / Import, y seleccionaremos la opcin Android /
Existing Android Code Into Workspace.
Pulsamos el botn Next y accedemos a las opciones de importacin. En el campo Root Directory indicamos la
ruta indicada anteriormente, nos aseguramos que el proyecto llamado google-play-services_lib queda marcado en
la lista de Projects to Import, y marcamos la opcin Copy projects into workspace.

Finalmente pulsamos Finish y el proyecto de librera de los Google Play Services quedar importado en nuestro
explorador de paquetes de Eclipse. Como ltimo paso debemos entrar a las propiedades del proyecto importado
(botn derecho / Properties), accedemos a la seccin Java Build Path y nos aseguramos de que la opcin Android
Private Libraries est marcada en la ventana Order and Export.

Aceptamos, y con esto ya tenemos el proyecto de librera preparado.

El siguiente paso es crear nuestro proyecto, que despus har uso de esta librera. Para ello, creamos un nuevo
proyecto Android como siempre (File / New / Project / Android / Android Application Project) y lo configuramos
mediante las distintas pantallas del asistente (si no tienes experiencia creando este tipo de proyectos te recomiendo
leer los captulos iniciales del curso). Una vez creado accedemos a sus propiedades (botn derecho / Properties) y
entramos en la seccin Android. Aqu es donde debemos aadir la referencia al proyecto que hemos importado
para los Google Play Services. Pulsamos el botn Add, seleccionamos el proyecto importado y aceptamos.

A continuacin, editaremos el fichero AndroidManifest.xml de nuestra aplicacin y aadiremos la siguiente clusula


<meta-data> dentro del elemento <application>:

1 <meta-data android:name="com.google.android.gms.version"

2 android:value="@integer/google_play_services_version" />

Por ltimo, aadiremos al final del fichero proguard-project.txt las siguientes lineas para evitar que esta herramienta
elimine algunas clases necesarias:

1 -keep class * extends java.util.ListResourceBundle {

2 protected Object[][] getContents();

3 }

5 -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {

6 public static final *** NULL;

7 }

9 -keepnames @com.google.android.gms.common.annotation.KeepName class *

10 -keepclassmembernames class * {

11 @ccom.google.android.gms.common.annotation.KeepName *;

12 }

13

14 -keepnames class * implements android.os.Parcelable {

15 public static final ** CREATOR;

16 }

Con esto ya tendramos nuestro proyecto preparado para hacer uso de cualquiera de las APIs incluidas en los Google
Play Services. En los artculos dedicados a cada una de estas APIs haremos siempre referencia a ste para preparar el
proyecto y describiremos posteriormente los pasos adicionales especficos de cada servicio.
Google Play Services: Mapas en Android
Mapas en Android (Google Maps Android API v2) I
by Sgoliver on 05/12/2012 in Android, Programacin

En diciembre de 2012, Google presentaba la segunda versin de su API de Google Maps para Android. Esta nueva
versin presenta muchas novedades interesantes, de las que cabe destacar las siguientes:

Integracin con los Servicios de Google Play (Google Play Services) y la Consola de APIs.

Utilizacin a travs de un nuevo tipo especfico de fragment (MapFragment), una mejora muy esperada por
muchos.

Utilizacin de mapas vectoriales, lo que repercute en una mayor velocidad de carga y una mayor eficiencia
en cuanto a uso de ancho de banda.

Mejoras en el sistema de cach, lo que reducir en gran medida las famosas reas en blanco que tardan en
cargar.

Los mapas son ahora 3D, es decir, podremos mover nuestro punto de vista de forma que lo veamos en
perspectiva.

Al margen de las novedades generales, como desarrolladores qu diferencias nos vamos a encontrar con respecto a
la API anterior a la hora de desarrollar nuestras aplicaciones Android?

Pues la principal ser el componente que utilizaremos para la inclusin de mapas en nuestra aplicacin. Si
recordamos la anterior versin de la API, para incluir un mapa en la aplicacin debamos utilizar un control de
tipo MapView, que adems requera que su actividad contenedora fuera del tipoMapActivity. Con la nueva API nos
olvidaremos de estos dos componentes y pasaremos a tener slo uno, un nuevo tipo especfico
de fragment llamado MapFragment. Esto nos permitir entre otras cosas aadir uno [o varios, esto tambin es una
novedad] mapas a cualquier actividad, sea del tipo que sea, y contando por supuesto con todas las ventajas del uso
de fragments. Nota importante: dado que el nuevo control de mapas se basa en fragments, si queremos mantener
la compatibilidad con versiones de Android anteriores a la 3.0 tendremos que utilizar la librera de soporte android-
support. Ms adelante veremos ms detalles sobre esto.

Adems de esta novedad, la integracin de la API con los Google Play Services y la Consola de APIsde Google, harn
que los preparativos del entorno, las libreras utilizadas, y el proceso de obtencin de la API Key de Google Maps
sean un poco distintos a los que ya vimos en los artculos dedicados la primera versin.

Pues bien, en este nuevo artculo del Curso de Programacin Android vamos a describir los pasos necesarios para
hacer uso de la nueva versin de la API de mapas de Google (Google Maps Android API v2).

Como en los artculos previos donde aprendimos a utilizar la API v1, en este caso tambin ser necesario realizar
algunos preparativos y tareas previas antes de poder empezar a utilizarlos en nuestras aplicaciones.

En primer lugar, dado que la API v2 se proporciona como parte del SDK de Google Play Services, ser necesario
incorporar y configurar previamente en nuestro entorno de desarrollo dicho paquete. Ya dedicamos un artculo
especfico a describir esta tarea, por lo que no lo volveremos a repetir aqu.

El siguiente paso ser obtener una API Key para poder utilizar el servicio de mapas de Google en nuestra aplicacin.
Este paso ya lo comentamos en los artculos sobre la API v1, pero en este caso el procedimiento ser algo distinto. La
nueva API de mapas de Android, al igual que la mayora de APIs de Google, est integrada en la nueva Consola de
APIs de Google, por lo que el primer paso ser acceder a ella. Una vez hemos accedido, tendremos que crear un
nuevo proyecto mediante el botn CREATE PROJECT situado en la parte superior izquierda de la pantalla.
Aparecer entonces una ventana que nos solicitar el nombre e ID del proyecto. Introducimos algn nombre
descriptivo y un ID nico y aceptamos pulsando Create.

Una vez creado el proyecto, accederemos a la opcin APIs & Auth del men izquierdo. Desde esta ventana
podemos activar o desactivar cada una de las APIs de Google que queremos utilizar. En este caso slo activaremos la
llamada Google Maps Android API v2 pulsando sobre el botn ON/OFF situado justo a su derecha.

Una vez activado accederemos a la opcin Registered Apps del men izquierdo (submen de APIs & Auth) y
pulsaremos el botn REGISTER APP para registrar nuestra aplicacin.

Accediendo a dicha opcin tendremos la posibilidad de obtener nuestra nueva API Key que nos permita utilizar el
servicio de mapas desde nuestra aplicacin particular. Tendremos que indicar el nombre de la aplicacin, su tipo
(Android), el modo de acceso a la API (en este caso Accessing APIs directly from Android), el paquete java
utilizado en la aplicacin (que en mi caso ser net.sgoliver.android.mapasapi2) y la huella digital SHA1 del
certificado con el que firmamos la aplicacin.
Este ltimo dato introducido requiere alguna explicacin. Toda aplicacin Android debe ir firmada para poder
ejecutarse en un dispositivo, tanto fsico como emulado. Este proceso de firma es uno de los pasos que tenemos que
hacer siempre antes de distribuir pblicamente una aplicacin. Adicionalmentes, durante el desarrollo de la misma,
para realizar las pruebas y la depuracin del cdigo, aunque no seamos conscientes de ello tambin estamos firmado
la aplicacin con un certificado de pruebas.

En las ltimas versiones de Eclipse y el plugin ADT de Android, podemos consultar directamente la huella SHA1 de
nuestro certificado de pruebas accediendo al men Window /Preferences y entrando a la seccin Android / Build.

En caso de disponer de una versin ms antigua que no muestre directamente este dato, lo que al menos s debe
aparecer es la ruta del certificado de pruebas (campo Default debug keystore). Como se puede observar, en mi
caso el certificado de pruebas est en la ruta C:\Users\Salvador\.android\debug.keystore. Pues bien, si tuviramos
que obtener manualmente nuestra huella digital SHA1 deberemos acceder a dicha ruta desde la consola de
comando de Windows y ejecutar los siguientes comandos:

1 C:\>cd C:\Users\Salvador\.android\

3 C:\Users\Salvador\.android>"C:\Program Files\Java\jdk1.7.0_07\bin\keytool.exe" -list -v -keystore debug.keystore -alias

Suponiendo que tu instalacin de Java est en la ruta C:\Program Files\Java\jdk1.7.0_07. Si no es as slo debes
sustituir sta por la correcta. Esto nos deber devolver varios datos del certificado, entre ellos la huella SHA1.
Una vez rellenos todos los datos de la aplicacin, pulsamos el botn Register y ya deberamos tener nuestra API
Key generada, podremos verla en la pantalla siguiente dentro del apartado Android Key. Apuntaremos este dato
para utilizarlo ms tarde.

Con esto ya habramos concluido los preparativos iniciales necesarios para utilizar el servicio de mapas de Android
en nuestras propias aplicaciones, por lo que empecemos a crear un proyecto de ejemplo en Eclipse.

Abriremos Eclipse y crearemos un nuevo proyecto estandar de Android, en mi caso lo he llamado android-mapas-
api2. Recordemos utilizar para el proyecto el mismo paquete java que hemos indicado durante la obtencin de la
API key.

Tras esto lo primero que haremos ser aadir al fichero AndroidManifest.xml la API Key que acabamos de generar.
Para ello aadiremos al fichero, dentro de la etiqueta <application>, un nuevo elemento<meta-data> con los
siguientes datos:

1 ...

2 <application>

3 ...

4 <meta-data android:name="com.google.android.maps.v2.API_KEY"

5 android:value="api_key"/>

6 ...

7 </application>

Como valor del parmetro android:value tendremos que poner nuestra API Key recien generada.

Siguiendo con el AndroidManifest, tambin tendremos que incluir una serie de permisos que nos permitan acceder a
internet (INTERNET), conocer el estado de la red (ACCESS_NETWORK_STATE), acceder al almacenamiento externo
del dispositivo para la cach de mapas (WRITE_EXTERNAL_STORAGE) y hacer uso de los servicios web de Google
(READ_GSERVICES).

1 <uses-permission android:name="android.permission.INTERNET"/>

2 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

3 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

4 <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>

Por ltimo, dado que la API v2 de Google Maps Android utiliza OpenGL ES versin 2, deberemos especificar tambin
dicho requisito en nuestro AndroidManifest aadiendo un nuevo elemento <uses-feature>:

1 <uses-feature android:glEsVersion="0x00020000"

2 android:required="true"/>

El siguiente paso ser referenciar el proyecto de librera de los Google Play Services desde nuestro proyecto de
ejemplo, si no lo hemos hecho ya. Para ello iremos a sus propiedades pulsando botn derecho / Properties sobre
nuestro proyecto y accediendo a la seccin Android de las preferencias. En dicha ventana podemos aadir una nueva
librera en la seccin inferior llamada Library. Cuando pulsamos el botn Add nos aparecer la librera recien
importada y podremos seleccionarla directamente, aadindose a nuestra lista de libreras referenciadas por
nuestro proyecto.

Como ltimo paso de configuracin de nuestro proyecto, si queremos que nuestra aplicacin se pueda ejecutar
desde versiones antiguas de Android (concretamente desde la versin de Android 2.2) deberemos asegurarnos de
que nuestro proyecto incluye la librera android-support-v4.jar, que debera aparecer si desplegamos las seccin
Android Private Libraries o la carpeta libs de nuestro proyecto.
Las versiones ms recientes de ADT incluyen por defecto esta librera en nuestros proyectos, pero si no est incluida
podis hacerlo mediante la opcin del men contextual Android Tools / Add Support Library sobre el proyecto, o
bien de forma manual.

Y con esto hemos terminado de configurar todo lo necesario. Ya podemos escribir nuestro cdigo. Y para este primer
artculo sobre el tema nos vamos a limitar a mostrar un mapa en la pantalla principal de la aplicacin. En artculos
posteriores veremos como aadir otras opciones o elementos al mapa.

Para esto tendremos simplemente que aadir el control correspondiente al layout de nuestra actividad principal. En
el caso de la nueva API v2 este control se aadir en forma de fragment (de ah que hayamos tenido que incluir la
librera android-support para poder utilizarlos en versiones de Android anteriores a la 3.0) de un determinado tipo
(concretamente de la nueva clase com.google.android.gms.maps.SupportMapFragment), quedando por ejemplo de
la siguiente forma:

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

2 <fragment xmlns:android="http://schemas.android.com/apk/res/android"

3 android:id="@+id/map"

4 android:layout_width="match_parent"

5 android:layout_height="match_parent"

6 class="com.google.android.gms.maps.SupportMapFragment"/>

Por supuesto, dado que estamos utilizando fragments, la actividad principal tambin tendr que extender
a FragmentActivity (en vez de simplemente Activity como es lo normal). Usaremos tambin la versin
de FragmentActivity incluida en la librera android-support para ser compatibles con la mayora de las versiones
Android actuales.

1 public class MainActivity extends android.support.v4.app.FragmentActivity {

3 @Override

4 protected void onCreate(Bundle savedInstanceState) {

5 super.onCreate(savedInstanceState);

6 setContentView(R.layout.activity_main);
7 }

9 ...

10 }

Con esto, ya podramos ejecutar y probar nuestra aplicacin. En mi caso las pruebas las he realizado sobre un
dispositivo fsico con Android 2.2 ya que por el momento parece haber algunos problemas para hacerlo sobre el
emulador. Por tanto tendris que conectar vuestro dispositivo al PC mediante el cable de datos e indicar a Eclipse
que lo utilice para la ejecucin de la aplicacin.

Si ejecutamos el ejemplo deberamos ver un mapa en la pantalla principal de la aplicacin, sobre el que nos
podremos mover y hacer zoom con los gestos habituales o utilizando los controles de zoom incluidos por defecto
sobre el mapa.

Con este artculo espero haber descrito todos los pasos necesarios para comenzar a utilizar los servicios de mapas de
Google utilizando su nueva API Google Maps Android v2. Si tenis cualquier duda o propuesta de mejora no dudis
en escribirlo en los comentarios.

Como habis podido comprobar hay muchos preparativos que hacer, aunque ninguno de ellos de excesiva dificultad.
En los prximos artculos aprenderemos a utilizar ms caractersticas de la nueva API.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pagina del curso en GitHub.
Mapas en Android (Google Maps Android API v2) II
by Sgoliver on 09/12/2012 in Noticias, Programacin

En el artculo anterior del curso vimos cmo realizar todos los preparativos necesarios para comenzar a utilizar la
nueva versin de Google Maps para Android (Google Maps Android API v2): descargar las libreras necesarias,
obtener la API Key y configurar un nuevo proyecto en Eclipse.

En esta segunda entrega vamos a hacer un repaso de las opciones bsicas de los nuevos mapas: elegir el tipo de
mapa a mostrar, movernos por l de forma programtica, y obtener los datos de la posicin actual. Como aplicacin
de ejemplo (que podis descargar al final de este artculo), tomaremos como base la ya creada en el artculo
anterior, a la que aadiremos varias opciones de men (men tradicional o de overflow en la ActionBar,
dependiendo de la versin de Android sobre la que lo ejecutemos) para demostrar el funcionamiento de algunas
funciones del mapa.

Si hacemos un poco de memoria, recordaremos cmo en la antigua versin de la API de Google Maps era bastante
poco homogneo el acceso y modificacin de determinados datos del mapa. Por ejemplo, la consulta de la posicin
actual o la configuracin del tipo de mapa se hacan directamente sobre el control MapView, mientras que la
manipulacin de la posicin y el zoom se hacan a travs delcontrolador asociado al mapa (MapController). Adems,
el tratamiento de las coordenadas y las unidades utilizadas eran algo peculiares, teniendo estar continuamente
convirtiendo de grados a microgrados y de estos a objetos GeoPoint, etc.

Con la nueva API, todas las operaciones se realizarn directamente sobre un objeto GoogleMap, el componente base
de la API. Accederemos a este componente llamando al mtodo getMap() del fragmento MapFragment que
contenga nuestro mapa. Podramos hacerlo de la siguiente forma:

1 import com.google.android.gms.maps.GoogleMap;

2 ...

3 GoogleMap mapa = ((SupportMapFragment) getSupportFragmentManager()

4 .findFragmentById(R.id.map)).getMap();

Una vez obtenida esta referencia a nuestro objeto GoogleMap podremos realizar sobre l la mayora de las acciones
bsicas del mapa.

As, por ejemplo, para modificar el tipo de mapa mostrado podremos utilizar una llamada a su mtodosetMapType(),
pasando como parmetro el tipo de mapa:

MAP_TYPE_NORMAL

MAP_TYPE_HYBRID

MAP_TYPE_SATELLITE

MAP_TYPE_TERRAIN

Para nuestro ejemplo voy a utilizar una variable que almacene el tipo de mapa actual (del 0 al 3) y habilitaremos una
opcin de men para ir alternando entre las distintas opciones. Quedara de la siguiente forma:

1 private void alternarVista()

2 {

3 vista = (vista + 1) % 4;

5 switch(vista)
6 {

7 case 0:

8 mapa.setMapType(GoogleMap.MAP_TYPE_NORMAL);

9 break;

10 case 1:

11 mapa.setMapType(GoogleMap.MAP_TYPE_HYBRID);

12 break;

13 case 2:

14 mapa.setMapType(GoogleMap.MAP_TYPE_SATELLITE);

15 break;

16 case 3:

17 mapa.setMapType(GoogleMap.MAP_TYPE_TERRAIN);

18 break;

19 }

20 }

En cuanto al movimiento sobre el mapa, con esta nueva versin de la API vamos a tener mucha ms libertad que con
la anterior versin, ya que podremos mover libremente nuestro punto de vista (o cmara, como lo han llamado los
chicos de Android) por un espacio 3D. De esta forma, ya no slo podremos hablar de latitud-longitud (target) y
zoom, sino tambin de orientacin (bearing) y ngulo de visin (tilt). La manipulacin de los 2 ltimos parmetros
unida a posibilidad actual de ver edificios en 3D de muchas ciudades nos abren un mundo de posibilidades.

El movimiento de la cmara se va a realizar siempre mediante la construccin de un objetoCameraUpdate con los


parmetros necesarios. Para los movimientos ms bsicos como la actualizacin de la latitud y longitud o el nivel de
zoom podremos utilizar la claseCameraUpdateFactory y sus mtodos estticos que nos facilitar un poco el trabajo.

As por ejemplo, para cambiar slo el nivel de zoom podremos utilizar los siguientes mtodos para crear
nuestro CameraUpdate:

CameraUpdateFactory.zoomIn(). Aumenta en 1 el nivel de zoom.

CameraUpdateFactory.zoomOut(). Disminuye en 1 el nivel de zoom.

CameraUpdateFactory.zoomTo(nivel_de_zoom). Establece el nivel de zoom.

Por su parte, para actualizar slo la latitud-longitud de la cmara podremos utilizar:

CameraUpdateFactory.newLatLng(lat, long). Establece la lat-lng expresadas en grados.

Si queremos modificar los dos parmetros anteriores de forma conjunta, tambin tendremos disponible el mtodo
siguiente:

CameraUpdateFactory.newLatLngZoom(lat, long, zoom). Establece la lat-lng y el zoom.

Para movernos lateralmente por el mapa (panning) podramos utilizar los mtodos de scroll:

CameraUpdateFactory.scrollBy(scrollHorizontal, scrollVertical). Scroll expresado en pxeles.

Tras construir el objeto CameraUpdate con los parmetros de posicin tendremos que llamar a los
mtodos moveCamera() o animateCamera() de nuestro objeto GoogleMap, dependiendo de si queremos que la
actualizacin de la vista se muestre directamente o de forma animada.
Con esto en cuenta, si quisiramos por ejemplo centrar la vista en Espaa con un zoom de 5 podramos hacer lo
siguiente:

1 CameraUpdate camUpd1 =

2 CameraUpdateFactory.newLatLng(new LatLng(40.41, -3.69));

4 mapa.moveCamera(camUpd1);

Adems de los movimientos bsicos que hemos comentado, si queremos modificar los dems parmetros de la
cmara o varios de ellos simultaneamente tendremos disponible el mtodo ms
general CameraUpdateFactory.newCameraPosition() que recibe como parmetro un objeto de tipo CameraPosition.
Este objeto los construiremos indicando todos los parmetros de la posicin de la cmara a travs de su
mtodo Builder() de la siguiente forma:

1 LatLng madrid = new LatLng(40.417325, -3.683081);

2 CameraPosition camPos = new CameraPosition.Builder()

3 .target(madrid) //Centramos el mapa en Madrid

4 .zoom(19) //Establecemos el zoom en 19

5 .bearing(45) //Establecemos la orientacin con el noreste arriba

6 .tilt(70) //Bajamos el punto de vista de la cmara 70 grados

7 .build();

9 CameraUpdate camUpd3 =

10 CameraUpdateFactory.newCameraPosition(camPos);

11

12 mapa.animateCamera(camUpd3);

Como podemos comprobar, mediante este mecanismo podemos modificar todos los parmetros de posicin de la
cmara (o slo algunos de ellos) al mismo tiempo. En nuestro caso de ejemplo hemos centrado la vista del mapa
sobre el parque de El Retiro de Madrid, con un nivel de zoom de 19, una orientacin de 45 grados para que el
noreste est hacia arriba y un ngulo de visin de 70 grados de forma que veamos en 3D el monumento a Alfonso XII
en la vista de mapa NORMAL. En la siguiente imagen vemos el resultado:
Como podis ver, en esta nueva versin de la API se facilita bastante el posicionamiento dentro del mapa, y el uso de
las clases CameraUpdate y CameraPosition resulta bastante intuitivo.

Bien, pues ya hemos hablado de cmo modificar nuestro punto de vista sobre el mapa, pero si el usuario se mueve
de forma manual por l, cmo podemos conocer en un momento dado la posicin de la cmara?

Pues igual de fcil, mediante el mtodo getCameraPosition(), que nos devuelve un objetoCameraPosition como el
que ya conocamos. Accediendo a los distintos mtodos y propiedades de este objeto podemos conocer con
exactitud la posicin de la cmara, la orientacin y el nivel de zoom.

1 CameraPosition camPos = mapa.getCameraPosition();

3 LatLng coordenadas = camPos.target;

4 double latitud = coordenadas.latitude;

5 double longitud = coordenadas.longitude;

7 float zoom = camPos.zoom;

8 float orientacion = camPos.bearing;

9 float angulo = camPos.titl;

En nuestra aplicacin de ejemplo, que podis descargar al final del artculo, he aadido una nueva opcin de men
que muestra en un mensaje toast la latitud y longitud actual de la vista de mapa.
Y con esto habramos terminado de describir las acciones bsicas de configuracin y movimiento sobre el mapa. En
los prximos artculos veremos ms opciones, como la forma de aadir marcadores o dibujar sobre el mapa.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pagina del curso en GitHub.
Mapas en Android (Google Maps Android API v2) III
by Sgoliver on 10/12/2012 in Android, Programacin

En los dos artculos anteriores (I y II) del curso hemos visto cmo crear aplicaciones utilizando la nueva versin de la
API v2 de Google Maps para Android y hemos descrito las acciones principales sobre el mapa.

En este ltimo artculo de la serie nos vamos a centrar en los eventos del mapa que podemos capturar y tratar, en la
creacin y gestin de marcadores, y en el dibujo de lineas y polgonos sobre el mapa.

Sin ms prembulos, comencemos con los eventos. Si hacemos un poco de memoria, con la versin anterior de la
API debamos crear una nueva capa (overlay) para capturar los eventos principales de pulsacin. Sin embargo, el
nuevo componente de mapas soporta directamente los eventos de click, click largo y movimiento de cmara y
podemos implementarlos de la forma habitual, mediante su mtodo set correspondiente.

As por ejemplo, podramos implementar el evento de onclick llamando al mtodosetOnMapClickListener() con un


nuevo listener y sobrescribir el mtodo onMapClick(). Este mtodo recibe como parmetro, en forma de
objeto LatLng, las coordenadas de latitud y longitud sobre las que ha pulsado el usuario. Si quisiramos traducir estas
coordenadas fsica en coordenadas en pantalla podramos utilizar un objeto Projection (similar al que ya
comentamos para la API v1), obtenindolo a partir del mapa a travs del mtodo getProjection() y posteriormente
llamando atoScreenLocation() para obtener las coordenadas (x,y) de pantalla donde el usuario puls.

As, por ejemplo, si quisiramos mostrar un Toast con todos estos datos cada vez que se pulse sobre el mapa
podramos hacer lo siguiente:

1 mapa.setOnMapClickListener(new OnMapClickListener() {

2 public void onMapClick(LatLng point) {

3 Projection proj = mapa.getProjection();

4 Point coord = proj.toScreenLocation(point);

6 Toast.makeText(

7 MainActivity.this,

8 "Click\n" +

9 "Lat: " + point.latitude + "\n" +

10 "Lng: " + point.longitude + "\n" +

11 "X: " + coord.x + " - Y: " + coord.y,

12 Toast.LENGTH_SHORT).show();

13 }

14 });

De forma similar podramos implementar el evento de pulsacin larga, con la nica diferencia de que lo asignaramos
mediante setOnMapLongClickListener() y sobrescribiramos el mtodoonMapLongClick().

1 mapa.setOnMapLongClickListener(new OnMapLongClickListener() {

2 public void onMapLongClick(LatLng point) {

3 Projection proj = mapa.getProjection();

4 Point coord = proj.toScreenLocation(point);


5

6 Toast.makeText(

7 MainActivity.this,

8 "Click Largo\n" +

9 "Lat: " + point.latitude + "\n" +

10 "Lng: " + point.longitude + "\n" +

11 "X: " + coord.x + " - Y: " + coord.y,

12 Toast.LENGTH_SHORT).show();

13 }

14 });

As, cuando el usuario hiciera una pulsacin larga sobre el mapa veramos los datos en pantalla de la siguiente forma:

Tambin podremos capturar el evento de cambio de cmara, de forma que podamos realizar determinadas acciones
cada vez que el usuario se mueve manualmente por el mapa, desplazndolo, haciendo zoom, o modificando la
orientacin o el ngulo de visin. Este evento lo asignaremos al mapa mediante su
mtodo setOnCameraChangeListener() y sobrescribiendo el mtodoonCameraChange(). Este mtodo recibe como
parmetro un objeto CameraPosition, que ya vimos en el artculo anterior, por lo que podremos recuperar de l
todos los datos de la cmara en cualquier momento.

De esta forma, si quisiramos mostrar un Toast con todos los datos podramos hacer lo siguiente:

1 mapa.setOnCameraChangeListener(new OnCameraChangeListener() {

2 public void onCameraChange(CameraPosition position) {

3 Toast.makeText(

4 MainActivity.this,

5 "Cambio Cmara\n" +

6 "Lat: " + position.target.latitude + "\n" +

7 "Lng: " + position.target.longitude + "\n" +


8 "Zoom: " + position.zoom + "\n" +

9 "Orientacin: " + position.bearing + "\n" +

10 "ngulo: " + position.tilt,

11 Toast.LENGTH_SHORT).show();

12 }

13 });

Hecho esto, cada vez que el usuario se mueva por el mapa veramos lo siguiente:

El siguiente tema importante que quera tratar en este artculo es el de los marcadores. Rara es la aplicacin Android
que hace uso de mapas sin utilizar tambin este tipo de elementos para resaltar determinados puntos en el mapa. Si
recordamos el artculo sobre la API v1, vimos cmo podamos aadir marcadores aadiendo una nueva capa
(overlay) al mapa y dibujando nuestro marcador como parte de su evento draw(). En la nueva versin de la API
tendemos toda esta funcionalidad integrada en la propia vista de mapa, y agregar un marcador resulta tan sencillo
como llamar al mtodoaddMarker() pasndole la posicin en forma de objeto LatLng y el texto a mostrar en la
ventana de informacin del marcador. En nuestra aplicacin de ejemplo aadiremos un men de forma que cuando
lo pulsemos se aada automticamente un marcador sobre Espaa con el texto Pais: Espaa. Veamos cmo
escribir un mtodo auxiliar que nos ayuda a hacer esto pasndole las coordenadas de latitud y longitud:

1 private void mostrarMarcador(double lat, double lng)

2 {

3 mapa.addMarker(new MarkerOptions()

4 .position(new LatLng(lat, lng))

5 .title("Pais: Espaa"));

6 }

As de sencillo, basta con llamar al mtodo addMarker() pasando como parmetro un nuevo
objetoMarkerOptions sobre el que establecemos la posicin del marcador (mtodo position()) y el texto a incluir en
la ventana de informacin del marcador (mtodos title() para el ttulo y snippet() para el resto del texto). Si
ejecutamos la aplicacin de ejemplo y pulsamos el men Marcadores aparecer el siguiente marcador sobre el
mapa (la ventana de informacin aparece si adems se pulsa sobre el marcador):
Adems de facilitarnos la vida con la inclusin de marcadores en el mapa tambin tendremos ayuda a la hora de
capturar el evento de pulsacin sobre un marcador, ya que podremos asignar al mapa dicho evento como cualquiera
de los comentados anteriormente. En este caso el evento se asignar al mapa mediante el
mtodo setOnMarkerClickListener() y sobrescribiremos el mtodoonMarkerClick(). Dicho mtodo recibe como
parmetro el objeto Marker pulsado, de forma que podamos identificarlo accediendo a su informacin (posicin,
ttulo, texto, ). Veamos un ejemplo donde mostramos un toast con el ttulo del marcador pulsado:

1 mapa.setOnMarkerClickListener(new OnMarkerClickListener() {

2 public boolean onMarkerClick(Marker marker) {

3 Toast.makeText(

4 MainActivity.this,

5 "Marcador pulsado:\n" +

6 marker.getTitle(),

7 Toast.LENGTH_SHORT).show();

9 return false;

10 }

11 });

Con el cdigo anterior, si pulsamos sobre el marcador de Espaa aparecer el siguiente mensaje informativo:
Todo lo explicado sobre marcadores corresponde al comportamiento por defecto de la API, sin embargo tambin es
posible por supuesto personalizar determinadas cosas, como por ejemplo el aspecto de los marcadores. Esto se sale
un poco de este artculo, donde pretenda describir los temas ms bsicos, pero para quien est interesado tan slo
decir que mediante los mtodos icon() y anchor() del objeto MakerOptions que hemos visto antes es posible utilizar
una imagen personalizada para mostrar como marcador en el mapa. En la documentacin oficial (en ingls) podis
encontrar un ejemplo de cmo hacer esto.

Como ltimo tema, vamos a ver cmo dibujar lneas y polgonos sobre el mapa, elementos muy comunmente
utilizados para trazar rutas o delimitar zonas del mapa. Para realizar esto en la versin 2 de la API vamos a actuar una
vez ms directamente sobre la vista de mapa, sin necesidad de aadiroverlays o similares, y ayudndonos de los
objetos PolylineOptions y PolygonOptionsrespectivamente.

Para dibujar una linea lo primero que tendremos que hacer ser crear un nuevo objetoPolylineOptions sobre el que
aadiremos utilizando su mtodo add() las coordenadas (latitud-longitud) de todos los puntos que conformen la
linea. Tras esto estableceremos el grosor y color de la linea llamando a los
mtodos width() y color() respectivamente, y por ltimo aadiremos la linea al mapa mediante su
mtodo addPolyline() pasndole el objeto PolylineOptions recin creado.

En nuestra aplicacin de ejemplo he aadido un nuevo men para dibujar un rectngulo sobre Espaa. Veamos
cmo queda:

1 private void mostrarLineas()

2 {

3 //Dibujo con Lineas

5 PolylineOptions lineas = new PolylineOptions()

6 .add(new LatLng(45.0, -12.0))

7 .add(new LatLng(45.0, 5.0))

8 .add(new LatLng(34.5, 5.0))

9 .add(new LatLng(34.5, -12.0))

10 .add(new LatLng(45.0, -12.0));

11
12 lineas.width(8);

13 lineas.color(Color.RED);

14

15 mapa.addPolyline(lineas);

16 }

Ejecutando esta accin en el emulador veramos lo siguiente:

Pues bien, esto mismo podramos haberlo logrado mediante el dibujo de polgonos, cuyo funcionamiento es muy
similar. Para ello crearamos un nuevo objeto PolygonOptions y aadiremos las coordenadas de sus puntos en el
sentido de las agujas del reloj. En este caso no es necesario cerrar el circuito (es decir, que la primera coordenada y
la ltima fueran iguales) ya que se hace de forma automtica. Otra diferencia es que para polgonos el ancho y color
de la linea los estableceramos mediante los mtodos strokeWidth() y strokeColor(). Adems, el dibujo final del
polgono sobre el mapa lo haramos mediante addPolygon(). En nuestro caso quedara como sigue:

1 //Dibujo con polgonos

3 PolygonOptions rectangulo = new PolygonOptions()

4 .add(new LatLng(45.0, -12.0),

5 new LatLng(45.0, 5.0),

6 new LatLng(34.5, 5.0),

7 new LatLng(34.5, -12.0),

8 new LatLng(45.0, -12.0));

10 rectangulo.strokeWidth(8);

11 rectangulo.strokeColor(Color.RED);

12

13 mapa.addPolygon(rectangulo);

El resultado al ejecutar la accin en el emulador debera ser exactamente igual que el anterior.
Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pagina del curso en GitHub.

Y con esto habramos concluido la serie de tres artculos destinados a describir el funcionamiento bsico de la nueva
API v2 de Google Maps para Android. Espero haber aclarado las dudas principales a la hora de comenzar a utilizar la
nueva API. Tampoco descarto algn artculo adicional para comentar temas algo ms avanzados sobre esta API, pero
eso ser ms adelante.
Google Play Services: Notificaciones Push Android
Notificaciones Push Android: Google Cloud Messaging (GCM). Introduccin
by Sgoliver on 04/07/2012 in Android, Programacin

Los prximos artculos del Curso de Programacin Android los vamos a dedicar a describir qu es y cmo utilizar el
servicio Google Cloud Messaging. En este primer artculo haremos una introduccin al servicio y comentaremos la
forma de registrarnos para poder utilizarlo (no preocuparos, es gratuito), y en los dos siguientes veremos cmo
implementar las aplicaciones servidor (una vez ms en ASP.NET) y cliente (en Android). Empecemos.

En algunas ocasiones, nuestras aplicaciones mviles necesitan contar con la capacidad de notificar al usuario
determinados eventos que ocurren fuera del dispositivo, normalmente en una aplicacin web o servicio online
alojado en un servidor externo. Como ejemplo de esto podramos pensar en las notificaciones que nos muestra
nuestro mvil cuando se recibe un nuevo correo electrnico en nuestro servidor de correo habitual.

Para conseguir esto se nos podran ocurrir varias soluciones, por ejemplo mantener abierta una conexin
permanente con el servidor de forma que ste le pudiera comunicar inmediatamente cualquier nuevo evento a
nuestra aplicacin. Esta tcnica, aunque es viable y efectiva, requiere de muchos recursos abiertos constantemente
en nuestro dispositivo, aumentando por tanto el consumo de CPU, memoria y datos de red necesarios para ejecutar
la aplicacin. Otra solucin utilizada habitualmente sera hacer que nuestra aplicacin mvil revise de forma
peridica en el servidor si existe alguna novedad que notificar al usuario. Esto se denomina polling, y requiere
muchos menos recursos que la opcin anterior, pero tiene un inconveniente que puede ser importante segn el
objetivo de nuestra aplicacin: cualquier evento que se produzca en el servidor no se notificar al usuario hasta la
prxima consulta al servidor que haga la aplicacin cliente, que podra ser mucho tiempo despus.

Para solventar este problema Google introdujo en Android, a partir de la versin 2.2 (Froyo), la posibilidad de
implementar notificaciones de tipo push, lo que significa que es el servidor el que inicia el proceso de notificacin,
pudiendo realizarla en el mismo momento que se produce el evento, y el cliente se limita a esperar los mensaje sin
tener que estar periodicamente consultando al servidor para ver si existen novedades, y sin tener que mantener una
conexin permanentemente abierta con ste. En la arquitectura de Google, todo esto se consigue introduciendo un
nuevo actor en el proceso, un servidor de mensajera push o cloud to device (que se traducira en algo as como
mensajes de la nube al dispositivo), que se situara entre la aplicacin web y la aplicacin mvil. Este servidor
intermedio se encargar de recibir las notificaciones enviadas desde las aplicaciones web y hacerlas llegar a las
aplicaciones mviles instaladas en los dispositivos correspondientes. Para ello, deber conocer la existencia de
ambas aplicaciones, lo que se consigue mediante un protocolo bien definido de registros y autorizaciones entre los
distintos actores que participan en el proceso. Veremos ms adelante cmo implementar este proceso.

Este servicio de Google recibi en sus comienzos las siglas C2DM (Cloud to Device Messaging), pero coincidiendo con
su salida de fase beta modific su nombre a GCM (Google Cloud Messaging).

Lo primero que debemos comentar es la forma de darnos de alta en el servicio, que a pesar de ser gratuito requiere
de un proceso previo de registro y la generacin de una ApiKey, algo similar a lo que ya vimos al hablar de la
utilizacin de mapas en Android. Para hacer esto debemos acceder a la consola de APIs de Google, en siguiente
direccin:

https://code.google.com/apis/console

Suponiendo que es la primera vez que accedemos a esta consola, nos aparecer la pantalla de bienvenida y la opcin
de crear un nuevo proyecto.
Google API Console Create Project

Una vez creado el proyecto el navegador te redirige a una direccin similar a sta:

Google API Console URL Proyecto

Al nmero que aparece tras la etiqueta #project: lo llamaremos Sender ID y debemos anotarlo ya que nos har
falta ms adelante como identificador nico de la aplicacin web emisora de nuestros mensajes.

Una vez creado el nuevo proyecto vamos a activar el servicio GCM accediendo al men Services y pulsando el
botn ON/OFF asociado al servicio llamado Google Cloud Messaging for Android.

Google API Console Services

Se nos puede presentar entonces una ventana donde tendremos que aceptar las condiciones del servicio y tras sto
el servicio quedar activado, aunque an nos faltar un ltimo paso para poder utilizarlo. Como en el caso de la
utilizacin de la api de Google Maps, para hacer uso del servicio GCM tendremos que generar una ApiKey que nos
sirva posteriormente como identificacin de acceso. Para ello accedemos al men Api Access y pulsaremos sobre
el botn Create new Server Key.

Google API Console New Server Key


Nos aparecer un dilogo llamado Configure Server Key for, que aceptaremos sin ms pulsando el botn
Create, sin necesidad de rellenar nada.

Google API Console Configure Server Key

Y ya tendramos creada nuestra API Key, que aparecer en la seccin Simple API Access con el ttulo Key for server
apps (with IP locking).

Google API Console API Key

Con esto ya nos habramos registrado correctamente en el servicio GCM y habramos generado nuestra API Key para
identificarnos, con lo que estaramos en disposicin de construir nuestras aplicaciones cliente y servidor, algo que
veremos en los dos prximos artculos. En ste nos pararemos un poco ms para hacer una descripcin a grandes
rasgos de la arquitectura que tendr nuestro sistema y de cmo fluir la informacin entre cada uno de los servicios
y aplicaciones implicados.

Como hemos indicado antes, para asegurar la correcta comunicacin entre los tres sistemas se hace necesario un
protocolo de registros y autorizaciones que proporcione seguridad y calidad a todo el proceso. Este proceso se
resume en el siguiente grfico (click para ampliar), que intentar explicar a continuacin.

Proceso General GCM

Comentemos brevemente cada uno de los pasos indicados en el diagrama.

El primer paso, aunque no aparece en el grfico, sera la autenticacin de nuestra aplicacin web en el servicio GCM.
En la anterior versin del servicio GCM (llamada C2DM) esto deba hacerse mediante la utilizacin de otra API de
Google llamada ClientLogin, o a travs del protocolo OAuth 2.0, ambas dirigidas a obtener un token de autorizacin
que deba enviarse posteriormente en el resto de llamadas al servicio. Sin embargo, con la llegada de Google Cloud
Messaging, esto se ha simplificado mediante la obtencin y uso de una API Key, que es precisamente lo que hemos
comentado unos prrafos ms arriba. Como pasaba con el token de autorizacin, nuestra nueva API Key deber
acompaar a cada una de las llamadas que hagamos al servicio GCM desde nuestra aplicacin web.

Los siguientes pasado, ya s mostrados en el diagrama, seran los siguientes:


1. El siguiente paso es el equivalente al ya comentado para el servidor pero esta vez desde el punto de vista de
la aplicacin cliente. La aplicacin Android debe registrarse en los servidores GCM como cliente capaz de
recibir mensajes desde dicho servicio. Para esto es necesario que el dispositivo/emulador cumplan una serie
de requisitos:

1. Disponer de Android 2.2 o superior.

2. Tener configurada una cuenta de Google en el dispositivo o emulador. Configurable desde Ajustes / Cuentas
y sincronizacin.

3. Si se trata de un dispositivo real debe estar instalada la Google Play Store. Por el contrario si estamos
ejecutando la aplicacin desde el emulador bastar con usar un target que incluya las APIs de Google (para la nueva
versin de GCM incluida con los Google Play Services se requiere Android 4.2.2 o superior para poder probar en el
emulador).

2. Si el registro se finaliza correctamente se recibir un cdigo de registro (Registration ID) que la aplicacin
cliente deber conservar. Adems, la aplicacin Android deber estar preparada para recibir peridicamente
refrescos de este cdigo de registro, ya que es posible que el servidor GCM invalide peridicamente este ID,
genere uno nuevo y lo vuelva a notificar a la aplicacin cliente.

3. Este nuevo paso consiste en enviar, desde la aplicacin cliente a la aplicacin servidor, el cdigo de registro
GCM recibido, el cual har las veces de identificador nico del cliente en el servidor de forma que ste pueda
indicar ms tarde el dispositivo mvil concreto al que desea enviar un mensaje. La aplicacin servidora
tendr que ser capaz por tanto de almacenar y mantener todos los ID de registro de los distintos dispositivos
mviles que se registren como clientes capaces de recibir mensajes.

4. El ltimo paso ser obviamente el envo en s de un mensaje desde el servidor hasta un cliente determinado,
algo que se har a travs del servidor GCM (paso 4.1) y desde ste se dirigir al cliente concreto que debe
recibirlo (paso 4.2).

Como se puede comprobar, el procedimiento es relativamente complejo, aunque bastante intuitivo. En los prximos
artculos veremos cmo implementar cada uno de ellos. Una vez ms, para nuestro ejemplo utilizaremos una
aplicacin ASP.NET como aplicacin servidora, con SQL Server a modo de almacn de datos, y aprovechando que ya
hemos visto como crear servicios web SOAP y llamarlos desde aplicaciones Android, vamos a utilizar uno de stos
para la comunicacin entre cliente y servidor (paso 3).
Notificaciones Push Android: Google Cloud Messaging (GCM). Implementacin Servidor

by Sgoliver on 04/07/2012 in Android, Programacin

En el artculo anterior del curso hicimos una introduccin al servicio Google Cloud Messaging (GCM), vimos
cmo registrarnos y obtener la API Key necesaria para enviar mensajes y describimos a alto nivel la
arquitectura que tendr un sistema capaz de gestionar mensajera de tipo push a travs de este servicio de
Google. Este segundo artculo lo vamos a dedicar a la implementacin de una aplicacin web capaz de enviar
mensajes o notificaciones push a dispositivos Android. En el prximo artculo veremos cmo desarrollar la
aplicacin Android cliente capaz de recibir estos mensajes.
Como ya viene siendo habitual en el curso, el sistema elegido para desarrollar la aplicacin web ser ASP.NET,
utilizando C# como lenguaje, y SQL Server como base de datos.

Como ya comentamos en el artculo anterior, la aplicacin web ser responsable de las siguientes tareas:

1. Almacenar y mantener el listado de dispositivos cliente que podrn recibir mensajes.


2. Enviar los mensajes a los clientes a travs del servicio GCM de Google.
En cuanto al punto 1, la aplicacin deber ser capaz de recibir los datos de registro de cada cliente que se d
de alta para recibir mensajes y almacenarlos en la base de datos. Esto lo haremos mediante la creacin de un
servicio web que exponga un mtodo capaz de recoger y almacenar los datos de registro de un cliente. La
aplicacin Android se conectar directamente a este servicio web y llamar al mtodo con sus datos
identificativos para registrarse como cliente capaz de recibir notificaciones. Por supuesto que para esto se
podra utilizar cualquier otro mecanismo distinto a servicios web, por ejemplo una simple peticin HTTP al
servidor pasando los datos como parmetros, pero no nos vendr mal para seguir practicando con servicios
web en android, que en este caso ser de tipo SOAP.

Por su lado, el punto 2 lo resolveremos a modo de ejemplo con una pgina web sencilla en la que podamos
indicar el nombre de usuario de cualquiera de los dispositivos registrados en la base de datos y enviar un
mensaje de prueba a dicho cliente.

Vamos a empezar creando la base de datos, aunque no nos detendremos mucho porque ya vimos el
procedimiento por ejemplo en el primer artculo dedicado a servicios web SOAP. Tan slo decir que crearemos
una nueva base de datos llamada DBUSUARIOS, que tendr dos campos: NombreUsuarioy CodigoC2DM, el
primero de ellos destinado a almacenar un nombre de usuario identificativo de cada cliente registrado, y el
segundo para almacenar el RegistrationID de GCM recibido desde dicho cliente a travs del servicio web
(recomiendo consultar el artculo anterior para entender bien todo este protocolo requerido por GCM).
Una vez creada la base de datos vamos a crear en Visual Studio 2010 un nuevo proyecto C# de tipo ASP.NET
Web Application al que llamaremos GCMServer, y aadiremos a este proyecto un nuevo componente de tipo
Web Service llamado ServicioRegistroGCM.asmx. Todo este procedimiento tambin se puede consultar en
el artculo sobre servicios web SOAP en Android.
Aadiremos un slo mtodo web al servicio, al que llamaremos RegistroCliente() y que recibir como
hemos comentado 2 parmetros: el nombre de usuario y el ID de registro del cliente en GCM. El mtodo se
limitar a realizar el INSERT o UPDATE correspondiente con estos dos datos en la base de datos que hemos
creado de usuarios.
1
[WebMethod]
2
public int RegistroCliente(string usuario, string regGCM)
3
{
4
SqlConnection con =
5
new SqlConnection(
6 @"Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBUSUARIOS;Integrated
Security=True");
7

8
con.Open();
9

10
string cod = CodigoCliente(usuario);
11

12
int res = 0;
13
string sql = "";
14

15
if (cod == null)
16 sql = "INSERT INTO Usuarios (NombreUsuario, CodigoC2DM) VALUES (@usuario, @codi
17 else

18 sql = "UPDATE Usuarios SET CodigoC2DM = @codigo WHERE NombreUsuario = @usuario"

19

20 SqlCommand cmd = new SqlCommand(sql, con);

21
cmd.Parameters.Add("@usuario", System.Data.SqlDbType.NVarChar).Value = usuario;
22
cmd.Parameters.Add("@codigo", System.Data.SqlDbType.NVarChar).Value = regGCM;
23

24
res = cmd.ExecuteNonQuery();
25

26
con.Close();
27

28
return res;
29 }
30

El cdigo es sencillo, pero por qu es necesario considerar el caso del UPDATE? Como ya advertimos en el
artculo anterior, el servidor GCM puede en ocasiones refrescar (actualizar) el ID de registro de un cliente
comunicndoselo de nuevo a ste, por lo que a su vez la aplicacin cliente tendr que hacer tambin la misma
actualizacin contra la aplicacin web. Para ello, el cliente simplemente volver a llamar al
mtodo RegistroCliente() del servicio web pasando el mismo nombre de usuario pero con el ID de registro
actualizado. Para saber si el cliente est ya registrado o no el mtodo se apoya en un mtodo auxiliar
llamado CodigoCliente() que realiza una bsqueda de un nombre de usuario en la base de datos para
devolver su ID de registro en caso de encontrarlo. El cdigo de este mtodo es igual de sencillo que el anterior:
1
public string CodigoCliente(string usuario)
2 {
3 SqlConnection con =

4 new SqlConnection(

5 @"Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBUSUARIOS;Integrated


Security=True");
6

7
con.Open();
8

9 string sql = "SELECT CodigoC2DM FROM Usuarios WHERE NombreUsuario = @usuario";


10

11 SqlCommand cmd = new SqlCommand(sql, con);


12

13 cmd.Parameters.Add("@usuario", System.Data.SqlDbType.NVarChar).Value = usuario;

14

15 string cod = (String)cmd.ExecuteScalar();

16

17 con.Close();

18
return cod;
19
}
20

Con esto ya tendramos implementado nuestro servicio web para el registro de clientes.

Para el envo de los mensajes utilizaremos directamente la pgina Default.aspx creada por defecto al generar
el proyecto de Visual Studio. Modificaremos esta pgina para aadir tan slo un cuadro de texto donde
podamos introducir el nombre de usuario asociado al cliente al que queremos enviar un mensaje, un botn
Enviar con el realizar el envo, y una etiqueta donde mostrar el estado del envo. Quedara algo como lo
siguiente:

Web Envo GCM


El botn de envo, realizar una bsqueda en la base de datos del nombre de usuario introducido, recuperar
su Registration ID y enviar un mensaje de prueba con la fecha-hora actual.

1
protected void Button3_Click(object sender, EventArgs e)
2
{
3 ServicioRegistroGCM svc = new ServicioRegistroGCM();
4

5 string codUsuario = svc.CodigoCliente(TxtUsuario.Text);


6

7 bool res = enviarMensajePrueba(codUsuario);

9 if (res == true)

10 LblResultadoMensaje.Text = "Envo OK";

else
11
LblResultadoMensaje.Text = "Envo NOK";
12
}
13

Como vemos en el cdigo, toda la lgica de envo de mensajes la he encapsulado en el mtodo


auxiliar enviarMensajePrueba() para poder centrarme ahora en ella. En este mtodo es donde vamos a
hacer realmente uso de la API del servicio de Google Cloud Messaging, y por ello antes de ver la
implementacin vamos a hablar primero de las distintas opciones de esta API.
Todas las llamadas a la API de GCM para enviar mensajes se realizan mediante peticiones HTTP POST a la
siguiente direccin:

https://android.googleapis.com/gcm/send

La cabecera de esta peticin debe contener dos datos esenciales. Por un lado debemos indicar la API Key que
generamos en el primer artculo (atributo Authorization), y por otro lado el formato del contenido (en este
caso, los parmetros de la API) que vamos a incluir con la peticin (atributo Content-Type). GCM permite
formatear los datos como JSON (para lo que habra que indicar el valor application/json) o como texto
plano (para lo que debemos utilizar el valor application/x-www-form-urlencoded). En nuestro caso de
ejemplo utilizaremos la segunda opcin.
Dado que hemos elegido la opcin de texto plano, los distintos datos del contenido se formatearn como
parmetros HTTP con el formato tradicional, es decir, tendremos que construir una cadena de la forma
param1=valor1&param2=valor2&.
Entre los distintos datos que podemos incluir hay tan solo uno obligatorio, llamado registration_id, que
debe contener el ID de registro del cliente al que se le va a enviar el mensaje. A parte de ste tambin podemos
incluir los siguientes parmetros opcionales:
delay_while_idle. Hace que el servidor de GCM no enve el mensaje al dispositivo mientras ste no se encuentre
activo.
time_to_live. Indica el tiempo mximo que el mensaje puede permanecer en el servidor de GCM sin entregar
mientras el dispositivo est offline. Por defecto 4 semanas. Si se especifica algn valor tambin habr que incluir el
parmetro siguiente, collapse_key.
collapse_key. ste lo explicar con un ejemplo. Imaginad que activamos el parmetrodelay_while_idle y que el
dispositivo que debe recibir el mensaje permanece inactivo varias horas. Si durante esas horas se generaran varias
notificaciones hacia el dispositivo, estos mensajes se iran acumulando en el servidor de GCM y cuando el dispositivo se
activara le llegaran todos de golpe. Esto puede tener sentido si cada mensaje contiene informacin distinta y relevante,
pero y si los mensajes simplemente fueran por ejemplo para decirle al dispositivo Tienes correo nuevo? Sera absurdo
entregar en el varias notificaciones de este tipo en el mismo instante. Pues bien, para esto se utiliza el
parmetro collapse_key. A este parmetro podemos asignarle como valor cualquier cadena de caracteres, de forma
que si se acumulan en el servidor de GCM varios mensajes para el mismo dispositivo y con la misma collapse_key, al
dispositivo slo se le entregar el ltimo de ellos cuando ste se active, descartando todos los dems.
data.<nombre_dato>. Se pueden incluir tantos parmetros de este tipo como queramos, para incluir cualquier otra
informacin que queramos en el mensaje. Por ejemplo podramos pasar los datos de un nuevo correo recibido con dos
parmetros como los siguientes: data.emisor=aaa@gmail.com, y data.asunto=pruebagcm. Tan solo
recordad preceder el nombre de los datos con el prefijo data..
Una vez formateada convenientemente la cabecera y contenido de la peticin HTTP, y realizada sta a la
direccin indicada anteriormente, podemos obtener diferentes respuestas dependiendo del resultado de la
peticin. Diferenciaremos los distintos resultados por el cdigo de estado HTTP recibido en la respuesta:

200. El mensaje se ha procesado correctamente, en cuyo caso se devuelve en los datos un parmetro id= con el
cdigo del mensaje generado.
401. Ha fallado la autenticacin de nuestra aplicacin web contra los servidores de GCM. Normalmente significar algn
problema con la API Key utilizada.
500. Se ha producido un error al procesarse el mensaje. En este caso la respuesta incluir en su contenido un parmetro
Error= que indicar el cdigo de error concreto devuelto por GCM.
501. El servidor de GCM no est disponible temporalmente.
Y eso es todo, largo de contar pero sencillo en el fondo. Veamos cmo podemos implementar esto en C#, y
para ello vamos a ver el cdigo del mtodo que dejamos antes pendiente,enviarMensajePrueba(), y justo
despus lo comentamos.
1 private static bool enviarMensajePrueba(String registration_id)

2 {

3 String GCM_URL = @"https://android.googleapis.com/gcm/send";

4
string collapseKey = DateTime.Now.ToString();
5

6
Dictionary data = new Dictionary();
7
data.Add("data.msg",
8
HttpUtility.UrlEncode("Prueba. Timestamp: " + DateTime.Now.ToString()));
9

10
bool flag = false;
11
StringBuilder sb = new StringBuilder();
12

13 sb.AppendFormat("registration_id={0}&collapse_key={1}",
14 registration_id, collapseKey);

15

16 foreach (string item in data.Keys)

17 {
18 if (item.Contains("data."))

19 sb.AppendFormat("&{0}={1}", item, data[item]);

}
20

21
string msg = sb.ToString();
22
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(GCM_URL);
23
req.Method = "POST";
24
req.ContentLength = msg.Length;
25 req.ContentType = "application/x-www-form-urlencoded";
26

27 string apiKey = "AIzaSyCJ7QSQAznAmhDzNTLSUE6uX9aUfr9-9RI";

28 req.Headers.Add("Authorization:key=" + apiKey);

29

30 using (StreamWriter oWriter = new StreamWriter(req.GetRequestStream()))

31 {

oWriter.Write(msg);
32
}
33

34
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
35
{
36
using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
37 {
38 string respData = sr.ReadToEnd();

39

40 if (resp.StatusCode == HttpStatusCode.OK) // OK = 200

41 {

42 if (respData.StartsWith("id="))

flag = true;
43
}
44
else if (resp.StatusCode == HttpStatusCode.InternalServerError) // 500
45
Console.WriteLine("Error interno del servidor, prueba ms tarde.");
46
else if (resp.StatusCode == HttpStatusCode.ServiceUnavailable) // 503
47 Console.WriteLine("Servidor no disponible temporalmente, prueba ms
tarde.");
48
else if (resp.StatusCode == HttpStatusCode.Unauthorized) // 401
49
Console.WriteLine("La API Key utilizada no es vlida.");
50
else
51
52 Console.WriteLine("Error: " + resp.StatusCode);

53 }

}
54

55
return flag;
56
}
57

58

59

60

Como vemos el mtodo recibe directamente como parmetro el Registration ID del cliente al que se va a enviar
el mensaje. En primer lugar configuro todos los parmetros que pasar en la llamada a la API, que en este caso
de ejemplo tan slo sern, adems del registration_id ya comentado, elcolapse_key, y una dato
adicional que llamar data.msg (recordemos el prefijo data. obligatorio para este tipo de datos adicionales)
con un mensaje de prueba que contenga la fecha/hora actual. Toda la cadena con estos parmetros la
construyo utilizando un objeto StringBuilder. Lo nico reseable hasta ahora sera la forma de aadir el
parmetro adicional data.msg, que lo hago mediante la creacin de un objeto Dictionary y su
mtodo add() para aadir el dato, para poco despus generar la cadena final recorriendo este diccionario en
un bucle foreach. En este caso no sera necesaria toda esta parafernalia dado que slo vamos a aadir un
dato adicional, pero lo he dejado as para que tengis un ejemplo de patrn mediante el cual podeis aadir
ms de un dato adicional de una forma sencilla y organizada.
Una vez creada la cadena de parmetros y datos que incluiremos como contenido de la peticin creamos dicha
peticin como un objeto HttpWebRequest indicando la URL del servicio. Indicamos que la peticin ser de tipo
POST asignando la propiedad Method, y configuramos la cabecera con los dos datos que ya hemos comentado
antes en el artculo (Authorization y Content-Type). El primero de ellos al ser personalizado debemos
aadirlo utilizando el mtodo Add() de la coleccin Headersde la peticin. En cambio para el segundo existe
una propiedad del objeto HttpWebRequest con la que podemos establecerlo directamente,
llamada ContentType. Hecho esto, tan slo nos queda aadir el contenido a la peticin, lo que conseguimos
obteniendo el stream de escritura de la peticin medianteGetRequestStream() y escribiendo en l nuestra
cadena de parmetros mediante el mtodoWrite().
Seguidamente vamos a ejecutar la peticin y a obtener la respuesta como objeto HttpWebResponsemediante
una llamada a GetResponse(). Por ltimo, obtenemos el cdigo de estado HTTP de la respuesta mediante la
consulta a su propiedad StatusCode, y los datos asociados obteniendo elstream de lectura de la respuesta
mediante GetResponseStream() y el mtodo ReadToEnd() para leer todo el contenido. Evaluando estos
dos datos determinamos fcilmente el resultado del envo segn la informacin ya comentada antes en el
artculo.
Y con esto habramos terminado la implementacin del servidor. Haremos las pruebas pertinentes y mostrar el
resultado cuando implementemos la aplicacin Android cliente en el prximo artculo.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a
la pgina del curso en GitHub.
Actualizacin: Existe una librera .NET/Mono llamada PushSharp capaz de facilitarnos la vida a la hora de
enviar notificaciones push a dispositivos Android (y tambin iOS, Windows Phone y Blackberry).
Notificaciones Push Android: Google Cloud Messaging (GCM). Implementacin Cliente (Nueva
Versin)
by Sgoliver on 18/08/2013 in Android, Programacin

En los apartados anteriores del curso hemos hablado sobre el servicio Google Cloud Messaging y hemos visto cmo
implementar una aplicacin web que haga uso de dicho servicio para enviar mensajes a dispositivos Android. Para
cerrar el crculo, en este nuevo apartado nos centraremos en la aplicacin Android cliente.

Esta aplicacin cliente, como ya hemos comentado en alguna ocasin ser responsable de:

1. Registrarse contra los servidores de GCM como cliente capaz de recibir mensajes.

2. Almacenar el Registration ID recibido como resultado del registro anterior.

3. Comunicar a la aplicacin web el Registration ID de forma que sta pueda enviarle mensajes.

4. Recibir y procesar los mensajes desde el servidor de GCM.

En la versin anterior de GCM, las tareas 1 y 4 se realizaban normalmente utilizando como ayuda una librera
adicional (gcm.jar) proporcionada por Google. Sin embargo, en la nueva versin de GCM incluida como parte de
los Google Play Services cambian un poco la filosofa de trabajo y esta librera ya no es necesaria.

Por su parte, el punto 2 lo resolveremos fcilmente mediante el uso de SharedPreferences. Y por ltimo el punto 3 lo
implementaremos mediante la conexin al servicio web SOAP que creamos en elapartado anterior, sirvindonos
para ello de la librera ksoap2, tal como ya describimos en el captulo sobre servicios web SOAP en Android.

Durante el captulo construiremos una aplicacin de ejemplo muy sencilla, en la que el usuario podr introducir un
nombre de usuario identificativo y pulsar un botn para que quede guardado en las preferencias de la aplicacin.
Tras esto podr registrarse como cliente capaz de recibir mensajes desde GCM pulsando un botn llamado
Registrar. En caso de realizarse de forma correcta este registro la aplicacin enviar automticamente el
Registration ID recibido y el nombre de usuario almacenado a la aplicacin servidor a travs del servicio
web. Obviamente todo este proceso de registro debera hacerse de forma transparente para el usuario de una
aplicacin real, en esta ocasin he colocado un botn para ello slo por motivos didcticos y para poder hacer una
prueba ms controlada.

Como en el caso de cualquier otro servicio incluido en los Google Play Services el primer paso para crear nuestra
aplicacin Android ser importar el proyecto de librera de los servicios, crear nuestro propio proyecto y finalmente
hacer referencia a la librera desde nuestro proyecto. Todo este proceso est explicado en el artculo de introduccin
a los Google Play Services.

El siguiente paso ser configurar nuestro AndroidManifest. Lo primero que revisaremos ser la clusula <usessdk>,
donde como versin mnima del SDK debemos indicar la 8 (Android 2.2) o superior. Con esto nos aseguraremos
de que la aplicacin no se instala en dispositivos con versin de Android anterior, no soportadas por los Google Play
Services.

A continuacin aadiremos los permisos necesarios para ejecutar la aplicacin y utilizar GCM:

1 <uses-permission android:name="android.permission.INTERNET" />


2 <uses-permission android:name="android.permission.GET_ACCOUNTS" />

3 <uses-permission android:name="android.permission.WAKE_LOCK" />

4 <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

6 <permission android:name="net.sgoliver.android.newgcm.permission.C2D_MESSAGE"

7 android:protectionLevel="signature" />

8 <uses-permission android:name="net.sgoliver.android.newgcm.permission.C2D_MESSAGE" />

El primero (INTERNET) nos dar acceso a internet en la aplicacin, el segundo (GET_ACCOUNTS) es necesario porque
GCM requiere una cuenta de Google configurada en el dispositivo, el tercero (WAKE_LOCK) ser necesario para
utilizar un determinado tipo de broadcast receiver que comentaremos ms adelante, el cuarto (RECEIVE) es el que
permitir que la aplicacin se registre y reciba mensajes de GCM. Los dos ltimos aseguran que slo nosotros
podremos recibir los mensajes de nuestra aplicacin (sustituir mi paquete java net.sgoliver.android.newgcm por el
vuestro propio en estas dos lineas).

Por ltimo, como componentes de la aplicacin, adems de la actividad principal ya aadida por defecto, deberemos
declarar un broadcast receiver, que llamaremos GCMBroadcastReceiver (tenis que modificar el
elemento <category> con vuestro paquete java), y un servicio que llamaremosGCMIntentService. Ms adelante
veremos cul ser el cometido de cada uno de estos componentes.

1 <application

2 android:allowBackup="true"

3 android:icon="@drawable/ic_launcher"

4 android:label="@string/app_name"

5 android:theme="@style/AppTheme" >

7 ...

9 <receiver

10 android:name=".GCMBroadcastReceiver"

11 android:permission="com.google.android.c2dm.permission.SEND" >

12 <intent-filter>

13 <action android:name="com.google.android.c2dm.intent.RECEIVE" />

14 <category android:name="net.sgoliver.android.newgcm" />

15 </intent-filter>

16 </receiver>

17

18 <service android:name=".GCMIntentService" />

19

20 </application>
Una vez definido nuestro AndroidManifest con todos los elementos necesarios vamos a empezar a implementar la
funcionalidad de nuestra aplicacin de ejemplo. Empezaremos por el proceso de registro que se desencadena al
pulsar el botn Registrar de la aplicacin tras introducir un nombre de usuario.

Nuestro botn de registro tendr que realizar las siguientes acciones:

1. Verificar que el dispositivo tiene instalado Google Play Services.

2. Revisar si ya tenemos almacenado el cdigo de registro de GCM (registration id) de una ejecucin anterior.

3. Si no disponemos ya del cdigo de registro realizamos un nuevo registro de la aplicacin y guardamos los
datos.

El cdigo del botn con estos tres pasos, que iremos comentando por partes, sera el siguiente:

1 btnRegistrar.setOnClickListener(new OnClickListener() {

3 @Override

4 public void onClick(View v)

5 {

6 context = getApplicationContext();

8 //Chequemos si est instalado Google Play Services

9 //if(checkPlayServices())

10 //{

11 gcm = GoogleCloudMessaging.getInstance(MainActivity.this);

12

13 //Obtenemos el Registration ID guardado

14 regid = getRegistrationId(context);

15

16 //Si no disponemos de Registration ID comenzamos el registro

17 if (regid.equals("")) {

18 TareaRegistroGCM tarea = new TareaRegistroGCM();

19 tarea.execute(txtUsuario.getText().toString());

20 }

21 //}

22 //else

23 //{

24 // Log.i(TAG, "No se ha encontrado Google Play Services.");

25 //}

26 }
27 });

El chequeo de si estn instalados los Google Play Services en el dispositivo no se comporta demasiado bien al
ejecutar la aplicacin sobre el emulador (dependiendo de la versin de Android utilizada) por lo que he decidido
mantenerlo comentado para este ejemplo, pero en una aplicacin real s debera realizarse. Adems, tambin
debera incluirse en el evento onResume() de la actividad:

1 @Override

2 protected void onResume()

3 {

4 super.onResume();

6 // checkPlayServices();

7 }

En cuanto a la lgica para hacer el chequeo podremos ayudarnos de la clase GooglePlayServicesUtil, que dispone del
mtodo isGooglePlayServicesAvailable()para hacer la verificacin. En caso de no estar disponibles (si el mtodo
devuelve un valor distinto aSUCCESS) an podemos mostrar un dilogo de advertencia al usuario dando la
posibilidad de instalarlos. Esto lo haremos llamando al mtodo getErrorDialog() de la misma
clase GooglePlayServicesUtil. Quedara algo as:

1 private boolean checkPlayServices() {

2 int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

3 if (resultCode != ConnectionResult.SUCCESS)

4 {

5 if (GooglePlayServicesUtil.isUserRecoverableError(resultCode))

6 {

7 GooglePlayServicesUtil.getErrorDialog(resultCode, this,

8 PLAY_SERVICES_RESOLUTION_REQUEST).show();

9 }

10 else

11 {

12 Log.i(TAG, "Dispositivo no soportado.");

13 finish();

14 }

15 return false;

16 }

17 return true;

18 }

Si Google Play Services est instalado en el dispositivo el siguiente paso ser comprobar si ya tenemos guardado los
datos de registro de una ejecucin anterior, en cuyo caso no habr que volver a hacer el registro (salvo en contadas
ocasiones que comentaremos ahora). Esta comprobacin la heremos dentro de un mtodo
llamado getRegistrationId(), que entre otras cosas har uso de preferencias compartidas (Shared Preferences) para
recuperar los datos guardados. Nuestra aplicacin guardar 4 preferencias, que definiremos en nuestra actividad
como constantes:

1 private static final String PROPERTY_REG_ID = "registration_id";

2 private static final String PROPERTY_APP_VERSION = "appVersion";

3 private static final String PROPERTY_EXPIRATION_TIME = "onServerExpirationTimeMs";

4 private static final String PROPERTY_USER = "user";

La primera de ellas es el cdigo de registro de GCM, la segunda guardar la versin de la aplicacin para la que se ha
obtenido dicho cdigo, la tercera indicar la fecha de caducidad del cdigo de registro guardado, y por ltimo
guardaremos el nombre de usuario.

En el mtodo getRegistrationId() lo primero que haremos ser recuperar la preferenciaPROPERTY_REG_ID. Si sta no


est informada saldremos inmediatamente del mtodo para proceder a un nuevo registro.

Si por el contrario ya tenamos un registration_id guardado podramos seguir utilizndolo sin tener que registrarnos
de nuevo (lo devolveremos como resultado), pero habr tres situaciones en las que queremos volver a realizar el
registro para asegurarnos de que nuestra aplicacin pueda seguir recibiendo mensajes sin ningn problema:

Si el nombre de usuario ha cambiado.

Si la versin de la aplicacin ha cambiado.

Si se ha sobrepasado la fecha de caducidad del cdigo de registro.

Para verificar esto nuestro mtodo recuperar cada una de las preferencias compartidas, realizar las verificaciones
indicadas y en caso de cumplirse alguna de ellas saldr del mtodo sin devolver el antiguo registration_id para que
se vuelva a realizar el registro.

1 private String getRegistrationId(Context context)

2 {

3 SharedPreferences prefs = getSharedPreferences(

4 MainActivity.class.getSimpleName(),

5 Context.MODE_PRIVATE);

7 String registrationId = prefs.getString(PROPERTY_REG_ID, "");

9 if (registrationId.length() == 0)

10 {

11 Log.d(TAG, "Registro GCM no encontrado.");

12 return "";

13 }

14

15 String registeredUser =
16 prefs.getString(PROPERTY_USER, "user");

17

18 int registeredVersion =

19 prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);

20

21 long expirationTime =

22 prefs.getLong(PROPERTY_EXPIRATION_TIME, -1);

23

24 SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault());

25 String expirationDate = sdf.format(new Date(expirationTime));

26

27 Log.d(TAG, "Registro GCM encontrado (usuario=" + registeredUser +

28 ", version=" + registeredVersion +

29 ", expira=" + expirationDate + ")");

30

31 int currentVersion = getAppVersion(context);

32

33 if (registeredVersion != currentVersion)

34 {

35 Log.d(TAG, "Nueva versin de la aplicacin.");

36 return "";

37 }

38 else if (System.currentTimeMillis() > expirationTime)

39 {

40 Log.d(TAG, "Registro GCM expirado.");

41 return "";

42 }

43 else if (!txtUsuario.getText().toString().equals(registeredUser))

44 {

45 Log.d(TAG, "Nuevo nombre de usuario.");

46 return "";

47 }

48

49 return registrationId;
50 }

51

52 private static int getAppVersion(Context context)

53 {

54 try

55 {

56 PackageInfo packageInfo = context.getPackageManager()

57 .getPackageInfo(context.getPackageName(), 0);

58

59 return packageInfo.versionCode;

60 }

61 catch (NameNotFoundException e)

62 {

63 throw new RuntimeException("Error al obtener versin: " + e);

64 }

65 }

Como podis observar, para consultar la versin actual de la aplicacin utilizamos un mtodo
auxiliargetAppVersion() que obtiene la versin mediante el Package Manager y su mtodogetPackageInfo().

Bien, pues llegados aqu si el mtodo anterior nos ha devuelto un cdigo de registro (es decir, que ya tenamos uno
guardado) no tendramos que hacer nada ms, significara que ya estamos registrados en GCM y tan slo tenemos
que esperar a recibir mensajes. En caso contrario, tendremos que realizar un nuevo registro, de lo que nos
ocuparemos mediante la tarea asncrona TareaRegistroGCM.

Esta tarea asncrona tendr que realizar tres acciones principales: registrar la aplicacin contra los servidores de
GCM, registrarnos contra nuestro propio servidor al que tendr que enviar entre otras cosas
el registration_id obtenido de GCM, y por ltimo guardar como preferencias compartidas los nuevos datos de
registro.

1 private class TareaRegistroGCM extends AsyncTask<String,Integer,String>

2 {

3 @Override

4 protected String doInBackground(String... params)

5 {

6 String msg = "";

8 try

9 {

10 if (gcm == null)
11 {

12 gcm = GoogleCloudMessaging.getInstance(context);

13 }

14

15 //Nos registramos en los servidores de GCM

16 regid = gcm.register(SENDER_ID);

17

18 Log.d(TAG, "Registrado en GCM: registration_id=" + regid);

19

20 //Nos registramos en nuestro servidor

21 boolean registrado = registroServidor(params[0], regid);

22

23 //Guardamos los datos del registro

24 if(registrado)

25 {

26 setRegistrationId(context, params[0], regid);

27 }

28 }

29 catch (IOException ex)

30 {

31 Log.d(TAG, "Error registro en GCM:" + ex.getMessage());

32 }

33

34 return msg;

35 }

36 }

Lo primero que haremos ser obtener una instancia del servicio de Google Cloud Messaging mediante el
mtodo GoogleCloudMessaging.getInstance(). Obtenido este objeto, el registro en GCM ser tan sencillo como
llamar a su mtodo register() pasndole como parmetro el Sender ID queobtuvimos al crear el proyecto en la
Consola de APIs de Google. Esta llamada nos devolver elregistration_id asignado a nuestra aplicacin.

Tras el registro en GCM debemos tambin registrarnos en nuestro servidor, al que al menos debemos enviarle
nuestro registration_id para que nos pueda enviar mensajes posteriormente. En nuestro caso de ejemplo, adems
del cdigo de registro vamos a enviarle tambin nuestro nombre de usuario. Como ya dijimos este registro lo vamos
a realizar utilizando el servicio web que creamos en el artculo sobre la parte servidor. La llamada al servicio web es
anloga a las que ya explicamos en el artculo sobre servicios web SOAP por lo que no entrar en ms detalles, tan
slo veamos el cdigo.

1 private boolean registroServidor(String usuario, String regId)


2 {

3 boolean reg = false;

5 final String NAMESPACE = "http://sgoliver.net/";

6 final String URL="http://10.0.2.2:1634/ServicioRegistroGCM.asmx";

7 final String METHOD_NAME = "RegistroCliente";

8 final String SOAP_ACTION = "http://sgoliver.net/RegistroCliente";

10 SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

11

12 request.addProperty("usuario", usuario);

13 request.addProperty("regGCM", regId);

14

15 SoapSerializationEnvelope envelope =

16 new SoapSerializationEnvelope(SoapEnvelope.VER11);

17

18 envelope.dotNet = true;

19

20 envelope.setOutputSoapObject(request);

21

22 HttpTransportSE transporte = new HttpTransportSE(URL);

23

24 try

25 {

26 transporte.call(SOAP_ACTION, envelope);

27 SoapPrimitive resultado_xml =(SoapPrimitive)envelope.getResponse();

28 String res = resultado_xml.toString();

29

30 if(res.equals("1"))

31 {

32 Log.d(TAG, "Registrado en mi servidor.");

33 reg = true;

34 }

35 }
36 catch (Exception e)

37 {

38 Log.d(TAG, "Error registro en mi servidor: " + e.getCause() + " || " + e.getMessage());

39 }

40

41 return reg;

42 }

Por ltimo, si todo ha ido bien guardaremos los nuevos datos de registro (usuario, registration_id, version de la
aplicacin y fecha de caducidad) como preferencias compartidas. Lo haremos todo dentro del
mtodo setRegistrationId().

1 private void setRegistrationId(Context context, String user, String regId)

2 {

3 SharedPreferences prefs = getSharedPreferences(

4 MainActivity.class.getSimpleName(),

5 Context.MODE_PRIVATE);

7 int appVersion = getAppVersion(context);

9 SharedPreferences.Editor editor = prefs.edit();

10 editor.putString(PROPERTY_USER, user);

11 editor.putString(PROPERTY_REG_ID, regId);

12 editor.putInt(PROPERTY_APP_VERSION, appVersion);

13 editor.putLong(PROPERTY_EXPIRATION_TIME,

14 System.currentTimeMillis() + EXPIRATION_TIME_MS);

15

16 editor.commit();

17 }

La forma de guardar los datos mediante preferencias compartidas ya la comentamos en detalle en elartculo
dedicado a las Shared Preferences. Lo nico a comentar es la forma de calcular la fecha de caducidad del cdigo de
registro. Vamos a calcular esa fecha por ejemplo como la actual ms una semana. Para ello obtenemos la fecha
actual en milisegundos con currentTimeMillis() y le sumamos una constante EXPIRATION_TIME_MS que hemos
definido con el valor 1000 * 3600 * 24 * 7, es decir, los milisegundos de una semana completa.

Y con esto habramos terminado la fase de registro de la aplicacin. Pero para recibir mensajes an nos faltan dos
elementos importantes. Por un lado tendremos que implementar un Broadcast Receiver que se encargue
de recibir los mensajes, y por otro lado crearemos un nuevo servicio (concretamente unIntent Service) que se
encargue de procesar dichos mensajes. Esto lo hacemos as porque no es recomendable realizar tareas complejas
dentro del propio broadcast receiver, por lo que normalmente utilizaremos este patrn en el que delegamos todo el
trabajo a un servicio, y el broadcast receiver se limitar a llamar a ste.
En esta ocasin vamos a utilizar un nuevo tipo especfico de broadcast receiver,WakefulBroadcastReceiver, que nos
asegura que el dispositivo estar despierto el tiempo que sea necesario para que termine la ejecucin del servicio
que lancemos para procesar los mensajes. Esto es importante, dado que si utilizramos un broadcast receiver
tradicional el dispositivo podra entrar en modo de suspensin (sleep mode) antes de que terminramos de procesar
el mensaje.

Crearemos por tanto una nueva clase que extienda de WakefulBroadcastReceiver, la


llamamosGCMBroadcastReceiver, e implementaremos el evento onReceive() para llamar a nuestro servicio de
procesamiento de mensajes, que recordemos lo llamamos GCMIntentService. La llamada al servicio la realizaremos
mediante el mtodo startWakefulService() que recibir como parmetros el contexto actual, y el mismo intent
recibido sobre el que indicamos el servicio a ejecutar mediante su mtodo setComponent().

1 public class GCMBroadcastReceiver extends WakefulBroadcastReceiver

2 {

3 @Override

4 public void onReceive(Context context, Intent intent)

5 {

6 ComponentName comp =

7 new ComponentName(context.getPackageName(),

8 GCMIntentService.class.getName());

10 startWakefulService(context, (intent.setComponent(comp)));

11

12 setResultCode(Activity.RESULT_OK);

13 }

14 }

Para el servicio crearemos una nueva clase GCMIntentService que extienda deIntentService (para ms informacin
sobre los Intent Service puedes consultar el artculo dedicado a ellos) y como siempre implementaremos su
evento onHandleIntent(). Aqu lo primero que haremos ser nuevamente obtener una instancia a los Servicios de
Google Play, y posteriormente obtener el tipo de mensaje recibido (mediante getMessageType()) y sus parmetros
(mediantegetExtras()). Dependiendo del tipo de mensaje obtenido podremos realizar unas acciones u otras. Existen
algunos tipos especiales de mensaje (MESSAGE_TYPE_SEND_ERROR,MESSAGE_TYPE_DELETED, ) para ser
notificado de determinados eventos, pero el que nos interesa ms ser el tipo MESSAGE_TYPE_MESSAGE que
identifica a los mensajes normales o genricos de GCM. Para nuestro ejemplo, en caso de recibirse uno de estos
mensajes simplemente mostraremos una notificacin en la barra de estado llamando a un mtodo
auxiliar mostrarNotificacion(). La implementacin de este ltimo mtodo tampoco la comentaremos en detalle
puesto que tenis disponible un artculo del curso especialmente dedicado a este tema.

1 public class GCMIntentService extends IntentService

2 {

3 private static final int NOTIF_ALERTA_ID = 1;

5 public GCMIntentService() {
6 super("GCMIntentService");

7 }

9 @Override

10 protected void onHandleIntent(Intent intent)

11 {

12 GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);

13

14 String messageType = gcm.getMessageType(intent);

15 Bundle extras = intent.getExtras();

16

17 if (!extras.isEmpty())

18 {

19 if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))

20 {

21 mostrarNotification(extras.getString("msg"));

22 }

23 }

24

25 GCMBroadcastReceiver.completeWakefulIntent(intent);

26 }

27

28 private void mostrarNotification(String msg)

29 {

30 NotificationManager mNotificationManager =

31 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

32

33 NotificationCompat.Builder mBuilder =

34 new NotificationCompat.Builder(this)

35 .setSmallIcon(android.R.drawable.stat_sys_warning)

36 .setContentTitle("Notificacin GCM")

37 .setContentText(msg);

38

39 Intent notIntent = new Intent(this, MainActivity.class);


40 PendingIntent contIntent = PendingIntent.getActivity(

41 this, 0, notIntent, 0);

42

43 mBuilder.setContentIntent(contIntent);

44

45 mNotificationManager.notify(NOTIF_ALERTA_ID, mBuilder.build());

46 }

47 }

S es importante fijarse en que al final del mtodo onHandleIntent(), tras realizar todas las acciones necesarias para
procesar el mensaje recibido, debemos llamar al mtodocompleteWakefulIntent() de
nuestro GCMBroadcastReceiver. Esto har que el dispositivo pueda volver a entrar en modo sleep cuando sea
necesario. Olvidar esta llamada podra implicar consumir rpidamente la batera del dispositivo, y no es lo que
queremos, verdad?

Pues bien, hemos terminado. Ya tenemos nuestro servidor y nuestro cliente GCM preparados. Si ejecutamos ambas
y todo ha ido bien, introducimos un nombre de usuario en la aplicacin Android, pulsamos Registrar para
guardarlo y registrarnos, seguidamente desde la aplicacin web introducimos el mismo nombre de usuario del
cliente y pulsamos el botn Enviar GCM, en pocos segundos nos debera aparecer la notificacin en la barra de
estado de nuestro emulador como se observa en la imagen siguiente:

Es conveniente utilizar un emulador en el que se ejecute una versin de Android 4.2.2 o superior, dado que en
versiones anteriores Google Play Services podra no funcionar. An as, en ocasiones los mensajes tardar varios
minutos en recibirse, por lo que tened algo de paciencia.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pagina del curso en GitHub.
Integracin con Google+
Integracin con Google+ (I): Google+ Sign-In
by Sgoliver on 22/08/2013 in Android, Programacin

En los prximos artculos del Curso de Programacin Android vamos a centrarnos en otra de las novedades
presentadas hace poco como parte de los Google Play Services, en concreto la integracin de aplicaciones con
Google+. Integrar nuestras aplicaciones con Google+ nos va a permitir entre otras cosas la posibilidad de que los
usuarios inicien sesin en nuestra aplicacin con su cuenta de Google, personalizar la aplicacin en funcin de los
datos de su perfil pblico y sus crculos, o enviar publicaciones a su perfil informando de la actividad del usuario en
nuestra aplicacin. En este primer artculo nos centraremos en la primera y ms importante de estas posibilidades, el
inicio de sesin con Google+.

Al igual que ocurra con las apis de mapas o mensajera push, para hacer uso de la API de integracin con Google+
necesitaremos crear un nuevo proyecto en la consola de APIs de Google, habilitar el servicio de Google+, y generar
una nueva clave de acceso, en este caso un nuevo ID de Cliente para autenticacin mediante OAuth 2.0.

Para ello accederemos a la Consola de APIs y crearemos un nuevo proyecto con el nombre que deseemos utilizando
la opcin Create de la lista desplegable situada en la parte superior izquierda. Una vez creado el proyecto,
accederemos a la seccin Services y habilitaremos el servicio llamado Google+ API.

Tras esto, accederemos a la seccin API Access para generar el ID de acceso al servicio. Para ello pulsamos sobre la
opcin Create an OAuth 2.0 Client ID, lo que nos llevar a un asistente de configuracin. En la primera pantalla
indicaremos el nombre de la aplicacin y un logo (opcional).

En la segunda, seleccionaremos la opcin Installed application y Android como tipo de aplicacin. Adems
tendremos que rellenar el paquete java que utilizado en nuestra aplicacin y la huella SHA1 del certificado con el que
firmaremos la aplicacin. Esto ya lo hemos comentado en alguna ocasin, mientras estemos
desarrollando/depurando la aplicacin usaremos el certificado de pruebas (debug) instalado con el SDK de Android,
pero cuando subamos nuestra aplicacin a Google Play tendremos que modificar este dato por el correspondiente al
certificado real con el que firmemos la aplicacin final (si no lo hacemos as la aplicacin no funcionar). Si utilizamos
una versin reciente de Eclipse y el plugin de Android (ADT) podemos obtener la huella SHA1 del certificado de
pruebas accediendo a las preferencias de Eclipse, en la seccin Android / Build. En la siguiente captura podis ver la
localizacin de este dato:
Si no dispusiramos de una versin reciente de las herramientas de desarrollo, tambin podemos obtener el dato
utilizando la utilidad keytool de java, tal como se indica por ejemplo en el artculo del curso sobre la API de Google
Maps.

Por ltimo, activaremos la opcin Deep Linking Enabled y finalizaremos el asistente pulsando el botn Create
client ID.

Con esto ya tendramos configurado el proyecto en la Consola de APIs y podramos comenzar a crear el proyecto en
Eclipse. El primer paso, como ocurre con todos los proyectos que hacen uso de los Google Play Services, ser
importar el proyecto de librera que lo soporta y hacer referencia a l desde nuestro proyecto. Estos pasos se
comentan en detalle en el artculo de introduccin a los Google Play Services.

Una vez preparado el proyecto en Eclipse entramos por fin en la parte interesante. Para aadir el botn de login de
Google+ en nuestra aplicacin bastar con incluir en nuestro layout un control de tipoSignInButton de la siguiente
forma:

1 <com.google.android.gms.common.SignInButton

2 android:id="@+id/sign_in_button"

3 android:layout_width="wrap_content"

4 android:layout_height="wrap_content" />

Este botn ser el que permita al usuario acceder a nuestra aplicacin haciendo uso de su usuario de Google. Sin
embargo, no ser el botn el que desencadene el proceso de conexin a Google+, sino que actuaremos de una
forma algo peculiar. Nuestra actividad intentar conectarse al servicio desde su inicio de forma que si el usuario ya
estaba logueado con anterioridad el proceso de conexin se transparente para l. De forma anloga, justo al salir de
la actividad nos desconectaremos del servicio.
Pero entonces, para qu nos sirve el botn de login? Pues la clave del prrafo anterior est en las palabras si el
usuario ya estaba logueado con anterioridad. Y no slo eso, para que la conexin sea completamente transparente
y no necesite ninguna intervencin del usuario, ste debe estar ya logueado y adems debe haber dado su permiso
para que la aplicacin acceda a sus datos de Google+ y pueda realizar determinadas acciones en su nombre. En el
caso de que alguno de estos pasos no se hayan realizado ya, el intento de conexin realizado al inicio de la actividad
derivar en un error que deber ser tratado por el usuario. Y esto es precisamente lo que conseguir el usuario al
pulsar el botn de login colocado en la aplicacin. Pero tampoco hay que preocuparse, porque la API de Google+
proporciona todos los elementos necesarios para que el usuario pueda resolver estas acciones, por ejemplo el
dilogo de seleccin de la cuenta con la que se acceder a Google+, o el dilogo donde el usuario podr seleccionar
los permisos relacionados con Google+ que desea conceder a la aplicacin (por ejemplo, la visibilidad de
determinados crculos).

Pues bien, veamos cmo plasmamos en el cdigo de la aplicacin todo esto que hemos contado con palabras.
Empezaremos inicializando los componentes necesarios durante la creacin de la actividad. La conexin con Google+
se sustenta completamente en la clase PlusClient, por lo que el primer paso ser crear e inicilizar un objeto de este
tipo. Esto lo conseguimos mediante el mtodoPlusClient.Builder(). En esta inicializacin indicaremos adems las
actividades (de acciones, no de Activity) del usuario en la aplicacin que la propia aplicacin podr publicar en el
perfil de Google+ en nombre del usuario (por ejemplo acciones del estilo a He escuchado tal cancin, He visto tal
imagen o He comentado tal noticia). Existen varios tipos de actividad predefinidas
comoBuyActivity, ListenActivity, CommentActivity para actividades de compras, reproduccin de msica o
comentarios (podis revisar la lista completa en esta pgina y un tipo genrico (AddActivity) para cuando las
actividades que enviar nuestra aplicacin a Google+ con encaja con ninguno de los tipos predefinidos. La lista de
actividades que la aplicacin podr enviar al perfil de Google+ del usuario se configurar mediante el
mtodo setVisibleActivities(), y sern mostradas al usuario al loguearse por primera vez en la aplicacin de forma
que ste sea consciente de ello y pueda conceder su permiso. Ms tarde pondr una captura de pantalla donde
podr verse esto claramente. En nuestro caso aadiremos por ejemplo las
actividades AddActivity yListenActivity para ver el efecto. Adems de esto, para terminar inicializaremos tambin un
dilogo de progreso que utilizaremos ms tarde. Veamos cmo queda el mtodo onCreate() al completo:

1 @Override

2 protected void onCreate(Bundle savedInstanceState)

3 {

4 super.onCreate(savedInstanceState);

5 setContentView(R.layout.activity_main);

7 btnSignIn = (SignInButton)findViewById(R.id.sign_in_button);

9 plusClient = new PlusClient.Builder(this, this, this)

10 .setVisibleActivities(

11 "http://schemas.google.com/AddActivity",

12 "http://schemas.google.com/ListenActivity")

13 .build();

14

15 connectionProgressDialog = new ProgressDialog(this);

16 connectionProgressDialog.setMessage("Conectando...");
17

18 //...

19 }

Vamos ahora con la conexin y desconexin a Google+. Como dijimos anteriormente, la conexin la intentaremos
realizar desde el inicio de la actividad y nos desconectaremos juasto al salir, por lo que podemos aprovechar los
eventos onStart() y onStop() de la actividad para realizar estas acciones. Utilizaremos para ello los
mtodos connect() y disconnect() de la clase PlusClient.

1 @Override

2 protected void onStart()

3 {

4 super.onStart();

5 plusClient.connect();

6 }

8 @Override

9 protected void onStop()

10 {

11 super.onStop();

12 plusClient.disconnect();

13 }

Para capturar el resultado de estas acciones de conexin y desconexin podemos implementar los
eventos onConnected() y onDisconnected(), para lo que nuestra actividad tendr que implementar la
interfaz ConnectionCallbacks.

1 public class MainActivity extends Activity

2 implements ConnectionCallbacks

3 {

4 //...

6 @Override

7 public void onConnected(Bundle connectionHint)

8 {

9 connectionProgressDialog.dismiss();

10

11 Toast.makeText(this, "Conectado!",

12 Toast.LENGTH_LONG).show();

13 }
14

15 @Override

16 public void onDisconnected()

17 {

18 Toast.makeText(this, "Desconectado!",

19 Toast.LENGTH_LONG).show();

20 }

21 }

Con lo implementado hasta ahora bastar la mayora de las veces. Sin embargo, como dijimos al principio, la primera
vez que el usuario se intenta conectar se requieren acciones adicionales, como seleccionar la cuenta a utilizar con
Google+ o conceder a la aplicacin los permisos necesarios para interactuar con nuestro perfil de Google+. En estas
circunstancias la llamada al mtodo connect() no tendr xito. Cmo podemos solucionarlo? Pues como ya hemos
indicado esta situacin intentar solucionarse cuando el usuario pulse el botn de login de Google+ que hemos
colocado en la aplicacin. Pero para poder solucionarlo antes debemos saber qu ha ocurrido exactamente en la
llamada a connect(). Esto lo podemos saber implementando el evento onConnectionFailed()que se ejecuta cuando la
llamada a connect() no finaliza correctamente. Este evento recibe como parmetro un objeto de
tipo ConnectionResult que contiene el motivo por el que no hemos podido conectarnos al servicio de Google+. Para
poder gestionar este evento haremos que nuestra actividad implemente otra interfaz ms
llamada OnConnectionFailedListener.

1 public class MainActivity extends Activity

2 implements ConnectionCallbacks, OnConnectionFailedListener

3 {

4 //...

6 @Override

7 public void onConnectionFailed(ConnectionResult result)

8 {

9 //...

10

11 connectionResult = result;

12 }

13 }

Como podemos ver, en este evento nos limitaremos por el momento a guardar el objetoConnectionResult para
tenerlo disponible cuando el usuario pulse el botn de login.

Y vamos ya por fin con nuestro botn. Qu debemos hacer cuando el usuario pulse el botn de login? Pues en
primer lugar comprobaremos que no estamos ya conectados mediante una llamada al mtodoisConnected(), en
cuyo caso no habr nada que hacer. Si no estamos an conectados pueden ocurrir dos cosas: que dispongamos ya
del resultado del intento de conexin en forma de objetoConnectionResult, o que an no lo tengamos disponible.
Para este ltimo caso utilizaremos el dilogo de progreso que inicializamos en el onCreate() de la actividad, lo
mostraremos mediante su mtodo show() y quedaremos a la espera de disponer del resultado de la conexin.
En caso de conocer ya el resultado de la conexin, llamaremos a su mtodostartResolutionForResult(). Este mtodo
mgico provocar que se muestren al usuario las opciones necesarias para resolver los errores detectados
durante el intento de conexin a Google+, entre ellos la seleccin de cuenta o el dilogo de concesin de permisos
de Google+.

1 btnSignIn.setOnClickListener(new OnClickListener() {

3 @Override

4 public void onClick(View view)

5 {

6 if (!plusClient.isConnected())

7 {

8 if (connectionResult == null)

9 {

10 connectionProgressDialog.show();

11 }

12 else

13 {

14 try

15 {

16 connectionResult.startResolutionForResult(

17 MainActivity.this,

18 REQUEST_CODE_RESOLVE_ERR);

19 }

20 catch (SendIntentException e)

21 {

22 connectionResult = null;

23 plusClient.connect();

24 }

25 }

26 }

27 }

28 });

Cuando el usuario termine de configurar las opciones de conexin a Google+ se lanzar automticamente el
evento onActivityResult(), momento que aprovecharemos para volver a realizar la conexin llamando de nuevo
a connect() ahora que no deberan quedar acciones por realizar por parte el usuario. Si todo va bien, este nuevo
intento de conexin debera terminar con xito y el usuario quedara conectado (y entre otras cosas se ejecutar el
evento onConnected() del que ya hemos hablado).
1 @Override

2 protected void onActivityResult(int requestCode, int responseCode, Intent intent)

3 {

4 if (requestCode == REQUEST_CODE_RESOLVE_ERR &&

5 responseCode == RESULT_OK)

6 {

7 connectionResult = null;

8 plusClient.connect();

9 }

10 }

Con esto casi hemos terminado. Pero nos faltan un par de detalles por cerrar que antes he omitido a posta para
llevar un orden ms lgico en la explicacin. En primer lugar, qu ocurre cuando el resultado del primer intento de
conexin nos llega despus de que el usuario haya pulsado el botn de login (y por tanto ya se est mostrando el
dilogo de progreso)? En ese caso no slo guardaremos el resultado de la conexin, sino que desencadenaremos
directamente el proceso de resolucin de errores llamando a startResolutionForResult() igual que hemos hecho en el
evento onClick del botn de login. De esta forma, el evento onConnectionFailed() quedara finalmente de la
siguiente forma:

1 @Override

2 public void onConnectionFailed(ConnectionResult result)

3 {

4 if (connectionProgressDialog.isShowing())

5 {

6 if (result.hasResolution())

7 {

8 try

9 {

10 result.startResolutionForResult(this,

11 REQUEST_CODE_RESOLVE_ERR);

12 }

13 catch (SendIntentException e)

14 {

15 plusClient.connect();

16 }

17 }

18 }

19
20 connectionResult = result;

21 }

Por tlimo, nos queda cerrar el dilogo de progreso una vez que el usuario est correctamente conectado, lo que
haremos en el evento onConnected() y utilizaremos para ello el mtododismiss() del dilogo, quedando finalmente
as:

1 @Override

2 public void onConnected(Bundle connectionHint)

3 {

4 connectionProgressDialog.dismiss();

6 Toast.makeText(this, "Conectado!",

7 Toast.LENGTH_LONG).show();

8 }

Y ahora s habramos terminado, por lo que estamos en condiciones de probar la aplicacin. Recomiendo probar en
un dispositivo real conectado por USB, ya que los servicios de Google Play no funcionan correctamente en el
emulador (dependiendo de la versin e imagen de Android utilizada).

Al ejecutar la aplicacin se mostrar el botn de login de Google+ y comenzar de forma silenciosa el proceso de
conexin.

Al pulsar el botn de Iniciar Sesin debemos ya contar con el resultado de la conexin, por lo que se nos debe dirigir
directamente al proceso de resolucin de errores. En primer lugar tendremos que seleccionar la cuenta de google
con la que queremos contectarnos:
Tras seleccionar una cuenta aparecer el dilogo de configuracin de permisos de Google+, en el que se informa al
usuario de los permisos que solicita la aplicacin. Podremos definir los crculos a los que tendr acceso la aplicacin y
los crculos que podrn ver las publicaciones que la aplicacin realice por nosotros en nuestro perfil de Google+.

En la captura anterior me gustara que prestrais atencin al texto del segundo bloque, donde indica Permitir que
la actividad de aplicaciones y de audio est disponible. Este mensaje debe coincidir con las actividades que
establecimos al llamar al mtodo setVisibleActivities() al principio del ejemplo. Recuerdo que nosostros
seleccionamos la genrica AddActivity y la de audioListenActivity.

Tras aceptar esta ltima pantalla deberamos estar ya conectados correctamente a Google+, apareciendo el mensaje
toast que nos informa de ello. Lo que podremos hacer a partir de aqu queda para el siguiente artculo.

Pero no terminamos an, nos quedan un par de temas importantes por aadir. Si queremos que nuestra aplicacin
cumpla con las polticas de Google+ debemos ofrecer al usuario una opcin para cerrar sesin en Google+, y otra
para que pueda revocar los permisos que ha concedido a nuestra aplicacin la primera vez que se conect.

En mi ejemplo aadir estas opciones como acciones del men de overflow de la action bar (si necesitas informacin
sobre cmo hacer esto puedes ojear el artculo sobre la action bar).

Cerrar sesin ser tan sencillo como llamar al mtodo clearDefaultAccount() y desconectarnos con disconnect().
Posteriormente volvemos a llamar a connect() para que se inicie un nuevo proceso de login si el usuario pulse de
nuevo el botn.

La opcin de revocar los permisos de la aplicacin tampoco es mucho ms complicada. Llamaremos igual que antes
al mtodo clearDefaultAccount() para eliminar la vinculacin de nuestra cuenta con la aplicacin, y posteriormente
llamaremos a revokeAccessAndDisconnect() para revocar los permisos concedidos. Este ltimo mtodo recibe como
parmetro el listener con el que podremos capturar el evento de revocacin de permisos finalizada
(onAccessRevoked). En nuestro tan solo mostraremos un toast para informar de ello. Veamos cmo quedaran
ambas acciones en el eventoonMenuItemSelected de la action bar.

1 @Override

2 public boolean onMenuItemSelected(int featureId, MenuItem item)

3 {

4 switch (item.getItemId())

5 {

6 //Cerrar Sesin
7 case R.id.action_sign_out:

8 if (plusClient.isConnected())

9 {

10 plusClient.clearDefaultAccount();

11 plusClient.disconnect();

12 plusClient.connect();

13

14 Toast.makeText(MainActivity.this,

15 "Sesin Cerrada.",

16 Toast.LENGTH_LONG).show();

17 }

18

19 return true;

20 //Revocar permisos a la aplicacin

21 case R.id.action_revoke_access:

22 if (plusClient.isConnected())

23 {

24 plusClient.clearDefaultAccount();

25

26 plusClient.revokeAccessAndDisconnect(

27 new OnAccessRevokedListener() {

28 @Override

29 public void onAccessRevoked(ConnectionResult status)

30 {

31 Toast.makeText(MainActivity.this,

32 "Acceso App Revocado",

33 Toast.LENGTH_LONG).show();

34 }

35 });

36 }

37

38 return true;

39 default:

40 return super.onMenuItemSelected(featureId, item);


41 }

42 }

Y hasta aqu el primer artculo sobre integracin con Google+. En el siguiente veremos varias de las funcionalidades
que tendremos disponibles al estar conectados con este servicio.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pagina del curso en GitHub.
Integracin con Google+ (II): Datos de perfil y Crculos
by Sgoliver on 19/09/2013 in Android, Programacin

En el artculo anterior de la serie vimos cmo podamos incluir en nuestra aplicacin la opcin de que el usuario se
pueda loguear utilizando su cuenta de Google a travs de Google+. En este nuevo artculo veremos cmo acceder a
la informacin del perfil del usuario y cmo recuperar la lista de sus contactos y la informacin sobre ellos.

Empecemos por el principio. Una vez el usuario est correctamente logueado en Google+ nuestra aplicacin tendr
acceso a todos los datos pblicos de su perfil. El acceso a estos datos se podr realizar a travs de la clase Person,
que contiene los mtodos necesarios para recuperar cada uno de los datos del perfil.

Para obtener una referencia a un objeto Person para el usuario logueado utilizaremos el
mtodoloadPerson() pasndole como segundo parmetro el ID del usuario si lo conocemos, o el valor especial me.
Por su parte, el primer parmetro de loadPerson() debe ser una referencia a un objeto que implemente la
interfaz PlusClient.OnPersonLoadedListener, que aade un nico mtodo (onPersonLoaded) que ser llamado
cuando se hayan recuperado los datos del usuario solicitado y pueda utilizarse su objeto Person asociado.

En nuestro caso de ejemplo haremos que sea nuestra actividad principal la que implemente dicha interfaz e
implementaremos el mtodo onPersonLoaded() como uno ms de la actividad. Indicar aqu que para el ejemplo de
este artculo partiremos del ya construido en el artculo anterior. Veamos entonces cmo accederamos a los datos
del usuario logueado:

1 public class MainActivity extends Activity

2 implements ConnectionCallbacks, OnConnectionFailedListener,

3 PlusClient.OnPersonLoadedListener

4 {

5 @Override

6 public void onConnected(Bundle connectionHint)

7 {

8 //...

10 //Informacin del perfil del usuario logueado:

11 plusClient.loadPerson(this, "me");

12 }

13

14 @Override

15 public void onPersonLoaded(ConnectionResult status, Person person) {

16 if (status.getErrorCode() == ConnectionResult.SUCCESS)

17 {

18 txtInfo.setText(

19 person.getId() + "\n" +

20 person.getDisplayName() + "\n" +
21 person.getPlacesLived().get(0).getValue() + "\n" +

22 person.getOrganizations().get(1).getName() + "\n" +

23 person.getUrls().get(0).getValue() + "\n" +

24 plusClient.getAccountName()

25 );

26 }

27 }

28

29 //...

30 }

Como podemos ver, en el mtodo onConnected() llamamos a loadPerson() para solicitar de forma asncrona los
datos del usuario me, es decir, el usuario actualmente loguado en la aplicacin. Una vez estos datos estn
disponible se ejecutar automticamente el mtodo onPersonLoaded(), en el que se recibe como parmetro el
objeto Person que encapsula todos los datos del perfil pblico del usuario.

Para acceder a los datos a travs del objeto Person tenemos disponibles multitud de mtodos que podis consultar
en la documentacin oficial de la clase. Yo, a modo de ejemplo, utilizo cinco de ellos y muestro el resultado en un
simple cuadro de texto (txtInfo) que he aadido a la interfaz. En primero lugar recupero el ID del usuario con getId(),
a continuacin recupero el nombre mostrado en el perfil con getDisplayName(), despus el lugar de residencia
con getPlacesLived(), mi empresa actual con getOrganizations() y el primero de los enlaces pblicos de mi perfil que
corresponde a mi web utilizando getUrls().

Como podis ver algunos de estos mtodos devuelven listas de datos, como getPlacesLived() ogetOrganizations(), a
cuyos elementos accedemos mediante el mtodo get(i) y obtenemos su valor
con getName() o getValue() dependiendo de la entidad recuperada. Como ya he dicho, los datos disponibles son
muchos y lo mejor es consultar la documentacin oficial en cada caso.

Si volveis a mirar el cdigo anterior, al final del todo recupero un dato ms, en este caso utilizando directamente un
mtodo del cliente de Google+ en vez de la clase Person. Este mtodogetAccountName() se utiliza para consultar la
direccin de correo electrnico del usuario logueado, ya que aunque existe un mtodo getEmails() en la
clase Person, ste no devolver la direccin principal del usuario a menos que ste la haya hecho pblica en su perfil.

Si ejecutamos ahora la aplicacin de ejemplo podris ver los datos del perfil del usuario que hayis utilizado para
hacer login, en mi caso algo as:

Lo siguiente que vamos a recuperar son los contactos incluidos en los crculos del usuario. Recordad que tal como
vimos en el artculo anterior la aplicacin slo tendr acceso a los crculos a los que usuario haya dado permiso al
loguarse en la aplicacin. Otro detalle a tener en cuenta es que podremos acceder a los contactos pero no a los
nombres de los crculos que los contienen.
Para recuperar los contactos incluidos en los crculos del usuario que son visibles para la aplicacin utilizaremos el
mtodo loadPeople(). Este mtodo nos devolver un objeto PersonBuffer con todos los contactos de los crculos
visibles. Al igual que pasaba con loadPerson(), esta carga la har de forma asncrona de forma que cuando haya
finalizado se llamar automticamente al mtodoonPeopleLoaded() del objeto que le pasemos como parmetro,
que debe implementar la interfaz PlusClient.OnPeopleLoadedListener. Para ello haremos lo mismo que antes,
implementaremos dicha interfaz en nustra actividad principal.

1 public class MainActivity extends Activity

2 implements ConnectionCallbacks, OnConnectionFailedListener,

3 PlusClient.OnPersonLoadedListener, PlusClient.OnPeopleLoadedListener

4 {

5 @Override

6 public void onConnected(Bundle connectionHint)

7 {

8 //...

10 //Personas en mis crculos visibles para la aplicacin:

11 plusClient.loadPeople(this, Person.Collection.VISIBLE);

12 }

13

14 @Override

15 public void onPeopleLoaded(ConnectionResult status, PersonBuffer personBuffer, String nextPageToken) {

16 if (status.getErrorCode() == ConnectionResult.SUCCESS)

17 {

18 try

19 {

20 int count = personBuffer.getCount();

21 StringBuffer contactos = new StringBuffer("");

22

23 for (int i = 0; i < count; i++)

24 {

25 contactos.append(

26 personBuffer.get(i).getId() + "|" +

27 personBuffer.get(i).getDisplayName() + "\n");

28 }

29

30 txtContactos.setText(contactos);
31 }

32 finally

33 {

34 personBuffer.close();

35 }

36 }

37 }

38 }

Comentemos la implementacin del mtodo onPeopleLoaded(). Lo primero que hacemos es obtener el nmero de
contactos recuperados llamando al mtodo getCount() del PersonBuffer. Hecho esto recorremos la lista accediendo
a cada contacto mediante el mtodo get(i) que devuelve su objetoPerson asociado. A partir de aqu ya podemos
mostrar los datos necesarios de cada contacto utilizando los mtodos de la clase Person que ya hemos comentado
antes. Como ejemplo yo muestro el ID y el nombre de cada contacto en un cuadro de texto adicional que he aadido
a la interfaz. Por ltimo, pero no menos importante, debemos cerrar el objeto PersonBuffer mediante su
mtdoclose() para liberar recursos.

Si volvemos a ejecutar ahora la aplicacin obtendremos algo similar a lo siguiente:

Por ltimo, comentar que ahora que tenemos los ID de cada contacto, si en algn momento necesitamos obtener los
datos de su perfil podramos utilizar el mismo mtodo loadPerson() que hemos comentado antes pasndole su ID
como segundo parmetro. As, por ejemplo, si quisiera recuperar los datos del perfil de Roman Nurik realizaramos la
siguente llamada:

1 //Perfil de un contacto:

2 plusClient.loadPerson(this, "113735310430199015092");

Y hasta aqu este segundo artculo de la serie. En el prximo veremos cmo podemos publicar contenidos en
Google+ desde nuestra aplicacin.

Puedes consultar y/o descargar el cdigo completo de los ejemplos desarrollados en este artculo accediendo a la
pagina del curso en GitHub.

S-ar putea să vă placă și