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