Mostrando entradas con la etiqueta Programación en Android - Auor : Salvador Gómez Oliver. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programación en Android - Auor : Salvador Gómez Oliver. Mostrar todas las entradas

sábado, 27 de diciembre de 2014

Hilos y Tareas Asíncronas (Thread y AsyncTask) en Android



Todos los componentes de una aplicación Android, tanto las actividades, los servicios [sí, también los servicios], o los broadcast receivers se ejecutan en el mismo hilo de ejecución, el llamado hilo principal, main thread o GUI thread, que como éste último nombre indica también es el hilo donde se ejecutan todas las operaciones que gestionan la interfaz de usuario de la aplicación. Es por ello, que cualquier operación larga o costosa que realicemos en este hilo va a bloquear la ejecución del resto de componentes de la aplicación y por supuesto también la interfaz, produciendo al usuario un efecto evidente de lentitud, bloqueo, o mal funcionamiento en general, algo que deberíamos 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 aplicación o esperar a que termine.


Obviamente, éstos son el tipo de errores que nadie quiere ver al utilizar su aplicación, y en este apartado y los siguientes vamos a ver varias formas de evitarlo utilizando procesos en segundo plano para ejecutar las operaciones de larga duración. En este primer apartado de la serie nos vamos a centrar en dos de las alternativas más directas a la hora de ejecutar tareas en segundo plano en Android:

• Crear nosotros mismos de forma explícita un nuevo hilo para ejecutar nuestra tarea.
• Utilizar la clase auxiliar AsyncTask proporcionada por Android.

Mi idea inicial para este capítulo era obviar la primera opción, ya que normalmente la segunda solución nos es más que suficiente, y además es mas sencilla y más 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 rápidamente por la primera opción para después centrarnos un poco más en la segunda. Además, aprovechando el tema de la ejecución de tareas en segundo plano, vamos a ver también cómo utilizar un control (el ProgressBar) y un tipo de diálogo (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 aplicación de ejemplo en cuya actividad principal colocaremos un control ProgressBar (en mi caso llamado pbarProgreso) y un botón (btnSinHilos) que ejecute una tarea de larga duración. Para simular una operación de larga duración vamos a ayudarnos de un método auxiliar que lo único que haga sea esperar 1 segundo, mediante una llamada a Thread. sleep().


Haremos que nuestro botón ejecute este método 10 veces, de forma que nos quedará una ejecución de unos 10 segundos en total:


Como veis, aquí todavía no estamos utilizando nada especial, por lo que todo el código se ejecutará en el hilo principal de la aplicación. En cuanto a la utilización del control ProgressBar vemos que es muy sencilla y no requiere apenas configuración. En nuestro caso tan sólo hemos establecido el valor máximo que alcanzará (el valor en el que la barra de progreso estará rellena al máximo) mediante el método setMax(100), posteriormente la hemos inicializado al valor mínimo mediante una llamada a setProgress(0) de forma que inicialmente aparezca completamente vacía, y por último en cada iteración del bucle incrementamos su valor en 10 unidades llamando a incrementProgressBy(10), de tal forma que tras la décima iteración la barra llegue al valor máximo de 100 que establecimos antes. Finalmente mostramos un mensaje Toast para informar de la finalización de la tarea. Pues bien, ejecutemos la aplicación, pulsemos el botón, y veamos qué ocurre.

No era eso lo que esperábamos ¿verdad? Lo que ha ocurrido es que desde el momento que hemos pulsado el botón para ejecutar la tarea, hemos bloqueado completamente el resto de la aplicación, incluida la actualización 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 decíamos, este efecto puede empeorar. Probemos ahora a pulsar el botón de la tarea y mientras ésta se ejecuta realicemos cualquier acción sobre la pantalla, un simple click sobre el fondo nos basta. Veamos qué ocurre ahora.

Vemos cómo al intentar hacer cualquier acción sobre la aplicación Android nos ha advertido con un mensaje de error que la aplicación no responde debido a que se está ejecutando una operación de larga duración en el hilo principal. El usuario debe elegir entre esperar a que termine de ejecutarla o forzar el cierre de la aplicación. Pues bien, estos son los efectos que vamos a intentar evitar. La opción más 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 parámetro un nuevo objeto Runnable que debemos construir implementando su método run(), dentro del cual vamos a realizar nuestra tarea de larga duración. Hecho esto, debemos llamar al método start() del objeto Threaddefinido para comenzar la ejecución de la tarea en segundo plano.


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 método run() no podríamos ir actualizando directamente nuestra barra de progreso de la misma forma que lo hacíamos antes. Para solucionar esto, Android proporciona varias alternativas, entre ellas la utilización del método post() para actuar sobre cada control de la interfaz, o la llamada al método runOnUiThread() para "enviar" operaciones al hilo principal desde el hilo secundario [Nota: Sí, vale, sé que no he nombrado la opción de los Handler, pero no quería complicar más el tema por el momento].
Ambas opciones requieren como parámetro un nuevo objeto Runnable del que nuevamente habrá que implementar su método run() donde se actúe sobre los elementos de la interfaz. Por ver algún ejemplo, en nuestro caso hemos utilizado el método post() para actuar sobre el control ProgressBar, y el método runOnUiThread()para mostrar el mensaje toast.


Utilicemos este código dentro de un nuevo botón de nuestra aplicación de ejemplo y vamos a probarlo en el emulador.

Ahora sí podemos ver el progreso de nuestra tarea reflejado en la barra de progreso. La creación 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 ejecución de la tarea en segundo plano. Sin embargo miremos de nuevo el código anterior. Complicado de leer, ¿verdad? Y eso considerando que tan sólo estamos actualizando un control de nuestra interfaz. Si el número de controles fuera mayor, o necesitáramos una mayor interacción con la interfaz el código empezaría a ser inmanejable, difícil de leer y mantener, y por tanto también más 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 más organizada y legible. La forma básica de utilizar la clase AsyncTask consiste en crear una nueva clase que extienda de ella y sobrescribir varios de sus métodos entre los que repartiremos la funcionalidad de nuestra tarea. Estos métodos son los siguientes:

• onPreExecute(). Se ejecutará antes del código principal de nuestra tarea. Se suele utilizar para preparar la ejecución de la tarea, inicializar la interfaz, etc.
doInBackground(). Contendrá el código principal de nuestra tarea.
onProgressUpdate(). Se ejecutará cada vez que llamemos al método publishProgress() desde el método doInBackground().
• onPostExecute(). Se ejecutará cuando finalice nuestra tarea, o dicho de otra forma, tras la finalización del método doInBackground().
• onCancelled(). Se ejecutará cuando se cancele la ejecución de la tarea antes de su finalización normal.


Estos métodos tienen una particularidad esencial para nuestros intereses. El método doInBackground() se ejecuta en un hilo secundario (por tanto no podremos interactuar con la interfaz), pero sin embargo todos los demás se ejecutan en el hilo principal, lo que quiere decir que dentro de ellos podremos hacer referencia direct a nuestros controles de usuario para actualizar la interfaz. Por su parte, dentro de doInBackground() tendremos la posibilidad de llamar
periódicamente al método publishProgress() para que automáticamente desde el método onProgressUpdate() se actualice la interfaz si es necesario. Al extender una nueva clase de AsyncTask indicaremos tres parámetros de tipo:

• El tipo de datos que recibiremos como entrada de la tarea en el método doInBackground().

• El tipo de datos con el que actualizaremos el progreso de la tarea, y que recibiremos como parámetro del método onProgressUpdate() y que a su vez tendremos que incluir como parámetro del método publishProgress().

• El tipo de datos que devolveremos como resultado de nuestra tarea, que será el tipo de retorno del método doInBackground() y el tipo del parámetro recibido en el método onPostExecute().

En nuestro caso de ejemplo, extenderemos de AsyncTask indicando los tipos Void, Integer y Boolean respectivamente, lo que se traducirá en que:

• doInBackground() no recibirá ningún parámetro de entrada (Void).
• publishProgress() y onProgressUpdate() recibirán como parámetros datos de tipo entero (Integer).
• doInBackground() devolverá como retorno un dato de tipo booleano y onPostExecute() también recibirá como parámetro un dato del dicho tipo (Boolean).

Dicho esto, cómo repartiremos la funcionalidad de nuestra tarea entre los distintos métodos. Pues sencillo, en onPreExecute() inicializaremos la barra de progreso estableciendo su valor máximo y poniéndola a cero para comenzar. En doInBackground() ejecutaremos nuestro bucle habitual llamando a publishProgress() tras cada iteración indicando el progreso actual. En onProgressUpdate() actualizaremos el estado de la barra de progreso con el valor recibido como parámetro. Y por último en onPostExecute() mostraremos el mensaje Toast de finalización de la tarea. Veamos el código completo:



Si observamos con detenimiento el código, la única novedad que hemos introducido es la posibilidad de cancelar la tarea en medio de su ejecución. Esto se realiza llamando al método cancel() de nuestra AsyncTask (para lo cual añadiremos un botón más a nuestra aplicación de ejemplo, además del nuevo que añadiremos para comenzar la tarea). Dentro de la ejecución de nuestra tarea en doInBackground() tendremos además que consultar periodicamente el resultado del método isCancelled() que nos dirá si el usuario ha cancelado la tarea (es decir, si se ha llamado al método cancel()), en cuyo caso deberemos de terminar la ejecución lo antes posible, en nuestro caso de ejemplo simplemente saldremos del bucle con la instrucción break. Además, tendremos en cuenta que en los casos que se cancela la tarea, tras el método doInBackground() no se llamará a onPostExecute() sino al método onCancelled(), dentro del cual podremos realizar cualquier acción para confirma la cancelación de la tarea. En nuestro caso mostraremos un mensaje Toast informando de ello.

Mucho mejor que las alternativas anteriores, verdad? Pero vamos a mostrar una opción más. Si queremos que el usuario pueda ver el progreso de nuestra tarea en segundo plano, pero no queremos que interactúe mientras tanto con la aplicación tenemos la opción de mostrar la barra de progreso dentro de un diálogo. Android nos proporciona directamente un componente de este tipo en forma de un tipo especial de diálogo llamado ProgressDialog.

Configurar un cuadro de diálogo de este tipo es muy sencillo. Para ello vamos a añadir un botón más a nuestra aplicación de ejemplo, donde inicializaremos el diálogo y lanzaremos la tarea en segundo plano. Para inicializar el diálogo comenzaremos por crear un nuevo objeto ProgressDialog pasándole como parámetro 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 diálogo, en nuestro caso el mensaje "Procesando…", y el valor máximo de nuestro progreso, que lo mantendremos en 100. Por último indicaremos si deseamos que el diálogo sea cancelable, es decir, que el usuario pueda cerrarlo pulsando el botón Atrás del teléfono.
Para nuestro ejemplo activaremos esta propiedad para ver cómo podemos cancelar también nuestra tarea en segundo plano cuando el usuario cierra el diálogo. Tras la configuración del diálogo lanzaremos la AsyncTask del ejemplo anterior, que tendremos que modificar ligeramente para adaptarla al nuevo diálogo. Veamos el código por ahora:


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

En onProgressUpdate() la única diferencia será que actualizaremos el progreso llamando al método setProgress() del ProgressDialog en vez de la ProgressBar.

El código de onPreExecute() sí tendrá algún cambio más. Aprovecharemos este método para implementar el evento onCancel del diálogo, dentro del cual cancelaremos también la tarea en segundo plano llamando al método cancel(). Además, inicializaremos el progreso del diálogo a 0 y lo mostraremos al usuario mediante el método show().

Por último, en el método onPostExecute() además de mostrar el Toast de finalización, tendremos que cerrar previamente el diálogo llamando a su método dismiss().

Veamos el código completo de la AsyncTask modificada para usar el nuevo ProgressDialog.



Si ahora ejecutamos nuestro proyecto y pulsamos sobre el último botón incluido veremos cómo el diálogo aparece por encima de nuestra actividad mostrando el progreso de la tarea asíncrona. Tras finalizar, el diálogo desaparece y se muestra el mensaje toast de finalización. Si en cambio, se pulsa el botón Atrás del dispositivo antes de que la tarea termine el diálogo se cerrará y se mostrará el mensaje de cancelación.


Saludos compañeros, aprovechen la información.








viernes, 26 de diciembre de 2014

Cuadros de dialogo en Android - Notificaciones



Ya hemos visto dos de las principales alternativas a la hora de mostrar notificaciones a los usuarios de nuestras aplicaciones: los toast y las notificaciones de la barra de estado. En este último apartado sobre notificaciones vamos a comentar otro mecanismo que podemos utilizar para mostrar o solicitar información puntual al usuario. El mecanismo del que hablamos son los cuadros de diálogo.

En principio, los diálogos de Android los podremos utilizar con distintos fines, en general:

> Mostrar un mensaje.
> Pedir una confirmación rápida.
> Solicitar al usuario una elección (simple o múltiple) entre varias alternativas.

De cualquier forma, veremos también cómo personalizar completamente un diálogo para adaptarlo a cualquier otra necesidad.

El uso actual de los diálogos en Android se basa en fragmets, pero por suerte tenemos toda la funcionalidad implementada una vez más en la librería de compatibilidad android-support-v4.jar (que debe estar incluida por defecto en tu proyecto si lo has creado con una versión reciente del pluin de Eclipse) por lo que no tendremos problemas al ejecutar nuestra aplicación en versiones antiguas de Android. En este caso nos vamos a basar en la clase DialogFragment. Para crear un diálogo lo primero que haremos será crear una nueva clase que herede de DialogFragment y sobrescribiremos uno de sus métodos, onCreateDialog(), que será el encargado de construir el diálogo con las opciones que necesitemos.

La forma de construir cada diálogo dependerá de la información y funcionalidad que necesitemos. A continuación mostraré algunas de las formas más habituales.


Diálogo de Alerta

Este tipo de diálogo se limita a mostrar un mensaje sencillo al usuario, y un único botón de OK para confirmar su lectura. Lo construiremos mediante la clase AlertDialog, y más concretamente su subclase AlertDialog.Builder, de forma similar a las notificaciones de barra de estado que ya hemos comentado en el capítulo anterior. Su utilización es muy sencilla, bastará con crear un objeto de tipo AlertDialog.Builder y establecer las propiedades del diálogo mediante sus métodos correspondientes: título [setTitle()], mensaje [setMessage()] y el texto y comportamiento del botón [setPositiveButton()]. Veamos un ejemplo:


Como vemos, al método setPositiveButton() le pasamos como argumentos el texto a mostrar en el botón, y la implementación del evento onClick en forma de objeto OnClickListener. Dentro de este evento, nos limitamos a cerrar el diálogo mediante su método cancel(), aunque podríamos realizar cualquier otra acción.

Para lanzar este diálogo por ejemplo desde nuestra actividad principal, obtendríamos una referencia al Fragment Manager mediante una llamada a getSupportFragmentManager(), creamos un nuevo objeto de tipo DialogoAlerta y por último mostramos el diálogo mediante el método show() pasándole la referencia al fragment manager y una etiqueta identificativa del diálogo.


El aspecto de nuestro diálogo de alerta sería el siguiente:



Diálogo de Confirmación

Un diálogo de confirmación es muy similar al anterior, con la diferencia de que lo utilizaremos para solicitar al usuario que nos confirme una determinada acción, por lo que las posibles respuestas serán del tipo Sí/No.

La implementación de estos diálogos será prácticamente igual a la ya comentada para las alertas, salvo que en esta ocasión añadiremos dos botones, uno de ellos para la respuesta afirmativa (setPositiveButton()), y el segundo para la respuesta negativa (setNegativeButton()). Veamos un ejemplo:


En este caso, generamos a modo de ejemplo dos mensajes de log para poder verificar qué botón pulsamos en el diálogo. El aspecto visual de nuestro diálogo de confirmación sería el siguiente:



Diálogo de Selección

Cuando las opciones a seleccionar por el usuario no son sólo dos, como en los diálogos de confirmación, sino que el conjunto es mayor podemos utilizar los diálogos de selección para mostrar una lista de opciones entre las que el usuario pueda elegir.

Para ello también utilizaremos la clase AlertDialog, pero esta vez no asignaremos ningún mensaje ni definiremos las acciones a realizar por cada botón individual, sino que directamente indicaremos la lista de opciones a mostrar (mediante el método setItems()) y proporcionaremos la implementación del evento onClick() sobre dicha lista (mediante un listener de tipo DialogInterface.OnClickListener), evento en el que realizaremos las acciones oportunas según la opción elegida. La lista de opciones la definiremos como un array tradicional. Veamos cómo:


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


Este diálogo permite al usuario elegir entre las opciones disponibles cada vez que se muestra en pantalla.

Pero, ¿y si quisiéramos recordar cuál es la opción u opciones seleccionadas por el usuario para que aparezcan marcadas al visualizar de nuevo el cuadro de diálogo? Para ello podemos utilizar los métodos setSingleChoiceItems() o setMultiChiceItems(), en vez de el setItems() utilizado anteriormente. La diferencia entre ambos métodos, tal como se puede suponer por su nombre, es que el primero de ellos permitirá una selección simple y el segundo una selección múltiple, 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 método tan sólo se diferencia de setItems() en que recibe un segundo parámetro adicional que indica el índice de la opción marcada por defecto. Si no queremos tener ninguna de ellas marcadas inicialmente pasaremos el valor -1.


De esta forma conseguiríamos un diálogo como el de la siguiente imagen:


Si por el contrario optamos por la opción de selección múltiple, la diferencia principal estará en que tendremos que implementar un listener del tipo DialogInterface.OnMultiChoiceClickListener. En este caso, en el evento onClick recibiremos tanto la opción seleccionada (item) como el estado en el que ha quedado (isChecked). Además, en esta ocasión, el segundo parámetro adicional que indica el estado por defecto de las opciones ya no será un simple número entero, sino que tendrá que ser un array de booleanos.
En caso de no querer ninguna opción seleccionada por defecto pasaremos el valor null.


Y el diálogo nos quedaría de la siguiente forma:


Tanto si utilizamos la opción de selección simple como la de selección múltiple, para salir del diálogo tendremos que pulsar la tecla "Atrás" de nuestro dispositivo.


Diálogos Personalizados

Por último, vamos a comentar cómo podemos establecer completamente el aspecto de un cuadro de diálogo. Para esto vamos a actuar como si estuviéramos definiendo la interfaz de una actividad, es decir, definiremos un layout XML con los elementos a mostrar en el diálogo. En mi caso voy a definir un layout de ejemplo llamado dialog_personal.xml que colocaré como siempre en la carpeta res/layout. Contendrá por ejemplo una imagen a la izquierda y dos líneas de texto a la derecha:


Por su parte, en el método onCreateDialog() correspondiente utilizaremos el método setView() del builder para asociarle nuestro layout personalizado, que previamente tendremos que inflar como ya hemos comentado otras veces utilizando el método inflate(). Finalmente podremos incluir botones tal como vimos para los diálogos de alerta o confirmación. En este caso de ejemplo incluiremos un botón de Aceptar.


De esta forma, si ejecutamos de nuevo nuestra aplicación de ejemplo y lanzamos el diálogo 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 Diálogos), todos ellos muy útiles para cualquier tipo de aplicación y que es importante conocer bien para poder decidir entre ellos dependiendo de las necesidades que tengamos.


Saludos compañeros, aprovechen la información.









viernes, 19 de diciembre de 2014

Notificaciones de la Barra de Estado en Android



Hemos tratado ya un primer mecanismo de notificaciones disponibles en la plataforma Android: los llamados Toast. Como ya comentamos, este tipo de notificaciones, aunque resultan útiles y prácticas en muchas ocasiones, no deberíamos utilizarlas en situaciones en las que necesitemos asegurarnos la atención del usuario ya que no requiere de ninguna intervención por su parte, se muestran y desaparecen automáticamente de la pantalla.

En este nuevo apartado vamos a tratar otro tipo de notificaciones algo más 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 música 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 más 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 (más un texto que aparece durante unos segundos)…


… y un mensaje con más información al desplegar la bandeja del sistema, donde en este caso concreto se nos informa del evento que se ha producido ("Missed call"), el número de teléfono asociado, y la fecha/hora del evento. Además, al pulsar sobre la notificación se nos dirige automáticamente al historial de llamadas.


Pues bien, aprendamos a utilizar este tipo de notificaciones en nuestras aplicaciones. Vamos a construir para ello una aplicación de ejemplo, como siempre lo más sencilla posible para centrar la atención en lo realmente importante. En este caso, el ejemplo va a consistir en un único botón que genere una notificación de ejemplo en la barra de estado, con todos los elementos comentados y con la posibilidad de dirigirnos a la propia aplicación 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 librería 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 habéis creado con alguna versión 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 más recientes. La clase en cuestión se llama NotificationCompat.Builder y lo que tendremos que hacer será crear un nuevo objeto de este tipo pasándole el contexto de la aplicación y asignar todas las propiedades que queramos mediante sus métodos set().

En primer lugar estableceremos los iconos a mostrar mediante los métodos setSmallIcon() y setLargeIcon() que se corresponden con los iconos mostrados a la derecha y a la izquierda del contenido de la notificación en versiones recientes de Android. En versiones más antiguas tan sólo se mostrará el icono pequeño a la izquierda de la notificación. Además, el icono pequeño también se mostrará en la barra de estado superior.

A continuación estableceremos el título y el texto de la notificación, utilizando para ello los métodos setContentTitle() y setContentText().

Por último, estableceremos el ticker (texto que aparece por unos segundos en la barra de estado al generarse una nueva notificación) mediante setTicker() y el texto auxiliar (opcional) que aparecerá a la izquierda del icono pequeño de la notificación mediante setContentInfo(). La fecha/hora asociada a nuestra notificación se tomará automáticamente de la fecha/hora actual si no se establece nada, o bien puede utilizarse el método setWhen() para indicar otra marca de tiempo. Veamos cómo quedaría nuestro código por el momento:


El segundo paso será establecer la actividad a la cual debemos dirigir al usuario automáticamente si éste pulsa sobre la notificación. Para ello debemos construir un objeto PendingIntent, que será el que contenga la información de la actividad asociada a la notificación 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 método PendingIntent getActivity(). Por último asociaremos este objeto a la notificación mediante el métod setContentIntent() del Builder. Veamos cómo quedaría esta última parte comentada:


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


Ya estamos en disposición de probar nuestra aplicación de ejemplo. Para ello vamos a ejecutarla en el emulador de Android y pulsamos el botón que hemos implementado con todo el código anterior. Si todo va bien, debería aparecer en ese mismo momento nuestra notificación en la barra de estado, con el icono y texto definidos.

Si ahora salimos de la aplicación y desplegamos la bandeja del sistema podremos verificar el resto de información de la notificación tal como muestra la siguiente imagen:


Por último, si pulsamos sobre la notificación se debería abrir de nuevo automáticamente la aplicación de ejemplo.

En definitiva, como podéis 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.


Saludos compañeros, aprovechen la información.








Notificaciones Toast en Android



En Android existen varias formas de notificar mensajes al usuario, como por ejemplo los cuadros de diálogo modales o las notificaciones de la bandeja del sistema (o barra de estado). Pero en este apartado nos vamos a centrar en primer lugar en la forma más sencilla de notificación: los llamados Toast.

Un toast es un mensaje que se muestra en pantalla durante unos segundos al usuario para luego volver a desaparecer automáticamente sin requerir ningún tipo de actuación por su parte, y sin recibir el foco en ningún 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 rectángulo gris ligeramente translúcido. Por sus propias características, este tipo de notificaciones son ideales para mostrar mensajes rápidos y sencillos al usuario, pero por el contrario, al no requerir confirmación por su parte, no deberían utilizarse para hacer notificaciones demasiado importantes.

Su utilización es muy sencilla, concentrándose toda la funcionalidad en la clase Toast. Esta clase dispone de un método estático makeText() al que deberemos pasar como parámetro el contexto de la actividad, el texto a mostrar, y la duración del mensaje, que puede tomar los valores LENGTH_LONG o LENGTH_SHORT, dependiendo del tiempo que queramos que la notificación aparezca en pantalla. Tras obtener una referencia al objeto Toast a través de este método, ya sólo nos quedaría mostrarlo en pantalla mediante el método show().

Vamos a construir una aplicación de ejemplo para demostrar el funcionamiento de este tipo de notificaciones. Y para empezar vamos a incluir un botón que muestre un toast básico de la forma que acabamos de describir:


Si ejecutamos esta sencilla aplicación en el emulador y pulsamos el botón que acabamos de añadir veremos como en la parte inferior de la pantalla aparece el mensaje "Toast por defecto", que tras varios segundos desaparecerá automáticamente.


Como hemos comentado, éste es el comportamiento por defecto de las notificaciones toast, sin embargo también podemos personalizarlo un poco cambiando su posición en la pantalla. Para esto utilizaremos el método setGravity(), al que podremos indicar en qué zona deseamos que aparezca la notificación. La zona deberemos indicarla con alguna de las constantes definidas en la clase Gravity: CENTER, LEFT, BOTTOM, … o con alguna combinación de éstas.

Para nuestro ejemplo vamos a colocar la notificación en la zona central izquierda de la pantalla. Para ello, añadamos un segundo botón a la aplicación de ejemplo que muestre un toast con estas características:


Si volvemos a ejecutar la aplicación y pulsamos el nuevo botón 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 notificación, Android nos ofrece la posibilidad de definir un layout XML propio para toast, donde podremos incluir todos los elementos necesarios para adaptar la notificación a nuestras necesidades. Para nuestro ejemplo vamos a definir un layout sencillo, con una imagen y una etiqueta de texto sobre un rectángulo gris:


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 apartados de interfaz gráfica. Una vez construido el layout modificaremos los valores de los distintos controles para mostrar la información que queramos. En nuestro caso, tan sólo modificaremos el mensaje de la etiqueta de texto, ya que la imagen ya la asignamos de forma estática en el layout XML mediante el atributo android:src. Tras esto, sólo nos quedará establecer la duración de la notificación con setDuration() y asignar el layout personalizado al toast mediante el método setView(). Veamos cómo quedaría todo el código incluido en un tercer botón de ejemplo:



Si ejecutamos ahora la aplicación de ejemplo y pulsamos el nuevo botón, veremos como nuestro toast aparece con la estructura definida en nuestro layout personalizado.


Como podéis comprobar, mostrar notificaciones de tipo Toast en nuestras aplicaciones Android es algo de  lo más sencillo, y a veces resultan un elemento de lo más interesante para avisar al usuario de determinados eventos.


Saludos compañeros, aprovechen la información.








Utilización de Content Providers en Android



En el apartado anterior vimos cómo construir un content provider personalizado para permitir a nuestras aplicaciones Android compartir datos con otras aplicaciones del sistema. En este nuevo apartado 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. Además, veremos cómo también podemos acceder a datos del propio sistema Android (logs de llamadas, lista de contactos, agenda telefónica, bandeja de entrada de sms, etc) utilizando este mismo mecanismo.

Vamos a comenzar explicando cómo podemos utilizar el content provider que implementamos en el apartado anterior para acceder a los datos de los clientes. Para no complicar mucho el ejemplo ni hacer más difícil las pruebas y la depuración en el emulador de Android vamos a hacer uso el content provider desde la propia aplicación de ejemplo que hemos creado. De cualquier forma, el código necesario sería exactamente igual si lo hiciéramos desde otra aplicación distinta.

Utilizar un content provider ya existente es muy sencillo, sobre todo comparado con el laborioso proceso de construcción de uno nuevo. Para comenzar, debemos obtener una referencia a un Content Resolver, objeto a través del que realizaremos todas las acciones necesarias sobre el content provider. Esto es tan fácil como utilizar el método getContentResolver() desde nuestra actividad para obtener la referencia indicada.
Una vez obtenida la referencia al content resolver, podremos utilizar sus métodos query(), update(), insert() y delete() para realizar las acciones equivalentes sobre el content provider. Por ver varios ejemplos de la utilización de estos métodos añadiremos a nuestra aplicación 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 botón.

Empecemos por la consulta de clientes. El procedimiento será prácticamente igual al que vimos en los apartados 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 serán el ID, el nombre, el teléfono y el email. Tras esto, obtendremos como dijimos antes una referencia al content resolver y utilizaremos su método query() para obtener los resultados en forma de cursor. El método query() recibe, como ya vimos en el apartado anterior, la Uri del content provider al que queremos acceder, el array de columnas que queremos recuperar, el criterio de selección, los argumentos variables, y el criterio de ordenación de los resultados. En nuestro caso, para no complicarnos utilizaremos tan sólo los dos primeros, pasándole el CONTENT_URI de nuestro provider y el array de columnas que acabamos de definir.


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 más, si tienes dudas sobre cómo recorrer un cursor, puedes consultar los apartados del curso dedicados al tratamiento de bases de datos SQLite, por ejemplo éste. Veamos cómo quedaría el código:


Para insertar nuevos registros, el trabajo será también 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 método insert() pasándole la URI del content provider en primer lugar, y los datos del nuevo registro como segundo parámetro.


Por último, y más sencillo todavía, la eliminación de registros la haremos directamente utilizando el método delete() del content resolver, indicando como segundo parámetro el criterio de localización de los registros que queremos eliminar, que en este caso serán los que hayamos insertado nuevos con el segundo botón de ejemplo (aquellos con nombre = ‘ClienteN’).


Como muestra gráfica, veamos por ejemplo el resultado de la consulta de clientes (primer botón) en la aplicación 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 documentación oficial del paquete android.provider podemos consultar los datos que tenemos disponibles a través de este mecanismo, entre ellos encontramos por ejemplo: el historial de llamadas, la agenda de contactos y teléfonos, 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 teléfono, marca y descuelga igual que lo harías en un dispositivo físico. Y para emular llamadas entrantes podremos hacerlo una vez más desde Eclipse, accediendo a la vista del DDMS. En esta vista, si accedemos a la sección "Emulator Control" veremos un apartado llamado "Telephony Actions". Desde éste, podemos introducir un número de teléfono origen cualquiera y pulsar el botón "Call" para conseguir que nuestro emulador reciba una llamada entrante. Sin aceptar la llamada en el emulador pulsaremos "Hang Up" para terminar 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 añadiremos un botón más a la aplicación de ejemplo.

Consultando la documentación del content provider veremos que podemos extraer diferentes datos relacionados con la lista de llamadas. Nosotros nos quedaremos sólo con dos significativos, el número 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 método query(). 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 código:


Lo único fuera de lo normal que hacemos en el código anterior es la decodificación del valor del tipo de llamada recuperado, que la hacemos comparando el resultado con las constantes Calls.INCOMING_ TYPE (entrante), Calls.OUTGOING_TYPE (saliente), Calls.MISSED_TYPE (perdida) proporcionadas por la propia clase provider.

n último detalle importante. Para que nuestra aplicación pueda acceder al historial de llamadas del dispositivo tendremos que incluir en el fichero AndroidManifest.xml los permisos READ_CONTACTS y READ_CALL_LOG utilizando la cláusula <uses-permission> correspondiente.


Si ejecutamos la aplicación 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.


Saludos compañeros, aprovechen la información.








Construcción de Content Providers en Android



En este nuevo apartado del curso vamos a tratar el temido [o a veces incomprendido] tema de los Content Providers.

Un Content Provider no es más que el mecanismo proporcionado por la plataforma Android para permitir compartir información entre aplicaciones. Una aplicación que desee que todo o parte de la información que almacena esté disponible de una forma controlada para el resto de aplicaciones del sistema deberá proporcionar un content provider a través del cual se pueda realizar el acceso a dicha información. Este mecanismo es utilizado por muchas de las aplicaciones estándar de un dispositivo Android, como por ejemplo la lista de contactos, la aplicación de SMS, o el calendario/agenda. Esto quiere decir que podríamos 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 bibliografía sobre programación 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 después pasar a la construcción 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 más dentro de nuestras aplicaciones. Así, en este primer apartado sobre el tema veremos cómo crear nuestro propio content provider para compartir datos con otras aplicaciones, y en el próximo apartado veremos cómo utilizar este mecanismo para acceder directamente a datos de terceros.

Empecemos a entrar en materia. Para añadir un content provider a nuestra aplicación 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 aplicación tendrá que contar previamente con algún método de almacenamiento interno para la información que queremos compartir. Lo más común será disponer de una base de datos SQLite, por lo que será esto lo que utilizaré para todos los ejemplos de este apartado, pero internamente podríamos tener los datos almacenados de cualquier otra forma, por ejemplo en ficheros de texto, ficheros XML, etc. El content provider será el mecanismo que nos permita publicar esos datos a terceros de una forma homogénea y a través 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 unívoca del resto de registros. Como ejemplo, los registros devueltos por un content provider de clientes podría tener este aspecto:


Sabiendo esto, es interesante que nuestros datos también cuenten internamente con este campo _ID (no tiene por qué llamarse igual) de forma que nos sea más sencillo después generar los resultados del content provider.

Con todo esto, y para tener algo desde lo que partir, vamos a construir en primer lugar una aplicación 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 apartados dedicados al tratamiento de bases de datos SQLite en Android (consultar índice del curso).

Por volver a recordarlo muy brevemente, lo que haremos será crear una nueva clase que extienda a SQLiteOpenHelper, definiremos las sentencias SQL para crear nuestra tabla de clientes, e implementaremos finalmente los métodos onCreate() y onUpgrade(). El código de esta nueva clase, que yo he llamado ClientesSqliteHelper, quedaría como sigue:


Como notas relevantes del código anterior:

*Nótese el campo "_id" que hemos incluido en la base de datos de clientes por lo motivos indicados un poco más arriba. Este campo lo declaramos como INTEGER PRIMARY KEY AUTOINCREMENT, de forma que se incremente automáticamente cada vez que insertamos un nuevo registro en la base de datos.

* En el método onCreate(), además de ejecutar la sentencia SQL para crear la tabla Clientes, también inserta varios registros de ejemplo.
* Para simplificar el ejemplo, el método onUpgrade() se limita a eliminar la tabla actual y crear una nueva con la nueva estructura. En una aplicación real habría que hacer probablemente la migración 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 aplicación principal de ejemplo no mostrará en principio nada en pantalla ni hará nada con la información. Esto lo he decidido así para no complicar el código de la aplicación innecesariamente, ya que no nos va a interesar el tratamiento directo de los datos por parte de la aplicación principal, sino su utilización a través del content provider que vamos a construir.

Una vez que ya contamos con nuestra aplicación 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 más 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 dirección "http://www.sgoliver.net", para acceder a un content provider utilizaremos una dirección similar a:

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

Las direcciones URI de los content providers están 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, también llamado authority. Dado que este dato debe ser único es una buena práctica 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 haría indicando al final de la URI el ID de dicho registro. Por ejemplo la uri "content://net.sgoliver.android.contentproviders/clientes/23" haría 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 están solicitando. Esto lo veremos un poco más adelante.

Sigamos. El siguiente paso será extender a la clase ContentProvider.

Si echamos un vistazo a los métodos 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 serán los métodos que permitirán acceder a los datos (consulta, inserción, modificación y eliminación, respectivamente) y por último, el método getType() permitirá conocer el tipo de datos devueltos por el content provider (más tarde intentaremos explicar algo mejor esto último).

Además de implementar estos métodos, también definiremos una serie de constantes dentro de nuestra nueva clase provider, que ayudarán posteriormente a su utilización. 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

Además, para seguir la práctica habitual de todos los content providers de Android, encapsularemos además esta dirección en un objeto estático de tipo Uri llamado CONTENT_URI.


A continuación 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 estándar están definidas en la clase BaseColumns, por lo que para añadir las nuevas columnas de nuestro content provider definiremos una clase interna pública tomando como base la clase BaseColumns y añadiremos nuestras nuevas columnas


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


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 genérica o a un registro concreto a través de su ID. Por ejemplo:

* content://net.sgoliver.android.contentproviders/clientes –> Acceso genérico a tabla de clientes

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

Para conseguir esto definiremos también como miembro de la clase un objeto UriMatcher y dos nuevas constantes que representen los dos tipos de URI que hemos indicado: acceso genérico a tabla (lo llamaré CLIENTES) o acceso a cliente por ID (lo llamaré CLIENTES_ID). A continuación inicializaremos el objeto UriMatcher indicándole el formato de ambos tipos de URI, de forma que pueda diferenciarlos y devolvernos su tipo (una de las dos constantes definidas, CLIENTES o CLIENTES_ID).


En el código anterior vemos como mediante el método 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. Más tarde veremos cómo utilizar esto de forma práctica.

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

El primero de ellos es onCreate(). En este método nos limitaremos simplemente a inicializar nuestra base de datos, a través de su nombre y versión, y utilizando para ello la clase ClientesSqliteHelper que creamos al principio del apartado.


La parte interesante llega con el método query(). Este método recibe como parámetros una URI, una lista de nombres de columna, un criterio de selección, una lista de valores para las variables utilizadas en el criterio anterior, y un criterio de ordenación. Todos estos datos son análogos a los que comentamos cuando tratamos la consulta de datos en SQLite para Android, apartado que recomiendo releer si no tenéis muy frescos estos conocimientos. El método query deberá devolver los datos solicitados según la URI indicada y los criterios de selección y ordenación pasados como parámetro. 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 genérico a la tabla de clientes habrá que realizar la consulta SQL correspondiente a la base de datos respetando los criterios pasados como parámetro.

Para distinguir entre los dos tipos de URI posibles utilizaremos como ya hemos indicado el objeto uriMatcher, utilizando su método match(). Si el tipo devuelto es CLIENTES_ID, es decir, que se trata de un acceso a un cliente concreto, sustituiremos el criterio de selección por uno que acceda a la tabla de clientes sólo por el ID indicado en la URI. Para obtener este ID utilizaremos el método getLastPathSegment() del objeto uri que extrae el último elemento de la URI, en este caso el ID del cliente.

Hecho esto, ya tan sólo queda realizar la consulta a la base de datos mediante el método query() de SQLiteDatabase. Esto es sencillo ya que los parámetros son análogos a los recibidos en el método query() del content provider.


Como vemos, los resultados se devuelven en forma de objeto Cursor, una vez más exactamente igual a como lo hace el método query() de SQLiteDatabase.

Por su parte, los métodos update() y delete() son completamente análogos a éste, con la única diferencia de que devuelven el número de registros afectados en vez de un cursor a los resultados. Vemos directamente el código:


El método 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 método insert() de SQLiteDatabase, y posteriormente construiremos la nueva URI mediante el método auxiliar ContentUris.withAppendedId() que recibe como parámetro la URI de nuestro content provider y el ID del nuevo elemento.


Por último, tan sólo nos queda implementar el método getType(). Este método se utiliza para identificar el tipo de datos que devuelve el content provider. Este tipo de datos se expresará como un MIME Type, al igual que hacen los navegadores web para determinar el tipo de datos que están recibiendo tras una petición 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 más existirán 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 implementación del método getType() quedaría como sigue:


Como se puede observar, utilizamos una vez más el objeto UriMatcher para determinar el tipo de URI que se está solicitando y en función de ésta devolvemos un tipo MIME u otro.

Pues bien, con esto ya hemos completado la implementación del nuevo content provider. Pero aún nos queda un paso más, como indicamos al principio del apartado. Debemos declarar el content provider en nuestro fichero AndroidManifest.xml de forma que una vez instalada la aplicación 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.


Ahora sí hemos completado totalmente la construcción de nuestro nuevo content provider mediante el cual otras aplicaciones del sistema podrán acceder a los datos almacenados por nuestra aplicación.

En el siguiente apartado veremos cómo utilizar este nuevo content provider para acceder a los datos de nuestra aplicación de ejemplo, y también veremos cómo 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.


Saludos compañeros, aprovechen la información.









       
free counters

Páginas vistas en total según Google