Клиент Android REST, образец?

Даже если этот поток принял ответ, не стесняйтесь предлагать другие идеи, вы используете или как


Я встречал эти статьи:

И это привело меня к этому видеообъявлению Google I/O 2010 о клиентских приложениях REST

С тех пор я создаю компонент REST как статический компонент в классе контроллера приложений.

С этого момента, я думаю, я должен изменить шаблон. Кто-то указал, что Приложение Google IOSched - отличный пример того, как писать клиенты REST на Android. Кто-то еще сказал, что эти способы слишком сложны.

Итак, может ли кто-нибудь, пожалуйста, показать нам, что является лучшей практикой? Короче и просто.
Приложение IOSched слишком сложное для примера использования.

Ответ 1

EDIT 2 (октябрь 2017 г.):

Это 2017. Просто используйте Retrofit. Почти нет причин использовать что-либо еще.

EDIT:

Оригинальный ответ более чем на полтораста лет во время этого редактирования. Хотя концепции, представленные в первоначальном ответе, по-прежнему сохраняются, как указывают другие ответы, теперь есть библиотеки, которые облегчают задачу для вас. Что еще более важно, некоторые из этих библиотек обрабатывают изменения конфигурации устройства для вас.

Оригинальный ответ приведен ниже для справки. Но, пожалуйста, также найдите время, чтобы изучить некоторые из клиентских библиотек Rest для Android, чтобы узнать, соответствуют ли они вашим прецедентам. Ниже приведен список некоторых из библиотек, которые я оценил. Он ни в коем случае не должен быть исчерпывающим списком.


Исходный ответ:

Представление моего подхода к тому, чтобы клиенты REST на Android. Я не утверждаю, что это самое лучшее, хотя:) Также обратите внимание, что это то, что я придумал в ответ на мои требования. Возможно, вам понадобится больше слоев/добавьте больше сложностей, если это потребует ваш вариант использования. Например, у меня нет локального хранилища; потому что мое приложение может терпеть потерю нескольких ответов REST.

Мой подход использует только AsyncTask под обложками. В моем случае я "вызываю" эти задачи из моего экземпляра Activity; но для полного учета таких случаев, как поворот экрана, вы можете называть их из Service или таких.

Я сознательно выбрал самого клиента REST как API. Это означает, что приложение, использующее мой клиент REST, даже не должно знать о реальном URL REST и используемом формате данных.

Клиент будет иметь 2 слоя:

  • Верхний слой: целью этого слоя является предоставление методов, которые отражают функциональность REST API. Например, у вас может быть один метод Java, соответствующий каждому URL-адресу в вашем REST API (или даже два - один для GET и один для POST).
    Это точка входа в API клиента REST. Это слой, который приложение будет использовать нормально. Это может быть синглтон, но не обязательно.
    Ответ вызова REST обрабатывается этим слоем в POJO и возвращается в приложение.

  • Это уровень ниже уровня AsyncTask, который использует методы HTTP-клиента для фактического выхода и выполнения этого вызова REST.

Кроме того, я решил использовать механизм обратного вызова для передачи результата AsyncTask обратно в приложение.

Достаточно текста. Теперь посмотрим на некоторый код. Возьмем гипотетический URL-адрес API REST - http://myhypotheticalapi.com/user/profile

Верхний слой может выглядеть следующим образом:

   /**
 * 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();

}

Обратите внимание, что приложение не использует JSON или XML (или любой другой формат), возвращенный API REST напрямую. Вместо этого приложение видит только bean Profile.

Затем нижний уровень (слой AsyncTask) может выглядеть так:

/**
 * 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 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);
    }

Здесь, как приложение может использовать API (в Activity или 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
            }
        });

Я надеюсь, что комментариев достаточно, чтобы объяснить дизайн; но я был бы рад предоставить дополнительную информацию.

Ответ 2

"Разработка клиентских приложений REST для Android" Вирджила Добжански привела к большому дискуссиям, поскольку исходный код не был представлен во время сеанса или был предоставлен впоследствии.

Единственная эталонная реализация, которую я знаю (прокомментируйте, если вы знаете больше) доступна в Datadroid (сеанс Google IO упоминается в/презентации). Это библиотека, которую вы можете использовать в своем приложении.

Вторая ссылка запрашивает "наилучшую" структуру REST, которая в значительной степени обсуждается в stackoverflow. Для меня важна размер приложения, за которым следует производительность реализации.

  • Обычно я использую обычную реализацию org.json, которая является частью Android с уровня API 1 и поэтому не увеличивает размер приложения.
  • Для меня очень интересной была информация, найденная в JSON parsers performance в комментариях: по состоянию на Android 3.0 Honeycomb, потоковый парсер GSON включен как android.util.JsonReader. К сожалению, комментарии больше не доступны.
  • Spring Android (который я иногда использую) поддерживает Jackson и GSON. Документация Spring Android RestTemplate указывает на примерное приложение.

Поэтому я придерживаюсь org.json или GSON для сложных сценариев. Для архитектуры реализации org.json я использую статический класс, который представляет примеры использования сервера (например, findPerson, getPerson). Я вызываю эту функцию из службы и использую классы служебных программ, которые выполняют сопоставление (конкретный проект) и сетевой IO (мой собственный шаблон REST для простого GET или POST). Я стараюсь избегать использования рефлексии.

Ответ 3

Никогда не используйте AsynTask для выполнения сетевого запроса или того, что необходимо сохранить. Задача Async тесно связана с вашей деятельностью, и если пользователь изменит ориентацию экрана с момента создания приложения, AsyncTask будет остановлен.

Я предлагаю вам использовать Service pattern с Intent Service и ResultReceiver. Взгляните на RESTDroid. Это библиотека, которая позволяет вам выполнять любой запрос REST асинхронно и уведомлять свой пользовательский интерфейс с помощью Listen Listers, реализующих шаблон обслуживания Virgil Dobjanschi.

Ответ 4

Существует еще одна библиотека с гораздо более чистым API и безопасными по типу данными. https://github.com/kodart/Httpzoid

Вот простой пример использования

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

Или сложнее с обратными вызовами

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();

Он новый, но выглядит очень многообещающим.

Ответ 5

Там много библиотек, и я использую этот: https://github.com/nerde/rest-resource. Это было создано мной, и, как вы можете видеть в документации, оно стало более чистым и простым, чем другие. Он не ориентирован на Android, но я использую его, и он работает очень хорошо.

Он поддерживает HTTP Basic Auth. Он выполняет грязную работу по сериализации и десериализации объектов JSON. Вам понравится, особенно если ваш API - это Rails.

Ответ 6

Отказ от ответственности: я участвую в проекте open2mobile с открытым исходным кодом

Другой альтернативой клиенту REST является использование rest2mobile.

Подход немного отличается, поскольку он использует конкретные примеры отдыха для генерации кода клиента для службы REST. Код заменяет URL-адрес REST и полезные данные JSON на собственные Java-методы и POJO. Он также автоматически обрабатывает соединения с сервером, асинхронные вызовы и POJO в/из конверсий JSON.

Обратите внимание, что этот инструмент поставляется в разных вариантах (cli, plugins, android/ios/js), и вы можете использовать плагин для Android-студии для создания API непосредственно в вашем приложении.

Весь код можно найти на github здесь.