Пользователь
0,0
рейтинг
26 июня 2014 в 15:21

Библиотека Android Data Processor для легкого построения REST запросов и их обработки

В повседневной работе постоянно сталкиваюсь с разработкой приложений использующих REST сервисы. Существующие библиотеки помогающие в построении запросов и их обработку не слишком меня устраивали по ряду причин. Возникла мысль о создании простого инструмента наподобие Universal Image Loader позволяющего быстро строить запросы и парсить полученные данные. В результате появился Android Data Processor

Процессор данных предназначен для выполнения REST запросов к сервисам или локально к файлам.
Запросы могут выполнятьс синхронно или асинхронно. Процессор не содержит парсеров. Для обработки результатов вы используете свои любимые парсеры данных и передаете им полученные данные в виде InputStream, String.

Инициализация процессора

Для использования процессора необходиомо его инициализировать при помощи конфигуратора. Конфигуратор позволяет задать основные параметры запроса согласно http://developer.android.com/reference/java/net/URL.html, encoding, timeout и т.д. Эти данные являются базовыми и могут без проблемно модифицироваться при построении конкретных запросов. Инициализацию удобнее всего выполнять в классе наследованном от Application:

	private void initDataProcessor() {
		DataProcessorConfiguration configuration = DataProcessorConfiguration
				.getBuilder()
				.setHost("google.com")
				.setLogEnabled(true)
				.setShowProcessingTime(true)
				.setTimeout(4000)
				.build();
		DataProcessor.getInstance().init(configuration);
	}

Запросы

В данный момент возможно конструирование GET, POST, MultipartRequest а также обработка локальных файлов.

Создание запроса


Request request = GetRequest.newInstance()
				.setLogTag("FB Login to server")
				.addGetParam("signature", "DH$FHJDDBHJV3393n")
				.setPath("login.php")
				.build();



Request request = PostRequest.newInstance()
				.addPostParam("email", "some@gmail.com")
				.addPostParam("password", "any_password")
				.setLogTag("Login to server")
				.addGetParam(VAR_SIG, SIGNATURE)
				.setPath("auth2.php")
				.build();



Request request = MultipartRequest.newInstance()
				.addTextBody("userName", "Alex")
				.addTextBody("email", "some@gmail.com")
				.addTextBody("password", "any_password")
				.addTextBody("sex", "male")
				.addJPEG("imagedata", bitmap, "image.jpg")
				.setLogTag("Create user")
				.addGetParam(VAR_SIG, SIGNATURE)
				.setPath("createuser.php")
				.build();


Обработка результатов запроса и их размещение в объекте

Полученные данные запроса могут быть обработаны любым вашим любимым парсером. Обработанные данные размещаются в объектах реализующий интерфейсы InputStreamDataInterface, StringDataInterface.


public class LoginResult  implements StringDataInterface {

	public static String		token				= "";
	public static String		email				= "";
	public static String		password			= "";

	@Override
	public void fillFromString(String src) throws Exception {
		JSONObject jsonObject = new JSONObject(src);
		token = jsonObject.getString("token");
		email = jsonObject.getString("email");
		password = jsonObject.getString("password");
	}


Выполнение запроса и получение результатов

Выполнение запроса может быть синхронным или асинхронным. Синхронный запрос возвращает непосредственно заполненный объект создаваемый в случае успешного запроса. Асинхронный запрос возвращает такой же объект или Exception в случае проблем через Callback.


DataProcessor.getInstance().executeAsync(request, LoginResult.class, handler);


Пример обработки в Callback:

	
private DataProcessor.Callback callback = new DataProcessor.Callback() {

                                              @Override
                                              public void onFinish(Object obj, int what) {
                                                 if (what == HttpStatus.SC_OK) {
                                                    ... успешные действия
                                                 } else {
                                                    Exception ex = (Exception) obj;
                                                    if (ex instanceof IOException) {
                                                       Log.e("IO Error", ex);
                                                    } else {
                                                       Log.e("Error", ex);
                                                    }
                                                 }
                                              }
                                           };


Пример приложения можно посмотреть по ссылке
Alexandr Tsvetkov @lordtao
карма
7,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (18)

  • 0
    а как насчёт в запрос добавить куку?
    у меня есть сервис, который авторизует по куке
    • 0
      Теперь есть возможность при построении добавить HttpContext
  • 0
    Теперь есть возможность при построении добавить HttpContext
  • 0
    Спасибо за труд. Действительно хорошо бы иметь подобный готовый инструмент.

    Подскажите
    как у вас обработка ошибок реализована
    обработка различных кодов ошибок HTTP, исключений работы с сетью.
    В примере выше это не раскрыто.

    • –1
      Если происходит исключение то оно передается в объекте сообщения Handler вместо подготовленного объекта

      private Handler getLoginHandler () {
      return new Handler () {
      
      	@ Override
      	public void handleMessage (Message msg) {
      	if (msg.what == ProcessingCentre.SUCCESS) {
      		LoginResult resultObject = (LoginResult) msg.obj;
      		...
      		} Else {
      			Exception ex = (Exception) msg.obj;
      			if (ex instanceof IOException) {
      				Log.e("IO Error", ex);
      			} else {
      				Log.w ("Can't login");
      			}
      		}
      	};
      	
      	}
      }
      


      Обработка различных кодов ошибки по отдельности сейчас не предусмотрена. В конечном итоге нам обычно требуется лишь два варианта — запрос успешен либо нет. Ошибки самого парсинга можно обрабатывать при реализации интерфейсов ...DataInterface в методе public void fillFrom… Тем не менее я подумаю как лучше сделать подобную обработку.
      • +1
        В конечном итоге нам обычно требуется лишь два варианта — запрос успешен либо нет


        Не совсем так. Логика обработки ошибок разная.
        Не всегда достаточно Да или Нет.

        Например, HTTP ошибки 401,403 — часто используются согласно протоколу.
        Т.е требуется авторизация и нужно показать это в интерфейсе.
        Ошибка 500 на сервере, это не тоже самое что SocketTimeout
        В первом случае это косяк на сервере, во втором пользователю можно сообщить
        что соединение оборвалось и стоит попробовать еще раз.
        Исключения работы с сетью также могут быть разные — Например при GPRS соединении могут вываливаться NoHttpResponseException,
        в случае которых можно сразу делать вторую попытку отправки запроса.

        Может вам просто сделать свой тип исключения — для всех HTTP кодов ошибок?
        • 0
          Спасибо за совет! Доделаем
        • 0
          Сделал немного по другому. Теперь HTTP код высылается в msg.what и может обрабатываться в Handler
          • 0
            Спасибо.
            Будем пробовать
  • +4
    А чем это лучше retrofit?
    • –3
      Не люблю аннотации
  • +2
    Существующие библиотеки помогающие в построении запросов и их обработку не слишком меня устраивали по ряду причин.

    Гм. Интересно. Можете в двух словах написать, чем вас не устроили retrofit, Spring Android (RestTemplate), Robospice, OkHttp — качественные и гораздо более функциональные решения? И чем ваша библиотека лучше? Ну, помимо того, что своё — всегда милее. :)
    • –1
      Как я уже сказал выше я не люблю пользоваться аннотациями. Это личное мнение не для спора. Поэтому некоторые из перечисленных библиотек не подходят. Далеко не все проекты требуют привлечения сильных но немного громоздких библиотек типа Spring Android или Robospice. Наиболее подходящим для моих целей был бы OkHttp, но там нет готовой возможности Mumultipart путем добавления данных как части запроса через один простой метод. По крайней мере так было некоторое время назад.
      Цель создания моей библиотеки — легковесное конструирование типичных запросов, выполнение и получение данных для дальнейшей обработки.
      • +2
        Как я уже сказал выше я не люблю пользоваться аннотациями.

        Но ведь аннотации — это часть языка, и довольно удобная часть языка. Для отказа от них должны быть какие-то весомые причины.

        Вообще, в случае с REST — маппинги URL, параметры, сериализация/десериализация параметров запросов и респонсов в целом — всё это очень удобно описывать неким подобием DSL, коим, в данном случае, и выступают аннотации. Вы же предлагаете те же параметры GET-запроса вбивать как гвозди явными вызовами addGetParam(), вместо того, чтобы просто и элегантно передать аннотированную сущность предметной области.

        Далеко не все проекты требуют привлечения сильных но немного громоздких библиотек типа Spring Android или Robospice.

        Предрелизный запуск ProGuard'а с включённой опцией shrinking'а решает эту проблему, оставляя ровно то, что использовалось в приложении. Т.е. от нескольких мегабайт того же Robospice останется от силы килобайт 100-150. И, сдаётся мне, гибкость и мощь этих библиотек и удобство дальнейшего развития приложения вполне стоят этих лишних 100-150 килобайт. Впрочем, это лишь моё мнение и я могу ошибаться. :)
      • +1
        Проблема с multipart была решена больше года назад:
        github.com/square/okhttp/issues/50

        В качестве тренировки конечно классно написать свою библиотеку, но вот если начать использовать ее, то:
        1. сильно многословная, что бы привести все к меняемому виду(retrofit), нужно будет писать кучу оберток.
        2. почему для результата нужно создавать именно хэндлер, не поимеем ли мы кучу утечек с таким «калбэком»?
        3. конвертер нельзя просто взять и подключить, его нужно обязательно делать базовым классом для всех респонзов + тягать парсер библиотеку(jackson, gson etc) кругом
        4. парсер класс, создается еще до того как известен ответ от сервера
        5. куча ручной работы по передачи параметров и чтению результатов

        К сожалению оно того не стоит, есть куча библиотек, с апи по лучше, из не упомянутых добавлю: volley, ion.
        Но retrofit, имхо, один из лучших, он именно решает ежедневные проблемы, заметно уменьшает количество писанины, так еще и тестировать достаточно легко.

        p.s. официальный андроид стиль, рекомендует использовать 4 пробела для исходных кодов. Если вы решили использовать табы, то возможно стоит научиться ими пользоваться, у вас сорсы везде разъехались. Классическая проблема при использовании табов.

  • 0
    А чем это лучше jsoup?
    • 0
      Ну, jsoup всё же немного из другой оперы.
    • +1
      Тем же чем чёрный лучше сладкого )

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.