Servicios Web Android

Preview only show first 6 pages with water mark for full document please download

Transcript

Servicios Web Android Índice 1 Introducción..................................................................................................................2 2 Ejercicios...................................................................................................................... 2 2.1 Listado de libros a partir de servicios web (0,5 puntos).......................................... 2 2.2 Obteniendo las portadas de los libros (0,5 puntos).................................................. 3 2.3 Añadir un libro (0,5 puntos).....................................................................................5 2.4 Búsqueda de libros (modificar el adaptador de la Base de Datos) (0,5 puntos)...... 5 2.5 Búsqueda de libros (mostrar el diálogo de búsqueda) (0,5 puntos).........................6 2.6 Búsqueda de libros (mostrando los resultados) (0,5 puntos)................................... 7 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android 1. Introducción En esta sesión del proyecto de integración vamos a aplicar lo aprendido durante las sesiones del módulo de servicios web en el proyecto de Android. Concretamente utilizaremos servicios web para acceder a una base de datos remota de libros, de tal forma que desde nuestra aplicación podremos listarlos y añadir libros nuevos. En esta sesión también veremos cómo implementar la opción de búsqueda en nuestra aplicación. 2. Ejercicios Una vez terminados los ejercicios podremos obtener el listado de libros y añadir nuevos libros usando los servicios web de jtech. También será posible buscar libros por el título o por el ISBN. 2.1. Listado de libros a partir de servicios web (0,5 puntos) En este primer ejercicio vas a utilizar el código que desarrollaste en el primer ejercicio de la segunda sesión del módulo de Servicios Web. En dicho ejercicio debías acceder al servicio de listado de libros y rellenar una tabla. Incorpora el código en tu proyecto de tal forma que lo que se rellene sea el ListView de la actividad Listado con la información de los libros en el servidor jtech. Nota: Recuerda que en la sesión anterior del proyecto con Android hicimos uso de la persistencia para que el acceso al listado remoto de libros se hiciera tan sólo la primera vez que se accediera al listado en nuestra aplicación. Esto se debe mantener en esta nueva versión, pero en lugar de cargar los libros del objeto singleton lo haremos del servicio web. Conéctate al servicio para inicializar la base de datos de la aplicación con el listado de libros y usa los libros de la base de datos para mostrar finalmente el listado en pantalla. Para comprobar si la BD está vacía, en el método onCreate, tras obtener el cursor, miraremos si ha devuelto algún registro. En caso de que no haya devuelto ninguno, deberemos poner en marcha la tarea de descarga de libros del servidor. if (items.getCount() <= 0) { // Lanza la tarea del servicio web ... } Cuando la tarea haya finalizado de descargar los libros, y los haya insertado en la base de datos (utilizando el adaptador creado en la sesión anterior), deberemos recargar el cursor y notificar al adapter que los datos han cambiado y por lo tanto debe recargar la lista en pantalla: 2 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android items.requery(); la.notifyDataSetChanged(); 2.2. Obteniendo las portadas de los libros (0,5 puntos) En este ejercicio modificaremos nuestro código para mostrar la portada de los libros en la actividad de listado, sólo para aquellos libros que efectivamente tengan portada. Para aquellos que no tengan, por defecto mostraremos el drawable logo.png que tenemos empaquetado en la aplicación. El servicio utilizado en el ejercicio anterior no nos proporciona la portada de los libros, así que va a ser necesario ir accediendo a los libros uno por uno. Para hacer esto sin sobrecargar la red, utilizaremos la descarga lazy de imágenes, tal como vimos en el tercer ejercicio de la primera sesión del módulo de acceso a servicios web. Tampoco estamos almacenando las portadas de los libros en la base de datos ni en ficheros locales, así que tendremos que conectarnos a los servicios jtech cada vez que accedamos al listado para mostrarlas. No es la mejor solución pero es la que usaremos en este ejercicio para simplificar (como mejora podríamos guardar las imágenes en un fichero e intentar recuperarlas de ahí en futuros accesos, pero no es necesario para este ejercicio). En primer lugar vamos a añadir un método a la actividad listado que dado un ISBN nos proporcione un objeto Bitmap correspondiente a la portada del mismo, en el caso en el que éste efectivamente la tenga. El código de la función podría ser el siguiente: public Bitmap getPortadaLibro(String isbn) { Bitmap portada = null; String urlToSendRequest = "http://server.jtech.ua.es/jbib-rest/resources/libros/" + isbn + "/imagen"; DefaultHttpClient httpClient = new DefaultHttpClient(); HttpGet httpget = new HttpGet(urlToSendRequest); HttpResponse response; try { response = httpClient.execute(httpget); portada = BitmapFactory.decodeStream(response.getEntity() .getContent()); } catch (Exception e) { e.printStackTrace(); } finally { httpClient.getConnectionManager().shutdown(); } return portada; } Obsérvese que estamos accediendo a un servicio específico que nos dará la portada de un libro a partir de su ISBN. El objeto Bitmap se obtiene a partir de la respuesta del servicio mediante la clase BitmapFactory. La función getPortadaLibro devolverá null en el 3 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android caso en el que el libro no tenga portada. Como hemos comentado anteriormente, accederemos al servicio en jtech cada vez que vayamos a mostrar el libro en la actividad de listado de libros. Por lo tanto, el sitio ideal para cargar la portada es en el método getView del adaptador que hayamos usado para poblar el ListView de elementos. Pero en lugar de llamar al método getPortadaLibro directamente, lo cual podría causar latencia, cargaremos cada portada individual mediante un AsyncTask. El AsyncTask deberá recibir el ISBN del libro. class CargarImagenTask extends AsyncTask { Bitmap portada; @Override protected Bitmap doInBackground(String... params) { return getPortadaLibro(params[0]); } @Override protected void onPostExecute(Bitmap result) { if (result != null) { this.portada = result; la.notifyDataSetChanged(); } } public Bitmap getPortada() { return portada; } } En el método doInBackground del AsyncTask será desde donde llamaremos a getPortadaLibro pasando como parámetro el ISBN. Cuando termine la descarga de forma correcta, en onPostExecute se guarda la portada descargada en una propiedad de la tarea, y se notifica al adapter que los datos han cambiado para que recargue y muestre la imagen. Deberemos llevar un control de las tareas de descarga activas y completadas, para evitar poner a descargar la misma imagen dos veces. Esto podemos hacerlo añadiendo un mapa de imágenes en descarga, y sólo lanzar la tarea de descarga si la imagen no se está descargando ya, tal como se hizo en el módulo de acceso a servicios web. En este caso, dado que no tenemos los libros encapsulados en un objeto Libro, sino que estamos accediendo a ellos mediante un cursor, podríamos indexar el mapa de imagenes en descarga mediante el ISBN directamente. Map imagenesCargando; Por lo tanto, en getView deberemos comprobar si hay una tarea de descarga activa o completada para el ISBN actual. En caso de no haberla, mostraremos el drawable por defecto y lanzaremos la tarea, añadiéndola al mapa. En caso de que la haya, comprobaremos si está activa o completada (si getPortada nos devuelve null o no). En caso de tener la imagen ya descargada, la mostramos en el ImageView, y en caso contrario mostraremos el drawable por defecto. 4 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android Nota Para que el tamaño de las imágenes en la lista sea constante, haremos que el ImageView tenga una altura y una anchura fija de 100dp. Esto deberemos definirlo en el layout de los items de la lista. 2.3. Añadir un libro (0,5 puntos) En este tercer ejercicio vamos a hacer uso una vez más de código desarrollado en sesiones anteriores, en concreto el código del primer ejercicio de la tercera sesión del módulo de Servicios Web. Incorpora el código de dicho ejercicio en tu aplicación de tal forma que desde ella sea posible añadir nuevos libros. Nota: Recuerda que al añadir un nuevo libro éste también debe ser insertado en la base de datos local de la aplicación. En la clase EditarLibro sólo modificaremos la parte referente a altas, no a modificaciones. Al pulsar el botón de guardar un nuevo libro lo que haremos es lanzar una tarea (AsyncTask) que realice el alta del libro, y cuando finalice la tarea (en onPostExecute), si el código de respuesta es 201 (el libro se ha añadido) lo insertaremos en la base de datos y finalizaremos la actividad. En caso de haber un error, simplemente mostraremos un toast con el mensaje de error. 2.4. Búsqueda de libros (modificar el adaptador de la Base de Datos) (0,5 puntos) En los siguientes ejercicios vamos a implementar las opciones de búsqueda del menú de la actividad de listado de libros. Para ello haremos uso de una ventana de diálogo manejada por Android para realizar este tipo de tareas. Para poder activar la búsqueda necesitamos los siguientes componentes: • Una configuración de búsqueda: se trata de un fichero XML que permite establecer el valor de determinados parámetros relacionados con el diálogo de búsqueda. • Una actividad de búsqueda: se trata de la actividad que recibirá el Intent que lanzará el diálogo de búsqueda junto con la cadena a buscar. • Una interfaz de búsqueda: puede ser el diálogo de búsqueda que se ha comentado anteriormente, o una vista especial que podemos introducir en la interfaz. En nuestro caso utilizaremos la primera alternativa. La documentación para la creación y utilización del cuadro de búsqueda de Android se puede encontrar en la siguiente dirección: http://developer.android.com/guide/topics/search/search-dialog.html 5 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android Comenzaremos por añadir dos métodos adicionales a la clase AdaptadorLibros llamados queryNombre y queryIsbn que recibirán como parámetro una cadena. Ambos deberán devolver un objeto de la clase Cursor apuntando a todos aquellos libros de la base de datos cuyo título o ISBN contengan, respectivamente, la cadena recibida como parámetro. Nota: En SQL podemos utilizar el operador like junto con el carácter comodin % en una cláusula WHERE. Por ejemplo: SELECT * FROM libros WHERE titulo LIKE "%titulo%". Es importante no olvidar poner dicho comodín. Por ejemplo, para la búsqueda por título en la query podemos poner como selección "titulo like ?" y como argumento de la selección new String[]{ "%" + titulo + "%"}. 2.5. Búsqueda de libros (mostrar el diálogo de búsqueda) (0,5 puntos) El siguiente paso será crear el archivo /res/xml/searchable.xml que contendrá la configuración de la búsqueda. El fichero debe contener un único elemento searchable, cuyo único atributo obligatorio es android:label. Debe apuntar al recurso de tipo cadena que contenga el nombre de la aplicación. El resto de atributos los puedes consultar en http://developer.android.com/guide/topics/search/searchable-config.html#searchable-element. Ahora vamos a modificar la declaración de nuestra actividad de listado de libros para que sea también una actividad de búsqueda. Esta actividad será la que recibirá el Intent con la cadena de búsqueda. Dentro del Manifest de la aplicación deberemos añadir el siguiente contenido al elemento activity correspondiente: A continuación, y también dentro del Manifest de la aplicación, deberemos indicar cuál será la actividad desde la cual se podrá mostrar el diálogo de búsqueda. En nuestro caso concreto será la propia actividad de listado de libros, por lo que dentro de su elemento activity en el Manifest añadimos lo siguiente (el atributo android:value en este caso indica qué actividad recibirá la búsqueda): Por fin podemos incluir una llamada al método onSearchRequested() desde las 6 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android opciones correspondientes (búsqueda por título y búsqueda por ISBN) del menú contextual de la actividad de búsqueda de libros. Comprueba que al incluir esta llamada se muestra correctamente el diálogo de búsqueda y que éste nos vuelve a dirigir a la actividad de listado de libros (aunque todavía no se muestren los resultados de la búsqueda). 2.6. Búsqueda de libros (mostrando los resultados) (0,5 puntos) Como vamos a proporcionar la posibilidad de realizar una búsqueda basada en dos criterios (título o ISBN) debemos establecer algún mecanismo para indicar a la actividad que recibe el Intent de la búsqueda (en nuestro caso la actividad de búsqueda de libros) cuál de estos dos criterios es el que se está utilizando. Para conseguir esto debemos sobrecargar el método onSearchRequested dentro de la actividad desde la que se lanza la búsqueda (que en nuestro caso es, también, la actividad de listado de libros). Dentro de este método inicializaremos un objeto Bundle que se pasará junto al Intent al hacer la búsqueda. El código quedaría de la siguiente forma: @Override public boolean onSearchRequested() { Bundle appData = new Bundle(); appData.putBoolean(Listado.BUSCAR_TITULO, buscarTitulo); startSearch(null, false, appData, false); return true; } Nota: El parámetro Listado.BUSCAR_TITULO es una cadena definida como pública y estática en la actividad de listado de libros. Su función es la de identificar el dato que se pasa a la actividad que recibirá el Intent de la búsqueda. El parámetro buscarTitulo, por otra parte, es un atributo booleano de esa misma actividad. No olvides cambiar el valor a true o false dependiendo de cuál fuera la opción de búsqueda seleccionada, antes de llamar a onSearchRequested. Por último haremos que la actividad de listado de libros muestre el resultado de la búsqueda, en caso de que dicha actividad fuera invocada desde el diálogo de búsqueda. Al ejecutar la búsqueda se lanzará una nueva instancia de la actividad Listado. Por lo tanto, en su método onCreate deberemos comprobar si la actividad ha sido lanzada de forma normal, o por una petición de búsqueda. Esto lo haremos comprobando si la acción del Intent que la lanzó es ACTION_SEARCH. En este caso, podemos extraer del Intent la query y también el parámetro que le pasamos indicando si se busca por título o por ISBN: if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA); boolean buscarTitulo = true; if (appData != null) { buscarTitulo = appData.getBoolean(Listado.BUSCAR_TITULO); } 7 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android // Crear el cursor a partir de la query oportuna ... } else { // Crear el cursor a partir de la lista completa de libros ... } Nota Si nuestra actividad fuese de tipo singleTop, singleTask, o singleInstance, al estar ya en la actividad cuando se solicita la búsqueda no se abriría una nueva instancia. En estos casos la búsqueda se debería realizar en el método onNewIntent, que es el que se ejecutaría al recibir el Intent de petición de búsqueda sobre una actividad ya creada. En caso de que la actividad se cree de la forma normal (sin recibir Intent de solicitud de búsqueda), ejecutaremos el código que teníamos anteriormente, en el que el cursor se creaba a partir de la lista de todos los libros. 8 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved. Servicios Web Android 9 Copyright © 2012-13 Dept. Ciencia de la Computación e IA All rights reserved.