Desde AsyncTask hasta Retrofit

La travesía para construir clientes

AsyncTask

Los primeros serán los últimos

AsyncTask

request

response

OnPreExecute()

OnPostExecute()

doInBackground()

Show me the code

public class AsyncTaskRequest extends AsyncTask<String, Void, ArrayList<Artist>> {

    private AsyncResponse responseListener;

    public AsyncTaskRequest(@NonNull AsyncResponse responseListener) {
        this.responseListener = responseListener;
    }

    @Override
    protected Object doInBackground(String... params) {

        if (params.length == 0) {
            responseListener.onError();
            return null;
        }

        HttpURLConnection urlConnection = null;
        BufferedReader reader = null;
        String artistQuery = params[0];
        String response;

        Uri uri = new Uri.Builder()
                .scheme("https")
                .authority("api.spotify.com")
                .appendPath("/v1")
                .appendPath("/search")
                .appendQueryParameter("type","artist")
                .appendQueryParameter("q",artistQuery)
                .build();

        try {
           
            //Apertura de conexión
            URL url = new URL(uri.toString());

            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.connect();

            //Obtener el stream de respuesta
            InputStream inputStream = urlConnection.getInputStream();
            StringBuilder buffer = new StringBuilder();
            if (inputStream == null) {
                responseListener.onError();
                return null;
            }

            //Acceso de lectura al stream de respuesta
            reader = new BufferedReader(new InputStreamReader(inputStream));

            //Construcción de la respuesta en un string
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line)
                        .append("\n");
            }

            if (buffer.length() == 0) {
                responseListener.onError();
                return null;
            }
            response = buffer.toString();
        }catch (IOException e){
            responseListener.onError();
            return null;
        }catch (MalformedURLException e) {
            e.printStackTrace();
        }finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (final IOException e) {
                    responseListener.onError();
                    Log.e(LOG_TAG, "Error closing stream", e);
                }
            }
        }

        try{
           return parseResponse(response);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(ArrayList<Artist> artists) {
        super.onPostExecute(responseObject);
        responseListener.onResponse(responseObject);
    }

    public interface AsyncResponse {
        public void onResponse(ArrayList<Artist> artists);
        public void onError();
    }
}

AsyncTask

  • Lectura manual de la respuesta del servidor
  • Control de multiples excepciones posibles
  • Publicación del progreso de la tarea en el background
  • Una considerable cantidad de código
  • Puede volverse tediosa la construcción de un AsyncTask para cada petición 
  • Utiliza AsyncTask no solo para peticiones

Volley

Compacto para su conveniencia

Volley

¿Cómo utilizar Volley?

Importar la librería en el Gradle y añadir permisos

Crear un Singleton 

Añade tus peticiones

Volley

¿Cómo funciona Volley?

The Big Picture

Añadir Request al RequestQueue

Ejecutar el Request de manera asíncrona

Devolver la respuesta al hilo principal

Volley

Ciclo de vida de una petición

Show me the code- Volley Singleton

public class VolleyClient extends Application{

    public static final String TAG = VolleyClient.class
            .getSimpleName();

     private static VolleyClient mInstance;

     private RequestQueue mRequestQueue;
     private static Context mCtx;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    private VolleyClient(Context context) {
         mCtx = context;
         mRequestQueue = getRequestQueue();

     }

     public static synchronized VolleyClient getInstance(Context context) {
         if (mInstance == null) {
             mInstance = new VolleyClient(context);
         }
         return mInstance;
     }

     public RequestQueue getRequestQueue() {
         if (mRequestQueue == null) {
             // getApplicationContext() is key, it keeps you from leaking the
             // Activity or BroadcastReceiver if someone passes one in.
             mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
         }
         return mRequestQueue;
     }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

Show me the code - Request Example

StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
 
        // Manejo de la respuesta
        parseArtistsResponse(response);
 
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
         
        // Manejo de errores
        error.printStackTrace();
 
    }
});
 
// Añadir la petición a la cola de peticiones
Volley.newRequestQueue(this).add(stringRequest);

Volley

  • Control de prioridad para las peticiones
  • Cancelación de peticiones
  • Distintos formatos para recibir la respuesta
  • Métodos con más semántica
  • Perfecto para llamadas pequeñas
  • Cache Thread

Retrofit

The android rest client

Estructura de Retrofit

Los archivos más importantes

ApiClient.Java

ApiService.Java

models

Api Service

Esta será la interfaz en la que modelamos la api

public interface SpotifyApiService {
  @GET("/v1/search?type=artist")
  void searchArtist(@Query("q") String query,
                      Callback<ArtistSearchResponse> serverResponse);
}

Api Service

Las cabeceras

 @GET("/v1/search?type=artist")


 @GET("/{version}/search?type=artist")

Las anotaciones de los métodos, de primera instancia, nos permiten manipular el tipo de método y la URL a la cuál haremos la petición.

Api Service

Argumentos del método

Los argumentos que contenga el método de la petición definen por medio de anotaciones qué papel jugará ese argumento.

@GET("/v1/search?type=artist")
void searchArtist(@Query("q") String query,
                      Callback<ArtistSearchResponse> serverResponse);

searchArtist("just", this);

/v1/search?type=artist&q=just

Api Client

En esta clase definiremos el comportamiento del objeto encargado de realizar las peticiones.
Para un mejor manejo de este objeto utilizamos el patrón Singleton.

APP

Client

Server

Api Client

El Truco del Singleton está en el constructor y en el método getInstance

public class ApiClient {

    public static ApiClient API_CLIENT;

    public static ApiClient getInstance(Context context) {
        if(API_CLIENT == null)
            API_CLIENT = new ApiClient()

        return API_CLIENT
    } 

    private ApiClient (Context context){
        //Constructor
    }
}

Retrofit

  • Modelado completo de los métodos de nuestra api
  • Manejo sencillo de headers de la petición
  • Parseo rápido mediante GSON
  • Parseos complejos con ayuda de deserializadores
  • RequestInterceptor
  • Posible integración con OkHttp

Bonus - RxJava + Retrofit

@GET("/v1/search?type=artist")
Observable<ArtistSearchResponse> searchArtist(@Query("q") String query);
public void performSearch(String query) {
        apiService.searchArtist(query)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<ArtistSearchResponse>() {
                    @Override
                    public void call(ArtistSearchResponse artistSearchResponse) {
                        artistSearchResponse.getArtists();
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        throwable.printStackTrace();
                    }
                });
    }

Bonus - RxJava + Retrofit

@GET("/v1/search?type=artist")
Observable<ArtistSearchResponse> searchArtist(@Query("q") String query);
public void performSearch(String query) {
        apiService.searchArtist(query)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        ArtistSearchResponse::getArtists,
                        Throwable::printStackTrace);
    }

Bonus - RxJava + Retrofit

apiService.searchArtist(query)
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(artistSearchResponse -> Observable.from(artistSearchResponse.getArtists()))
                .first()
                .subscribe(Artist::getName);
apiService.searchArtist(query)
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(artistSearchResponse -> Observable.from(artistSearchResponse.getArtists()))
                .last()
                .subscribe(Artist::getName);

Bonus - RxJava + Retrofit

@GET("/v1/artist")
Observable<Bio> getBio(@Query("name") String artistName);
apiService.searchArtist(query)
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(artistSearchResponse -> Observable.from(artistSearchResponse.getArtists()))
                .flatMap(artist -> apiService.getArtistBio(artist.getName()))
                .subscribe(bio -> bio.getAutor());

Bonus - RxJava + Retrofit

getApiService().downloadPDFHeader()
            .subscribeOn(Schedulers.newThread())
            .observeOn(Schedulers.newThread())
            .concatMap(response -> {
                readContentLength(response);
                setupFile();
                writeInCache(response);

                return Observable.from(obtainBlocks());
            })
            .concatMap(offset1 -> {
                int end;

                if (offset1 + BLOCK_SIZE > (contentLength - 1))
                    end = (int)contentLength;

                else
                    end = offset1 + BLOCK_SIZE;

                String range = getResources().getString(R.string.range_format, offset1, end);
                return ApiAdapter.getApiService().downloadPDFBlock(range);
            })
            .subscribeOn(Schedulers.newThread())
            .observeOn(Schedulers.newThread())
            .subscribe(this::writeInCache);

That's it!

silmood

@silmood

De asynctask a retrofit

By Petter Hdz

De asynctask a retrofit

  • 1,317