Llamadas asíncronas dentro y fuera del ámbito una Actividad en Android

- Andrés Cruz

In english

Llamadas asíncronas dentro y fuera del ámbito una Actividad en Android

Cuando se desea realizar un llamado asíncrono en HTML cuya petición no se "pierda" en el tiempo, es muy fácil a través de AJAX; si empleamos jQuery es más fácil todavía debido a su sintaxis y pocas líneas; en Android aunque es en realidad sencillo, la cosa cambia y no es tan directa; lo primero que se nos ocurre hacer es realizar una petición a través de un threat dentro del ámbito de una Actividad en Android de la siguiente manera:

public class MyActivity extends Activity {
...
new Thread(new Runnable() {
    public void run() {
        //Aquí ejecutamos nuestras tareas costosas
    }
}).start();
...

Problemas con la llamada asíncrono anterior…

Puede que no sea del todo recomendable emplear el código anterior dentro del ámbito una Actividad o Fragment Android según el resultado que se espera obtener; es decir, supongamos que empleamos un threat desde una Actividad en Android con el objetivo de realizar una petición para obtener datos desde un Servidor Web vía HTTP para finalmente mostrarlos de alguna manera (por ejemplo un listado o tabla).

Según el volumen de datos a recibir y la conexión que se tenga en esos momentos el tiempo de la consulta varía, supongamos que tardo unos 3 segundos en total en realizar la petición; ahora, ¿qué crees que pasa cuando el usuario rote la pantalla antes de los 3 segundos?, por ejemplo en el segundo 1,5:

Cambio de orientación pantalla

La petición "se pierde" y dado el Ciclo de vida de una Actividad en Android, el método onCreate se volvería a invocar y con esto nuestra petición HTTP de unos 3 segundos.

Además del caso presentado anteriormente, también es posible que el usuario navegue hacia otra Actividad Android en ese lapso que se realiza la petición (en nuestro ejemplo de 3 segundos) y nuevamente se perderá la conexión.

Esto se debe al hecho de definir un método asíncrono (en nuestro caso un threat) dentro del ámbito una Actividad Android y por lo tanto es obligada a regirse por el Ciclo de vida de una Actividad en Android.

¿Qué podemos hacer para evitar perder las llamadas asíncronas?

Declarar nuestro hilo o proceso asíncrono en una clase aparte o fuera del ámbito de la Actividad o Fragment Android; pero esto nuevamente trae el inconveniente que al rotar la pantalla o movernos a otra Actividad Android sea realizada la petición a través de una nueva instancia; aunque esto caso lo podemos evitar al crear una clase de instancia única (Patrón de Diseño Singleton) y de esta forma se evita crear nuevas instancias a lo largo de la aplicación pero esto último será tema de otra entrada; para crear una clase asincrona podemos emplear la clase provista por la API de Android llamada AsyncTask:

public class MyAsyncTask extends AsyncTask<Void, Void, String> {

	public AsyncResponse delegate = NULL;

	@Override
	public String doInBackground(Void... params) {
		return NULL;
	}

	@Override
	public void onPostExecute(String result) {
		delegate.processFinish(result);
	}

Los métodos doInBackground(Void... params) y onPostExecute(String result) son asíncronos en donde onPostExecute(String result) se ejecuta luego de doInBackground(Void... params); puedes ver el total de los métodos en el siguiente enlace AsyncTask API Android.

Además, el método onPostExecute(String result) de la clase MyAsyncTask invocamos al método llamado processFinish(result) de la clase AsyncResponse cuya definición veremos a continuación.

¿Qué pasa si deseamos realizar alguna tarea en la Actividad Android luego de que el proceso asíncrono retorne la data?

Para eso se emplea la Interfaz AsyncResponse dentro de la clase anterior llamada AsyncResponse que contiene el siguiente código:

public interface AsyncResponse {
	void processFinish(String output);
}

Para emplear el proceso o tarea asíncrono desde una Actividad o Fragment Android empleamos el siguiente código:

public class MyFragment extends Fragment implements
		AsyncResponse {

...
myAsyncTask = new MyAsyncTask();
…

	public void processFinish(String output) {
//El método processFinish() es invocado cuando la clase 
//asíncrona llamada MyAsyncTask ejecuta el método onPostExecute(result) method.
	}
}

La clase AsyncTask es perfecta para manejar procesos en segundo plano de corta duración (pocos segundos).

  • Implementamos la interfaz AsyncResponse en nuestro Fragment o Actividad Android.
  • El método llamado processFinish(String output) es el empleado para devolver cualquier data (por ejemplo en formato JSON) a nuestra Actividad o Fragment Android y realizar alguna operación desde el hilo principal de nuestra aplicación.

El método processFinish(String output) de nuestra Actividad o Fragment Android es invocado cuando la clase asíncrona llamada MyAsyncTask ejecuta el método onPostExecute(result) .

Usos de la clase AsyncTask

El esquema del código anterior, además de poder adaptarlo para realizar peticiones HTTP, también se puede adaptar para realizar otros tipos de trabajo cómo realizar una acción pasada un determinado tiempo (por ejemplo 5 segundos).

¿Porqué interesaría realizar acciones en base a un tiempo?

Como desarrollador Android seguramente también eres usuario de esta plataforma; habrás notado el típico "Deshacer Acción" que implemente Google en sus aplicaciones, como en el caso de gmail:

Deshacer en Gmail

La acción que desencadena al presionar sobre el botón "Deshacer" de este panel es la de deshacer el eliminado de un correo hace unos instantes.

Simulando la opción de deshacer a través de una llamada asíncrona

Podríamos emplear el esquema planteado anteriormente para simular esta acción:

  1. Se selecciona algunos de los items listados y se elimina (aunque internamente simplemente el item es escondido -NO se elimina de Base de Datos-).

Al realizar una tarea que requiera de la función deshacer se podría invocar la clase llamada MyAsyncTask que debe implementar un hilo cuya única función sería la de dormir un tiempo determinado (5 segundos) que debe coincidir con el tiempo que el panel se muestre en pantalla.

  1. Si en ese lapso el usuario no se ha revertido su acción, entonces se procede a eliminar el registro seleccionado por el usuario; caso contrario se muestra nuevamente el item de la lista oculto en un inicio.

La implementación del esquema planteado anteriormente será tratado en otra entrega sobre Android.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz En Udemy

Acepto recibir anuncios de interes sobre este Blog.