EDYCJA 2 (październik 2017):
Jest rok 2017. Po prostu użyj Retrofit. Nie ma prawie żadnego powodu, aby używać czegokolwiek innego.
EDYTOWAĆ:
Oryginalna odpowiedź ma ponad półtora roku w momencie tej edycji. Chociaż koncepcje przedstawione w pierwotnej odpowiedzi nadal są aktualne, jak wskazują inne odpowiedzi, istnieją obecnie biblioteki, które ułatwiają ci to zadanie. Co ważniejsze, niektóre z tych bibliotek obsługują zmiany konfiguracji urządzeń za Ciebie.
Oryginalna odpowiedź została zachowana poniżej w celach informacyjnych. Ale proszę również poświęcić trochę czasu na sprawdzenie niektórych bibliotek klienta Rest dla Androida, aby sprawdzić, czy pasują do twoich przypadków użycia. Poniżej znajduje się lista niektórych bibliotek, które oceniłem. W żadnym wypadku nie jest to lista wyczerpująca.
Oryginalna odpowiedź:
Przedstawiam moje podejście do posiadania klientów REST na Androida. Nie twierdzę jednak, że jest najlepszy :) Zwróć też uwagę, że to właśnie wymyśliłem w odpowiedzi na moje wymagania. Być może będziesz potrzebować więcej warstw / dodać więcej złożoności, jeśli wymaga tego Twój przypadek użycia. Na przykład w ogóle nie mam lokalnego magazynu; ponieważ moja aplikacja może tolerować utratę kilku odpowiedzi REST.
W moim podejściu używa się tylko AsyncTask
znaków pod kołdrą. W moim przypadku „wywołuję” te zadania z mojej Activity
instancji; ale aby w pełni uwzględnić przypadki takie jak obrót ekranu, możesz zadzwonić do nich z poziomu a Service
lub innego .
Świadomie wybrałem samego klienta REST na API. Oznacza to, że aplikacja korzystająca z mojego klienta REST nie musi nawet znać rzeczywistych adresów URL REST i używanego formatu danych.
Klient miałby 2 warstwy:
Warstwa wierzchnia: celem tej warstwy jest zapewnienie metod odzwierciedlających funkcjonalność REST API. Na przykład, możesz mieć jedną metodę Java odpowiadającą każdemu adresowi URL w Twoim REST API (lub nawet dwie - jedną dla GET i jedną dla POST).
To jest punkt wejścia do interfejsu API klienta REST. Jest to warstwa, z której aplikacja normalnie korzysta. Może to być singleton, ale niekoniecznie.
Odpowiedź na wywołanie REST jest analizowana przez tę warstwę w POJO i zwracana do aplikacji.
Jest to AsyncTask
warstwa niższego poziomu , która używa metod klienta HTTP, aby faktycznie wyjść i wykonać to wywołanie REST.
Ponadto zdecydowałem się użyć mechanizmu wywołania zwrotnego, aby przekazać wynik operacji z AsyncTask
powrotem do aplikacji.
Dość tekstu. Zobaczmy teraz kod. Weźmy hipotetyczny adres URL REST API - http://myhypotheticalapi.com/user/profile
Górna warstwa może wyglądać tak:
/**
* 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();
}
Należy pamiętać, że aplikacja nie używa formatu JSON ani XML (ani żadnego innego formatu) zwracanego bezpośrednio przez interfejs API REST. Zamiast tego aplikacja widzi tylko ziarno Profile
.
Wtedy dolna warstwa (warstwa AsyncTask) może wyglądać następująco:
/**
* 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);
}
Oto jak aplikacja może korzystać z interfejsu API (w Activity
lub 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
}
});
Mam nadzieję, że komentarze są wystarczające, aby wyjaśnić projekt; ale z przyjemnością udzielę więcej informacji.