Bienvenidos sean a este post, hoy veremos como usar a Asynctask con las modificaciones realizadas en el post anterior, como hemos hablado en otros casos Android no recomienda usar la conexion a internet o la red por medio del hilo principal pero gracias al StrictMode pudimos evitar que nos impida usarlo pero tenemos un solo inconveniente, el metodo listaPuntuaciones trabaja de forma sincrona lo que significa que debemos esperara hasta obtener una respuesta, y como vimos en otros casos dejando pendiente la tarea y no permitiendo que el metodo vuelva inmediatamente. Una posible solucion hubiera sido agregar una funcion X donde arrancaria una tarea para descargar el resultado pero nos devolveria el control inmediatamente sin devolver un resultado, y este metodo podria disparar otro evento cuando ya dispusieramos de las puntuaciones. Vamos a tomar el ejercicio modificado del post anterior y transformaremos a listaPuntuaciones para trabajar de forma “asincrona” pero la realidad no va a ser tan asi para ello vamos a abrir de nuevo a Asteroides y crearemos una nueva clase llamada AlmacenPuntuacionesSW_PHP_AsyncTask, una vez generado reemplazaremos el codigo generado por el siguiente:

Anuncios
package org.example.asteroides;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Vector;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AlmacenPuntuacionesSW_PHP_AsyncTask implements AlmacenPuntuaciones {
    private Context contexto;

    public AlmacenPuntuacionesSW_PHP_AsyncTask(Context contexto){
        this.contexto=contexto;
    }

    @Override
    public Vector<String> listaPuntuaciones(int cantidad){
        try{
            TareaLista tarea = new TareaLista();
            tarea.execute(cantidad);
            return tarea.get(4, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            Toast.makeText(contexto,"Tiempo Excedido de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (CancellationException e){
            Toast.makeText(contexto,"Error de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (Exception e){
            Toast.makeText(contexto,"Error con la tarea asincrona",
                    Toast.LENGTH_SHORT).show();
        }
        return new Vector<String>();
    }

    private class TareaLista extends AsyncTask<Integer,Void, Vector<String>>{

        @Override
        protected Vector<String> doInBackground(Integer... cantidad){
            Vector<String> resultado = new Vector<String>();
            URL url = null;
            try {
                url = new URL("http://172.128.10.9/lista.php"
                        +"?max=" + cantidad[0]);            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            HttpURLConnection conexion = null;
            try {
                conexion = (HttpURLConnection) url.openConnection();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try{
                if (conexion.getResponseCode() == HttpURLConnection.HTTP_OK){
                    BufferedReader lector = new BufferedReader(new
                            InputStreamReader(conexion.getInputStream()));
                    String linea = lector.readLine();
                    while (!linea.equals("")){
                        resultado.add(linea);
                        linea = lector.readLine();
                    }
                    lector.close();
                } else {
                    Log.e("Asteroides", conexion.getResponseMessage());
                }
            } catch (Exception e){
                Log.e("Asteroides", e.getMessage(),e);
            } finally {
                conexion.disconnect();
            }
            return resultado;
        }

    }

    @Override
    public void guardarPuntuacion(int puntos, String nombre, long fecha) {
        try{
            TareaGuardar tarea = new TareaGuardar();
            tarea.execute(String.valueOf(puntos),nombre,
                    String.valueOf(fecha));
            tarea.get(4, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            Toast.makeText(contexto,"Tiempo Excedido de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (CancellationException e){
            Toast.makeText(contexto,"Error de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (Exception e){
            Toast.makeText(contexto,"Error con la tarea asincrona",
                    Toast.LENGTH_SHORT).show();
        }
    }

    private class TareaGuardar extends AsyncTask<String,Void,Void>{
        @Override
        protected Void doInBackground(String... param){
            try{
                URL url = new URL("http://172.128.10.9/nuevo.php"
                        + "?puntos=" + param[0]
                        + "&nombre=" + URLEncoder.encode(param[1], "UTF-8")
                        + "&fecha=" + param[2]);
                HttpURLConnection conexion = (HttpURLConnection) url
                        .openConnection();
                try{
                    if (conexion.getResponseCode()==HttpURLConnection.HTTP_OK) {
                        BufferedReader lector = new BufferedReader(
                                new InputStreamReader(conexion.getInputStream()));
                        String linea = lector.readLine();
                        if (!linea.equals("Ok.")){
                            Log.e("Asteroides", "Error en servicio web nuevo");
                        }
                    } else {
                        Log.e("Asteroides", conexion.getResponseMessage());
                    }
                } catch (Exception e){
                    Log.e("Asteroides", e.getMessage(),e);
                } finally {
                    conexion.disconnect();
                }
            } catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }
}

En este caso tenemos varias modificaciones importantes, la primera es un objeto de tipo Context llamado contexto, luego tendremos un constructor para nuestra clase donde recibiremos el contexto, nuestro siguiente paso sera analizar los metodos que debemos redefinir para ello veremos a listaPuntuaciones():

Anuncios
    @Override
    public Vector<String> listaPuntuaciones(int cantidad){
        try{
            TareaLista tarea = new TareaLista();
            tarea.execute(cantidad);
            return tarea.get(4, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            Toast.makeText(contexto,"Tiempo Excedido de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (CancellationException e){
            Toast.makeText(contexto,"Error de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (Exception e){
            Toast.makeText(contexto,"Error con la tarea asincrona",
                    Toast.LENGTH_SHORT).show();
        }
        return new Vector<String>();
    }

Nuestra principal diferencia con respecto al post anterior es que en lugar de trabajar con la pagina PHP utilizaremos un bloque try/catch y en este crearemos un objeto llamado tarea del tipo TareaLista, del cual hablaremos despues, a dicho objeto le diremos que utilice execute() y enviara el valor de cantidad para finalmente devolver a tarea y por medio de get() le daremos un retardo de 4 seg., el primer catch evaluara si ocurrio un timeout y en caso de ocurrir nos mostrara un mensaje notificandolo, lo mismo ocurre con el siguiente catch donde monitorea si se cancelo el pedido u ocurrio un error en todos los casos nos mostrara una notificacion por medio de Toast del error pertinente, por ultimo retornara un valor de tipo Vector, veamos a la clase TareaLista:

    private class TareaLista extends AsyncTask<Integer,Void, Vector<String>>{

        @Override
        protected Vector<String> doInBackground(Integer... cantidad){
            Vector<String> resultado = new Vector<String>();
            URL url = null;
            try {
                url = new URL("http://172.128.10.9/lista.php"
                        +"?max=" + cantidad[0]);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            HttpURLConnection conexion = null;
            try {
                conexion = (HttpURLConnection) url.openConnection();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try{
                if (conexion.getResponseCode() == HttpURLConnection.HTTP_OK){
                    BufferedReader lector = new BufferedReader(new
                            InputStreamReader(conexion.getInputStream()));
                    String linea = lector.readLine();
                    while (!linea.equals("")){
                        resultado.add(linea);
                        linea = lector.readLine();
                    }
                    lector.close();
                } else {
                    Log.e("Asteroides", conexion.getResponseMessage());
                }
            } catch (Exception e){
                Log.e("Asteroides", e.getMessage(),e);
            } finally {
                conexion.disconnect();
            }
            return resultado;
        }
    }

Esta clase extiende a AsyncTask pero para este caso utilizaremos tres tipos de datos, les recomiendo este post donde explico esto, en nuestro caso solo redefiniremos un metodo el cual es doInBackground() donde recibiremos un dato de tipo Integer llamado cantidad, este codigo es exactamente igual al metodo listaPuntuaciones() del post anterior pero con unas sutiles diferencias, en este caso primero le enviaremos la cantidad a la url por medio de cantidad y el ordinal 0 para informar que use el primer elemento, esto quedara mas evidente en el siguiente caso, luego le agregaremos un bloque try/catch al objeto url y otro a conexion para poder enviar las excepciones correspondientes en caso de error, despues trabaja exactamente de la misma forma que vimos en el post anterior, nuestro siguiente metodo sera guardarPuntuacion():

    @Override
    public void guardarPuntuacion(int puntos, String nombre, long fecha) {
        try{
            TareaGuardar tarea = new TareaGuardar();
            tarea.execute(String.valueOf(puntos),nombre,
                    String.valueOf(fecha));
            tarea.get(4, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            Toast.makeText(contexto,"Tiempo Excedido de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (CancellationException e){
            Toast.makeText(contexto,"Error de conexion",
                    Toast.LENGTH_SHORT).show();
        } catch (Exception e){
            Toast.makeText(contexto,"Error con la tarea asincrona",
                    Toast.LENGTH_SHORT).show();
        }
    }

Este otro metodo es necesario para poder implementar la interfaz AlmacenPuntuaciones pero como sucedio en el caso anterior lo modificaremos de la siguiente manera, primero usaremos un bloque try/catch donde crearemos un objeto llamado tarea de tipo TareaGuardar, esta clase la veremos a continuacion, luego le diremos que llame a execute() y enviara los tres datos de los cuales a puntos y fecha los transformra en String por medio de valueOf y por ultimo creara un retardo de 4 seg. Despues tendremos tres bloque catch para enviar un mensaje en caso de que ocurra un timeout, una cancelacion o un error en la tarea, con esto terminamos nuestro metodo pasemos a describir la clase TareaGuardar:

    private class TareaGuardar extends AsyncTask<String,Void,Void>{
        @Override
        protected Void doInBackground(String... param){
            try{
                URL url = new URL("http://172.128.10.9/nuevo.php"
                        + "?puntos=" + param[0]
                        + "&nombre=" + URLEncoder.encode(param[1], "UTF-8")
                        + "&fecha=" + param[2]);
                HttpURLConnection conexion = (HttpURLConnection) url
                        .openConnection();
                try{
                    if (conexion.getResponseCode()==HttpURLConnection.HTTP_OK) {
                        BufferedReader lector = new BufferedReader(
                                new InputStreamReader(conexion.getInputStream()));
                        String linea = lector.readLine();
                        if (!linea.equals("Ok.")){
                            Log.e("Asteroides", "Error en servicio web nuevo");
                        }
                    } else {
                        Log.e("Asteroides", conexion.getResponseMessage());
                    }
                } catch (Exception e){
                    Log.e("Asteroides", e.getMessage(),e);
                } finally {
                    conexion.disconnect();
                }
            } catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }

Esta clase es muy similar a la anterior porque extiende a AsyncTask y solamente redefine al metodo doInBackground() donde usara una variable de tipo String llamada param, el cuerpo es exactamente igual al metodo guardarPuntuacion() que vimos en el post anterior pero con unas sutiles diferencias, primero tendremos un bloque try/catch por encima de todo, luego en lugar de enviar los datos directamente usaremos a param y sus ordinales para cada datos, p.e. para puntos es param[0], para nombre es param[1] y para fecha es param[2], despues el resto trabaja de la misma forma, con esto ya vimos la nueva clase creada, nuestra siguiente modificacion es en MainActivity donde cambiaremos la linea de seteo de almacen de la siguiente forma:

almacen = new AlmacenPuntuacionesSW_PHP_AsyncTask(this);

Como pueden ver en este caso si enviamos el contexto para poder trabajar, las otras sutiles diferencias son que los metodos no estan obligados a enviar la excepcion IOException sino que simplemente utilizamos bloques try/catch en cada una de las secciones donde es necesario pero si nos fijamos en la interfaz no fue necesario modificarlo, por ultimo ahora tenemos un contexto donde trabajar y esto genera una forma “asincrona” de trabajar porque en realidad si bien no usamos el hilo principal si seguimos trabajando de la misma forma, si lo compilan y prueban deberan tener exactamente el mismo resultado pero con la diferencia, imperceptible para nosotros, de que no usa el hilo principal.

Nota: Por alguna razon en esta ocasion el compilador me pidio que elimine la linea de minSDK de AndroidManifest.xml que agregamos en el post anterior, si los pide haganlo pero no eliminen la linea de permiso de internet.
Anuncios

En resumen, hoy hemos implementadoo la posibilidad de poder trabajar de forma asincronica con nuestra conexion y por fuera del hilo principal, hemos repasado un poco esta forma de trabajo y hemos logrado agilizar nuestra aplicacion sin posibilidades de bloqueo, espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00