Во многих проектах мне приходилось организовывать общение между клиентом под Android и сервером по протоколу HTTP. Для решение этой задачи, конечно, в ОС есть своё API: классы DefaultHttpClient, HttpPost, HttpGet и другие. В этой статье я хочу привести пример того, как можно архитектурно организовать взаимодействие между классами, чтобы получить удобный механизм выполнения запросов.

Чтобы понять удобство, которое я хочу продемонстрировать, сразу приведу пример кода, как будет выглядеть GET запрос (POST запрос будет выглядеть аналогично с небольшими добавлениями).

 /**
 * Call to tet GET request
 */
 private void testGetRequest() {
   Request.Builder builder = new Request.Builder();
   builder.setDebug(true)
    .setResponseListener(responseListener)
    .setUrl("http://requestb.in/1coag501")
    .build();
 }

Из метода видно, чтобы выполнить запрос, требуется «построить» объект запроса и вызвать у него метод build(), который выполнит всю «черную» работу, причем в фоновом потоке, чтобы не «вешать» UI поток Android-а. Если Вам знакомы принципы и паттерны ООП, то Вы, наверняка, увидели тут паттерн «Строитель», используя который мы применяем в данном случае «ленивую» инициализацию объекта запроса.

Для обработки результатов запроса можно задать слушателя методом setResponseListener(OnResponseListener listener). Слушатель должен реализовывать интерфейс Request.OnResponseListener. Простой пример приведен ниже. Таким образом,  обработку ответа сервера можно задать в текущем контексте, что бесспорно удобно, так как обычно при получение данных с сервера требуется обновить UI.

/**
 * Default simple response listener
 */
 private Request.ResponseListener responseListener = new Request.ResponseListener() {
    @Override
    public void onSuccess(HttpUtils.HttpResult httpResult) {
      if (httpResult != null) {
        Toast.makeText(TestActivity.this,
          httpResult.getEnvelope(), Toast.LENGTH_LONG).show();
      }
    }

    @Override
    public void onError(HttpUtils.HttpResult httpResult) {
      if (httpResult != null) {
        Toast.makeText(TestActivity.this,
          httpResult.getEnvelope(), Toast.LENGTH_LONG).show();
      }
    }
 };

Архитектура классов включает в себя:

  • HttpUtils — класс, реализующий работу с API ОС для выполнения запроса;
  • Request — класс, организующий создание потока для выполнения запроса;
  • Builder — внутренний класс для Request, выполняющий инициализацию объекта Request, запускающий поток запроса на выполнение.

Объект Builder внутри себя создает объект класса Request с заданными параметрами, который  реализует выполнение запроса в отдельном потоке. Параметры запроса (тип запроса POST или GET (другие типы можно добавить в реализацию, я же не стал этого делать), URL, тело запроса для POST запроса, необходимые заголовки запроса) задаются через setter-ы постоителя и передаются в итоге объект Request. Сам объект Request внутри себя не оперирует API ОС для выполнения запроса. Я решил инкапсулировать вызовы этих методов в отдельном классе HttpUtils, чтобы отделить логику «низкоуровневой» реализации.

В итоге получилась простая иерархия классов, а клиентский код, использующий эту иерархию, достаточно читабельный и удобный в использовании.

Реализацию выполнения запросов и тестовый проект доступен на github