Klient REST systemu Android, przykład?


115

Nawet jeśli ten wątek zaakceptował odpowiedź, możesz zaproponować inne pomysły, których używasz lub lubisz


Poznałem te artykuły:

To doprowadziło mnie do tego filmu Google I / O 2010 o aplikacjach klienckich REST

Od tej pory tworzę komponent REST jako komponent statyczny w mojej klasie kontrolera aplikacji.

Myślę, że od teraz powinienem zmienić wzór. Ktoś zwrócił uwagę, że aplikacja Google IOSched to świetny przykład tego, jak pisać klientów REST na Androida. Ktoś inny powiedział, że ten sposób jest zbyt skomplikowany.

Czy ktoś może nam pokazać, jakie są najlepsze praktyki? Krótko i prosto.
Aplikacja IOSched jest zbyt złożona dla przykładowego przypadku użycia.


Witam, Generalnie tworzę oddzielny pakiet dla usługi internetowej o nazwie "ws", mam uogólniającą klasę o nazwie "WebServicUtils.java". Klasa WebServiceUtils.java zawiera metody dostępu do usługi WWW. Nie jestem pewien, czy moja technika jest najlepsza, czy nie, ale jest ona wielokrotnego użytku za każdym razem, gdy kopiuję mój pakiet ws w aplikacji na Androida. Daj mi znać, jeśli chcesz dowiedzieć się więcej o mojej technice.
Ketan Parmar

Nie sądzę, by komentator YouTube miał lepszą alternatywę. Musimy pracować w ramach interfejsów API Androida, nawet jeśli są one często niesamowicie skomplikowane i rozwlekłe.
Timmmm,

Na marginesie, Mechanoid, open-source'owa wtyczka eclipse dla Androida może generować klientów JSON-REST za pomocą prostego DSL, przewodnik, jak z niego korzystać, można znaleźć tutaj robotoworks.com/mechanoid-plugin/service-client-dsl (Jestem autorem tej wtyczki, przepraszam za bezwstydną wtyczkę!)
Ian Warwick

Może to być bardzo pomocne dla osób uczących się implementacji klienta REST dla Androida. Prezentacja Dobjanschiego przepisana na plik PDF: drive.google.com/file/d/0B2dn_3573C3RdlVpU2JBWXdSb3c/ ...
Kay Zed

Odpowiedzi:


99

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 AsyncTaskznaków pod kołdrą. W moim przypadku „wywołuję” te zadania z mojej Activityinstancji; ale aby w pełni uwzględnić przypadki takie jak obrót ekranu, możesz zadzwonić do nich z poziomu a Servicelub 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:

  1. 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.

  2. Jest to AsyncTaskwarstwa 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 AsyncTaskpowrotem 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 Activitylub 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.


Ta odpowiedź podoba mi się bardziej ze względu na przykłady całkiem fajnego kodu. Dzięki
Marek Sebera

1
Prawdopodobnie nic nie warte, to tak naprawdę nie jest zgodne z właściwym wzorcem RESTful MVC, jak opisał Virgil Dobjanschi. Konieczne byłoby włączenie pełnej warstwy ContentProvider, która korzysta z bazy danych SQLite, z której aplikacja korzysta bezpośrednio. W przeciwnym razie jest to dobry, lekki klient REST dla Androida.
Cooper

1
Jedna mała rzecz, musisz wywołać wykonanie na tych Get / PostTask's
Mo Kargas

1
To jest naprawdę świetne. Jak uczynić GetResponseCallback bardziej ogólnym, aby nie przekazywał z powrotem tylko profilu, czy może sugerowałbyś utworzenie oddzielnego GetResponseCallback dla każdego typu danych z API?

1
@MichaelHerbig Tak, są sposoby na stworzenie GetResponseCallbackbardziej ogólnych. Preferuję użycie interfejsu znaczników : Lubię interface IGetResopnse{} oznaczać wszystkie klasy, które mogą być odpowiedziami. Następnie mam class Profile implements IGetResponseitd. Wreszcie zrobić GetResponseCallbackrodzajowe z IGetResponsejako górna granica : public abstract class GetResponseCallback<? extends IGetResponse>.
curioustechizen

11

„Tworzenie aplikacji klienckich Android REST” autorstwa Virgila Dobjanschiego wywołało wiele dyskusji, ponieważ podczas sesji nie przedstawiono żadnego kodu źródłowego ani nie dostarczono go później.

Jedyna implementacja referencyjna, jaką znam (jeśli wiesz więcej, proszę o komentarz) jest dostępna w Datadroid (sesja Google IO jest wymieniona w / prezentacja). Jest to biblioteka, z której możesz korzystać we własnej aplikacji.

Drugie łącze prosi o „najlepszą” strukturę REST, która jest intensywnie omawiana na temat przepływu stosu. Dla mnie ważny jest rozmiar aplikacji, a następnie wydajność wdrożenia.

  • Zwykle używam zwykłej implementacji org.json, która jest częścią Androida od poziomu API 1 i dlatego nie zwiększa rozmiaru aplikacji.
  • Dla mnie bardzo interesujące były informacje znalezione w komentarzach na temat wydajności parserów JSON : od Androida 3.0 Honeycomb parser strumieniowania GSON jest zawarty jako android.util.JsonReader. Niestety, komentarze nie są już dostępne.
  • Wiosenny Android (którego czasem używam) obsługuje Jacksona i GSON. Do wiosny Android RestTemplate Moduł dokumentacja wskazuje na app próbki .

Dlatego trzymam się org.json lub GSON dla bardziej złożonych scenariuszy. W przypadku architektury implementacji org.json używam klasy statycznej, która reprezentuje przypadki użycia serwera (np. FindPerson, getPerson). Nazywam tę funkcjonalność z usługi i używam klas narzędzi, które wykonują mapowanie (specyficzne dla projektu) i sieciowe IO (mój własny szablon REST dla zwykłego GET lub POST). Staram się unikać refleksji.


4
Książka O'Reilly, Programming Android, zawiera pełną implementację wzorca RESTful MVC Dobjanschiego (rozdziały 12-13).
Cooper

Dzięki za wskazówkę: znalazłem to oświadczenie na Amazon: „Rozdział 12 i 13 dotyczy dostawców treści. Obszerne omówienie dostawców treści z przykładowym kodem i przykładową aplikacją dostarczyło mi kilku nowych informacji na temat działania tej technologii i jej możliwości być używane w rzeczywistych sytuacjach programistycznych. Struktura dostawcy treści do przechowywania i odwoływania się do danych za pomocą identyfikatorów URI to jedna z nowatorskich funkcji systemu operacyjnego Android. Świetna praca w wyjaśnianiu technologii krok po kroku! ”
ChrLipp

2
Kod znajduje się na github.com/bmeike/ProgrammingAndroid2Examples (ale brakuje rozdziałów, można je znaleźć w kodzie pierwszej edycji github.com/bmeike/ProgrammingAndroidExamples )
ChrLipp

Czy ktoś był w stanie uruchomić ten kod w ICS +? Plik zadań w przykładzie FinchVideo zawiera zwięźle informację „- Awarie w ICS”. Byłem nieco rozczarowany zakupem książki, aby odkryć, że przykłady kodu są zepsute ...
Eageranalyst

7

Nigdy nie używaj AsynTask do wykonywania żądań sieciowych lub czegokolwiek, co wymaga utrwalenia. Async Task jest silnie powiązany z Twoją aktywnością i jeśli użytkownik zmieni orientację ekranu od momentu ponownego utworzenia aplikacji, AsyncTask zostanie zatrzymany.

Proponuję użyć wzorca usługi z usługą zamiaru i odbiornikiem wyników. Spójrz na RESTDroid . Jest to biblioteka, która pozwala na asynchroniczne wykonywanie dowolnego rodzaju żądań REST i powiadamianie interfejsu użytkownika za pomocą detektorów żądań implementujących wzorzec usług Virgila Dobjanschiego.


3

Istnieje inna biblioteka ze znacznie czystszym interfejsem API i bezpiecznymi danymi. https://github.com/kodart/Httpzoid

Oto prosty przykład użycia

Http http = HttpFactory.create(context);
http.post("http://example.com/users")
    .data(new User("John"))
    .execute();

Lub bardziej złożone z oddzwonieniami

Http http = HttpFactory.create(context);
http.post("http://example.com/users")
    .data(new User("John"))
    .handler(new ResponseHandler<Void>() {
        @Override
        public void success(Void ignore, HttpResponse response) {
        }

        @Override
        public void error(String message, HttpResponse response) {
        }

        @Override
        public void failure(NetworkError error) {
        }

        @Override
        public void complete() {
        }
    }).execute();

Jest świeży, ale wygląda bardzo obiecująco.


Wydaje się, że działa na AsyncTask, co nie jest dobre w przypadku długotrwałych żądań i przełączania między działaniami, ponieważ AsyncTask zostanie zabity po zakończeniu działania.
Malachiasz

1

Istnieje wiele bibliotek i używam tej: https://github.com/nerde/rest-resource . Ten został stworzony przeze mnie i, jak widać w dokumentacji, jest o wiele czystszy i prostszy niż inne. Nie koncentruje się na Androidzie, ale używam go i działa całkiem dobrze.

Obsługuje HTTP Basic Auth. Wykonuje brudną robotę serializacji i deserializacji obiektów JSON. Spodoba ci się to, szczególnie jeśli twoje API jest podobne do Railsów.


1

Zastrzeżenie: Jestem zaangażowany w projekt open source rest2mobile

Inną alternatywą jako klienta REST jest użycie rest2mobile .

Podejście jest nieco inne, ponieważ wykorzystuje konkretne przykłady reszt do generowania kodu klienta dla usługi REST. Kod zastępuje REST URL i ładunki JSON natywnymi metodami Java i POJO. Obsługuje również automatycznie połączenia z serwerem, wywołania asynchroniczne i konwersje POJO do / z JSON.

Zwróć uwagę, że to narzędzie jest dostępne w różnych wersjach (CLI, wtyczki, obsługa systemu Android / ios / js) i możesz użyć wtyczki Android Studio do wygenerowania interfejsu API bezpośrednio w swojej aplikacji.

Cały kod można znaleźć na github tutaj .


3
Zamiast reklamować swoją witrynę, zamień pierwszy link na rzeczywisty cel.
Skydan

0

Udostępniliśmy naszą lekką asynchroniczną bibliotekę klienta REST dla systemu Android o otwartym kodzie źródłowym, może się przydać, jeśli masz minimalne wymagania i nie chcesz samodzielnie zajmować się wielowątkowością - jest to bardzo OK w przypadku podstawowej komunikacji, ale nie jest pełnym klientem REST biblioteka.

Nazywa się libRESTfulClient i można go znaleźć na GitHub .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.