Buscar (Don't Edit)

miércoles, 9 de octubre de 2013

Android: Conexión JTDS de manera asíncrona con AsyncTask

¡Que tal! Regresamos a las andadas con android. Ya tenía bastante tiempo desde que no publicábamos algo sobre el sistema operativo de Google, pero déjenme presumirles contarles que en mi trabajo ya me han dado luz verde para empezar a trabajar sobre proyectos android, por lo que estaré mas en contacto con él y poder publicar algún truquito de vez en cuando (espero). Por lo pronto, los invitó a que lean lo que les traigo tras el salto...

Primero veamos el porque realizar tareas de manera asíncrona. En android existe el thread o hilo principal de la aplicación, sobre el cuál corren las activities, y el cuál tras un tiempo de congelamiento es abortado por el sistema operativo. Si nosotros quisiéramos por ejemplo descargar algún archivo de internet mediante la aplicación, consultar las noticias de un sitio web mediante RSS(el próximo tutorial que haga será sobre esto), o como en este caso conectarnos a una base de datos remota, es decir, todas aquellas tareas que impliquen un proceso de carga y que involucran tiempos de espera y quisiéramos ejecutarlos en el hilo principal sería abortado por android, por lo que tenemos a fuerza que ejecutarlo en un hilo diferente, es decir, en segundo plano. Además de que al hacerlo de ésta manera tendremos una aplicación mucho mas ágil, dinámica y veloz.

La clase AsyncTask
Android cuenta en su API con la clase AsyncTask, la cual nos permitirá realizar tareas en segundo plano de manera fácil y sencilla.
La clase AsyncTask funciona tal cual se ve en la imagen, donde el método doInBackground es el que realiza la tarea asíncrona, precedido del método onPreExecute con el cual podemos realizar acciones antes de que se ejecute la tarea y seguido de onPostExecute que se ejecuta al finalizar doInBackground y que recibe los resultados de éste. También tenemos el método onProgressUpdate, el cuál se ejecuta a la par de doInBackgorund y con el cuál podríamos, por ejemplo, utilizar para mostrar en pantalla el progreso de la tarea.

Para empezar a utilizar AsyncTask, tendremos que crear una InnerClass dentro de nuestra activity, en mi caso lo haré en el MainActivity y la llamaré GetDBConnection (¡Que creativo soy!) y haré que esta clase herede de AsyncTask.
A continuación me pedirá implementar los métodos de la clase AsyncTask, de hecho el único método que debemos implementar de manera obligatoria es doInBackground, aunque nosotros implementaremos también el método onPostExecute el cuál recibe el resultado de doInBackground. Entonces solo los implementamos y listo.

Antes de continuar me gustaría explicarles las parámetrizaciones que recibe la clase AsyncTask<Params, Progress, Result>
Params.- Es el tipo de parámetros que queremos que reciba nuestra clase para ejecutar la tarea.
Progress .- El tipo de parámetro de las unidades de progreso, normalmente de tipo entero y que nos indican el progreso de nuestra tarea. Útil al implementar el método onProgressUpdate.
Result .- Es el tipo de parámetro devuelto como resultado de la tarea hecha en doInBackgournd y que recibe el método onPostExecute.

La conexión!!! La conexión!!!
Bueno, ahora que ya tenemos preparado nuestro método asíncrono, es hora de ponerlo a trabajar. Para ello haremos una conexión a una base de datos SQL Server. 

1.- Para ello lo primero que haremos será leer este post, y ahora que ya sabemos como conectar una aplicación android a SQL Server nos vamos al segundo paso.

2. Lo segundo será crear una base de datos de ejemplo (db_ejemplo) y una tabla llamada Persona con la siguiente información:
3. Crearemos una clase SimpleDAO, que maneje nuestras consultas a la base de datos y cuyo método construirLector recibe nuestra query como parámetro y nos devuelve un ResultSet hidratado con la información.
4. Ahora crearemos la clase PersonaDAO como un Singletonque hereda de SimpleDAO y donde prepararemos todas las sentencias que hagamos sobre la tabla Persona.

Como pueden observar el método getNombrePersona recibe como parámetro el id de la persona en base de datos y es éste mismo método quien se encarga de preparar la query y llamar al método construirLector de su superclase SimpleDAO quien devuelve un ResultSet el cual solamente tenemos que leer y devolver el valor obtenido.

5.- A continuación regresamos a nuestra MainActivity y en el método doInBackground de nuestra clase GetDBConnection simplemente llamaremos al método getNombrePersona de PersonaDAO (ya deben saber cómo llamar a una clase Singleton) y le pasaremos el primer parámetro de nuestra serie de parámetros (en seguida veremos como funciona esto al momento de llamar a nuestra clase GetDBConnection en el paso 6).
También añadiremos en el método onPostExecute un Toast que nos imprima en pantalla el resultado devuelto por doInBackground, que en este caso es el nombre de la persona.
6. Listo, ahora para ejecutar nuestra tarea asíncrona solo debemos instanciar nuestra clase GetDBConnection y llamar a su método execute.
Fíjense como al método execute le paso como argumento el número 1, si se fijan en los parámetros del método doInBackground(Integer... params) se darán cuenta que es un arreglo de enteros, por lo que fácilmente podríamos enviarle varios argumentos, aunque en este caso como solamente estamos enviando un único entero como argumento, por eso es que al momento de obtener el parámetro desde doInBackground lo hacemos de la siguiente manera params[0].

7.- Ahora si, al correr la aplicación les deberá mostrar un mensaje en pantalla con el nombre de la persona en Base de Datos con id=1, que en mi caso sería así.

Con esto nosotros podríamos hacer que nuestra aplicación ejecute las consultas que deseemos de nuestra base de datos (o descargar ficheros, consultar datos por internet, etc.) sin que esto afecte el hilo principal de nuestra aplicación, ya que se estarían ejecutando en segundo plano. De ésta manera tendremos una aplicación ágil y también permitiríamos que el usuario realice otras acciones en nuestra aplicación mientras se ejecutan otras tareas al mismo tiempo.

Espero les sirva de ayuda. Hasta la próxima! ;)

4 comentarios:

  1. Buen dia

    Necesito de una gran ayuda

    Tengo un formulario el cual quiero que los parametros finales como Nombre, edad, correo electronico, se envien a una base de datos de mysql de manera remota...Podrian ayudarme??

    ResponderBorrar
  2. Muy buenos ejemplos.
    Continue para que siga aprendiendo

    ResponderBorrar
  3. Buenas Noches,

    Se me cierra antes de mostrar el Mensaje del TOAST y al hacer un Debug en Android Studio me lanza este error:
    java.sql.SQLException: I/O Error: DB server closed connection.

    No sé cual sea el error, talvez puedas ayudarme



    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: Process: tk.plasticoshercules.yimyh.sqlserver, PID: 15145
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: java.lang.RuntimeException: An error occured while executing doInBackground()
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at android.os.AsyncTask$3.done(AsyncTask.java:300)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.FutureTask.run(FutureTask.java:242)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.lang.Thread.run(Thread.java:818)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'java.sql.Statement java.sql.Connection.createStatement()' on a null object reference
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at tk.plasticoshercules.yimyh.sqlserver.SimpleDAO.construirLector(SimpleDAO.java:18)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at tk.plasticoshercules.yimyh.sqlserver.ClienteDAO.getCliente(ClienteDAO.java:29)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at tk.plasticoshercules.yimyh.sqlserver.MainActivity$GetDBConnection.doInBackground(MainActivity.java:27)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at tk.plasticoshercules.yimyh.sqlserver.MainActivity$GetDBConnection.doInBackground(MainActivity.java:24)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at android.os.AsyncTask$2.call(AsyncTask.java:288)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
    10-20 01:58:45.760 15145-15174/tk.plasticoshercules.yimyh.sqlserver E/AndroidRuntime: at java.lang.Thread.run(Thread.java:818) 

    ResponderBorrar