EDICIÓN 2 (octubre de 2017):
Estamos en 2017. Solo use Retrofit. Casi no hay razón para usar otra cosa.
EDITAR:
La respuesta original tiene más de un año y medio en el momento de esta edición. Aunque los conceptos presentados en la respuesta original aún se mantienen, como señalan otras respuestas, ahora existen bibliotecas que facilitan esta tarea. Más importante aún, algunas de estas bibliotecas manejan los cambios de configuración del dispositivo por usted.
La respuesta original se conserva a continuación como referencia. Pero también tómese el tiempo para examinar algunas de las bibliotecas cliente de Rest para Android para ver si se ajustan a sus casos de uso. La siguiente es una lista de algunas de las bibliotecas que he evaluado. De ninguna manera pretende ser una lista exhaustiva.
Respuesta original:
Presentando mi enfoque para tener clientes REST en Android. Aunque no digo que sea el mejor :) Además, tenga en cuenta que esto es lo que se me ocurrió en respuesta a mi requisito. Es posible que deba tener más capas / agregar más complejidad si su caso de uso lo exige. Por ejemplo, no tengo ningún almacenamiento local; porque mi aplicación puede tolerar la pérdida de algunas respuestas REST.
Mi enfoque usa solo AsyncTask
s bajo las sábanas. En mi caso, "llamo" a estas Tareas desde mi Activity
instancia; pero para tener en cuenta completamente casos como la rotación de pantalla, puede optar por llamarlos desde una Service
o tal.
Elegí conscientemente mi propio cliente REST para que fuera una API. Esto significa que la aplicación que usa mi cliente REST ni siquiera necesita conocer las URL REST reales y el formato de datos utilizado.
El cliente tendría 2 capas:
Capa superior: el propósito de esta capa es proporcionar métodos que reflejen la funcionalidad de la API REST. Por ejemplo, podría tener un método Java correspondiente a cada URL en su API REST (o incluso dos, uno para GET y otro para POST).
Este es el punto de entrada a la API del cliente REST. Esta es la capa que la aplicación usaría normalmente. Podría ser un singleton, pero no necesariamente.
Esta capa analiza la respuesta de la llamada REST en un POJO y la devuelve a la aplicación.
Esta es la AsyncTask
capa de nivel inferior , que utiliza métodos de cliente HTTP para salir y hacer esa llamada REST.
Además, elegí usar un mecanismo de devolución de llamada para comunicar el resultado de los mensajes de correo electrónico AsyncTask
a la aplicación.
Basta de texto. Veamos algo de código ahora. Tomemos una URL de API REST hipotética: http://myhypotheticalapi.com/user/profile
La capa superior podría verse así:
/**
* Entry point into the API.
*/
public class HypotheticalApi{
public static HypotheticalApi getInstance(){
//Choose an appropriate creation strategy.
}
/**
* Request a User Profile from the REST server.
* @param userName The user name for which the profile is to be requested.
* @param callback Callback to execute when the profile is available.
*/
public void getUserProfile(String userName, final GetResponseCallback callback){
String restUrl = Utils.constructRestUrlForProfile(userName);
new GetTask(restUrl, new RestTaskCallback (){
@Override
public void onTaskComplete(String response){
Profile profile = Utils.parseResponseAsProfile(response);
callback.onDataReceived(profile);
}
}).execute();
}
/**
* Submit a user profile to the server.
* @param profile The profile to submit
* @param callback The callback to execute when submission status is available.
*/
public void postUserProfile(Profile profile, final PostCallback callback){
String restUrl = Utils.constructRestUrlForProfile(profile);
String requestBody = Utils.serializeProfileAsString(profile);
new PostTask(restUrl, requestBody, new RestTaskCallback(){
public void onTaskComplete(String response){
callback.onPostSuccess();
}
}).execute();
}
}
/**
* Class definition for a callback to be invoked when the response data for the
* GET call is available.
*/
public abstract class GetResponseCallback{
/**
* Called when the response data for the REST call is ready. <br/>
* This method is guaranteed to execute on the UI thread.
*
* @param profile The {@code Profile} that was received from the server.
*/
abstract void onDataReceived(Profile profile);
/*
* Additional methods like onPreGet() or onFailure() can be added with default implementations.
* This is why this has been made and abstract class rather than Interface.
*/
}
/**
*
* Class definition for a callback to be invoked when the response for the data
* submission is available.
*
*/
public abstract class PostCallback{
/**
* Called when a POST success response is received. <br/>
* This method is guaranteed to execute on the UI thread.
*/
public abstract void onPostSuccess();
}
Tenga en cuenta que la aplicación no usa JSON o XML (o cualquier otro formato) devuelto directamente por la API REST. En cambio, la aplicación solo ve el bean Profile
.
Entonces, la capa inferior (capa AsyncTask) podría verse así:
/**
* An AsyncTask implementation for performing GETs on the Hypothetical REST APIs.
*/
public class GetTask extends AsyncTask<String, String, String>{
private String mRestUrl;
private RestTaskCallback mCallback;
/**
* Creates a new instance of GetTask with the specified URL and callback.
*
* @param restUrl The URL for the REST API.
* @param callback The callback to be invoked when the HTTP request
* completes.
*
*/
public GetTask(String restUrl, RestTaskCallback callback){
this.mRestUrl = restUrl;
this.mCallback = callback;
}
@Override
protected String doInBackground(String... params) {
String response = null;
//Use HTTP Client APIs to make the call.
//Return the HTTP Response body here.
return response;
}
@Override
protected void onPostExecute(String result) {
mCallback.onTaskComplete(result);
super.onPostExecute(result);
}
}
/**
* An AsyncTask implementation for performing POSTs on the Hypothetical REST APIs.
*/
public class PostTask extends AsyncTask<String, String, String>{
private String mRestUrl;
private RestTaskCallback mCallback;
private String mRequestBody;
/**
* Creates a new instance of PostTask with the specified URL, callback, and
* request body.
*
* @param restUrl The URL for the REST API.
* @param callback The callback to be invoked when the HTTP request
* completes.
* @param requestBody The body of the POST request.
*
*/
public PostTask(String restUrl, String requestBody, RestTaskCallback callback){
this.mRestUrl = restUrl;
this.mRequestBody = requestBody;
this.mCallback = callback;
}
@Override
protected String doInBackground(String... arg0) {
//Use HTTP client API's to do the POST
//Return response.
}
@Override
protected void onPostExecute(String result) {
mCallback.onTaskComplete(result);
super.onPostExecute(result);
}
}
/**
* Class definition for a callback to be invoked when the HTTP request
* representing the REST API Call completes.
*/
public abstract class RestTaskCallback{
/**
* Called when the HTTP request completes.
*
* @param result The result of the HTTP request.
*/
public abstract void onTaskComplete(String result);
}
Así es como una aplicación puede usar la API (en un Activity
o Service
):
HypotheticalApi myApi = HypotheticalApi.getInstance();
myApi.getUserProfile("techie.curious", new GetResponseCallback() {
@Override
void onDataReceived(Profile profile) {
//Use the profile to display it on screen, etc.
}
});
Profile newProfile = new Profile();
myApi.postUserProfile(newProfile, new PostCallback() {
@Override
public void onPostSuccess() {
//Display Success
}
});
Espero que los comentarios sean suficientes para explicar el diseño; pero me complacerá brindar más información.