Estructura de proyecto & Async HTTP Model Android

Android no nos deja realizar llamadas HTTP en el main thread por lo que para poder realizar la misma tenemos que crear un nuevo hilo de ejecución y realizar la misma, el instancia AsyncTask en una actividad o fragment puede resultarnos un poco no grato para nuestro código,nuestras vistas no necesitan saber lo que es un HTTPCODE o JSON nada de eso para nuestras vistas.

Si quieres pasar directamente al ejemplo y verlo directamente aqui esta el link del project.

Para organizar un poco nuestro codigo compartire el modelo que utilizo a la hora de comunicarme / interactuar con alguna api que retorne json o el formato que exponga la misma, este modelo digamos no podría decirse que no es mio y tampoco sabría de quien es pero con el tiempo y buscando en internet un poco de cada cosa lo he adquirido.

Es mas bien extender un poco el patron MVC, tenemos nuestras vistas, modelos y controladores, a esto le agregaria una sub capa sobre los «controladores» yo normalmente le llamo «Data» y dentro de este una clase que sea un Facade de todo lo concernientes a mi aplicación, desde la persistencia de datos hasta las llamadas a un web service.

Hasta este momento seria algo como esto de los que les hablo a nivel de paquetes.


Application/

    Views/
        Activities/
        Fragments/
        Adapters

    Domain/
        DTO/
        DAO/

    Data/
        DataFacade.java

El archivo que tendría toda la magia centralizada seria DataFacade, si la aplicación es pequeña se podría poder todo en esa clase, pero si la aplicación crece algo mas lo que hago es un nuevo paquete bajo Data.


Application/

    Views/
        Activities/
        Fragments/
        Adapters

    Domain/
        DTO/
        DAO/

    Data/
        Handlers/
        DataFacade.java

Handlers ahi estarían las clases encargadas de manejar tareas de un mismo tipo, digamos un UserHandler.class que se encargarían de agregar, eliminar, actualizar y eliminar usuarios, estos handlers accedido atraves de nuestro Facade (DataFacade) estos Handlers serian mas bien como DAO pero sobre la red.


Application/

    Views/
        Activities/
        Fragments/
        Adapters

    Domain/
        DTO/
        DAO/

    Data/
        Handlers/
            UserHandler.java
        DataFacade.java

Una vez tenido esta estructura nos llega la interrogante de crear una estructura de clases que pueda satisfacer a cada llamada de nuestra vista ya que en algunos lugares necesitaremos que nuestro DataFacade retorne de forma async cierta cantidad de parametros tanto en numero como en tipo y recordemos que java al diferencia de javascript no tienen funciones anónimas.

Este patron me surgió luego de un buen tiempo de trabajar con callbacks en Nodejs y quise implementarlo en android para sentirme un poco mas comodo.

Analicemos las AsyncTasks

 class AsyncTest extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String... params) {
            // ejecucion en segundo plano
        }

        @Override
        protected void onPostExecute(String result) {
            
        }

        @Override
        protected void onPreExecute() {}

        @Override
        protected void onProgressUpdate(Void... values) {}
    }

AsyncTask toman un parametro tipo String (tipo param, se le puede pasar mas de uno del mismo tipo) si queremos saber el progreso implementamos onProgressUpdate cambiamos el segundo parametro por Int y retorna acorte con esta firma un String sabiendo todo esto podriamos tener algo asi:

public class AsyncGeneric extends AsyncTask<Void, Void, Void> {
    private AsyncGeneric(){

    }

    public static AsyncGeneric getInstance(){
        instance = new AsyncGeneric();

        return instance;
    }

    @Override
    protected Void doInBackground(Void... params) {

    }

    @Override
    protected void onPostExecute(Void result) {
        
    }
}

Tenemos un AsyncTask generico que no toma parametros no retorna ningun parametro y a su vez tiene implementado un singleton (Odiado y amado por muchos). Asi como esta eso no nos sirve mucho la verdad pero si combinamos esto con una clase que sea como Runnable que es abstracta podriamso tener algo como esto.

public abstract class RunnerGeneric {

    public abstract void run();

    public void postExecute(){

    }
}

public class AsyncGeneric extends AsyncTask<Void, Void, Void> {

    private static AsyncGeneric instance = null;

    private RunnerGeneric runner;

    public RunnerGeneric getRunner() {
        return runner;
    }

    public AsyncGeneric setRunner(RunnerGeneric runner) {
        this.runner = runner;

        return instance;
    }

    private AsyncGeneric(){

    }

    public static AsyncGeneric getInstance(){
        instance = new AsyncGeneric();

        return instance;
    }

    @Override
    protected Void doInBackground(Void... params) {

        this.runner.run();

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        this.runner.postExecute();
    }

}

Donde podríamos perfectamente realizar algo como esto en uno de nuestras vistas:

AsyncGeneric.getInstance().setRunner(new CallBackGeneric() {

    @Override
    public void run() {
        // background thread
        // HTTP CALL
    }

    @Override
    public void postExecute() {

    }
}).execute();

Con esta solución podemos realizar llamadas HTTP en un segundo thread sin lidiar directamente con las AsyncTaks, si queremos que nuestro metodo retorne una lista de lo que sea simplemente creamos una propiedad en CallBackGeneric la poblamos en el metodo run e interactuamos con nuestra vista en el metodo postExecute; como esta ahora es mas bien una abstracción de las AsyncTask pero se pondra mejor.

AsyncGeneric.getInstance().setRunner(new CallBackGeneric() {

    private List<Algo> result = ...

    @Override
    public void run() {
        result = HTTPREQUEST()
    }

    @Override
    public void postExecute() {
        Activity.data = result;
    }
}).execute();

Como estamos ahora
Si combinamos esto con nuestra clase DataFacade.java podriamos tener algo como esto.


class DataFacade{

    //singleton pattern
    
    public void AnyMethod(CallBackGeneric callback){
        AsyncGeneric.getInstance().setRunner(callback).execute();
    }
}

class Activity{

    public void cargarDataDeInternet(){
        DataFacade.getInstance().AnyMethod(new CallBackGeneric() {
            ....
        });
    }

}

Con este codigo como esta aun tenemos nuestro problema principal, y es que nuestras vistas tienen conocimientos de cosas que no les conciernen, podemos abstraer esto creando otra clase CallBack que solo sea para vistas.


class CallBackView{
    public void backToView(List<Items> result){
    }
}

Con esta clase podriamos tener algo como esto:


class DataFacade{

    //singleton pattern
    
    public void AnyMethod(CallBackView callback){
        AsyncGeneric.getInstance().setRunner(new CallBackGeneric() {
            ....
        }).execute();
    }
}

class Activity{
    public void cargarDataDeInternet(){
        DataFacade.getInstance().AnyMethod(new CallBackView(){
            @Override
            public void backToView(List<Items> result){
                ....
                //logica de vistas.
            }
        });
    }
}

A medica que crees un nuevo metodo con nuevos requerimientos, simplemente agrega esa firma en la clase CallBackView y reescríbela en las vista donde la vayas a usar. Esto podría ser chocante para cualquier el tener una clase con mucho metodos en blanco y todo eso, pero es un buena solución para el problema, si alguien tiene una mejora para ese modelo que uso sera muy bien recibida.

Bueno con esto termino este «Tutorial»

Referencia

Projecto de ejemplo en GITHUB