OkHttp - продвинутый HTTP-клиент
Под Android имеется два стандартных HTTP-клиента: Apache HTTP Client и HttpURLConnection.
Первый оптимален на старых версиях Android (Eclair and Froyo), второй - на более поздних версиях.
Библиотека OkHttp - это альтернативный HTTP-клиент, основанный на исходных кодах HttpURLConnection, и реализующий множество дополнительных полезных функций. В частности, в OkHttp:
- добавлена поддержка протоколов HTTP 2 (draft), SPDY 3 (draft);
- реализовано автоматическое восстановление соединения, при возникновении распространенных сетевых проблем (например, проблем c прокси-сервером и TLS рукопожатием);
- реализован пул соединений, обеспечивающий повторное использование HTTP и SPDY соединений, за счет чего увеличивается пропускная способность и снижается время ожидания.
Важнейший плюс OkHttp - библиотека
устраняет проблемы реализации HttpURLConnection на старых версиях Android. Например, прозрачная поддержка GZip реализована в HttpURLConnection только в Gingerbread (Android 2.3), кэш ответов - в Ice Cream Sandwich (Android 4.0). OkHttp предоставляет весь этот функционал, начиная с Android 2.2.
Retrofit: безопасный REST-клиент
В общем случае, при выполнении запроса к REST-серверу, требуется выполнить ряд операций:
- сформировать URL;
- задать HTTP-заголовки;
- выбрать тип HTTP-запроса;
- сформировать тело HTTP-запроса, т.е. преобразовать Java объект в JSON;
- выполнить запрос, воспользовавшись HTTP-клиентом;
- распарсить результаты запроса - преобразовать полученный JSON в Java объект.
Библиотека
Retrofit позволяет описать все перечисленные операции с помощью аннотаций - компактно и без ошибок. Рассмотрим пример. Пусть у нас есть запрос из
BooksSet REST API:
GET /books/1234
Этот запрос возвращает JSON вида
{
"id": "1236",
"title": "Yellow mist",
"author": "Alexander Volkov",
"released": "1970"
}
С помощью Retrofit мы можем описать этот запрос следующим образом:
01 | import retrofit.http.GET; |
02 | import retrofit.http.Path; |
03 | import retrofit.http.Query; |
12 | public interface IBookSetRestAPI { |
14 | @GET ( "/v1/books/{book_id}" ) |
15 | Book getBook( @Path ( "book_id" ) int book_id); |
Выполнить запрос к серверу можно так:
1 | RestAdapter restAdapter = new RestAdapter.Builder() |
4 | IBookSetRestAPI rest_api = restAdapter.create(IBookSetRestAPI. class ); |
5 | Book book = rest_api.getBook( 136 ); |
Каждый запрос к REST-серверу описывается отдельной функцией в интерфейсе IBookSetRestAPI
. Тип HTTP запроса задается с помощью аннотаций @GET, @POST, @PUT, @DELETE, @HEAD, @PATCH
, параметры URI-шаблона задаются через параметры функции и описываются аннотацией @Path
.
В случае, когда в URL присутствуют переменные, они задаются через аннотацию @Query
, например запросы:
GET /v1/books?limit=10&offset=20
GET /v1/books?limit=10
описываются следующим образом:
01 | public interface IBookSetRestAPI { |
03 | @GET ( "/v1/books/{book_id}" ) |
04 | Book getBook( @Path ( "book_id" ) int book_id); |
07 | ListBooks getBooks( @Query ( "limit" ) int limit); |
10 | ListBooks getBooks( @Query ( "limit" ) int limit, @Query ( "offset" ) int offset); |
Разумеется, есть и другие аннотации - для задания заголовков HTTP-запросов, для формирования составных запросов и т.д.
Несмотря на то, что аннотации Retrofit по структуре похожи на аннотации JAX-RS, эти наборы аннотаций не совместимы. Причина: аннотации JAX-RS ориентированы на серверную часть, а аннотации Retrofit - на клиентскую.
Парсинг результатов
Функция
Book getBook(int book_id)
возвращает объект, распарсивание JSON производится автоматически. По умолчанию, для распарсивания используется GSON, т.е. в проект необходимо добавить
gson-2.2.4.jar. Библиотека поддерживает кастомизацию конвертации объектов, причем конвертеры XML и Protobuf
доступны вместе с библиотекой.
Можно получать результаты в "сыром", нераспарсенном виде. Достаточно определить интерфейс IBookSetRestAPI следующим способом:
01 | import retrofit.client.Response; |
02 | import retrofit.http.GET; |
03 | import retrofit.http.Path; |
04 | import retrofit.http.Query; |
06 | public interface IBookSetRestAPI { |
08 | @GET ( "/v1/books/{book_id}" ) |
09 | Response getBook( @Path ( "book_id" ) int book_id); |
12 | Response getBooks( @Query ( "limit" ) int limit); |
15 | Response getBooks( @Query ( "limit" ) int limit, @Query ( "offset" ) int offset); |
Объект
Response
дает прямой доступ к содержимому ответа сервера - можно парсить его вручную.
OkHttp и Retrofit
Retrofit выбирает HTTP-клиента следующим образом:
- Если OkHttp задействована в проекте, то используется OkHttpClient;
- В противном случае: если приложение работает под Android 2.2 или ниже, используется HttpClient, иначе - HttpURLConnection.
GZip и Retrofit
OkHttp использует Gzip автоматически при условии, что заголовок Accept-Encoding не задан явно. Таким образом, если Accept-Encoding не задан, то:
- OkHttp автоматически добавляет "Accept-Encoding: gzip" и отправляет запрос серверу;
- далее, проверяет ответ от сервера: если в заголовках ответа есть "Content-Encoding: gzip", значит данные запакованы;
- запакованные данные OkHttp автоматически распаковывает. В результате, клиент получает Response с распакованным содержимым; если же используется конвертер, то в функцию fromBody конвертера опять же приходят уже распакованные данные.
Если же по каким-либо причинам transparent gzip необходимо отключить, то следует
явно задать заголовок Accept-Encoding. Например:
1 | @Headers ( "Accept-Encoding: identity" ) |
5 | @Headers ( "Accept-Encoding: gzip" ) |
Запрос getBooks будет работать без gzip. Запрос getBooks2 будет поддерживать gzip, однако на выходе пользователь получит запакованные данные (как в Response, так и в функции fromBody конвертера), которые необходимо будет распаковывать вручную.
Синхронное и асинхронное выполнение запросов
Retrofit поддерживает оба варианта. Асинхронное выполнение запросов требует подключения библиотеки
RxJava.
Зависимости Retrofit
Все зависимости опциональны:
- OkHttp - если требуется OkHttpClient;
- GSon - если планируется использовать стандартный GSon конвертер;
- RxJava - если требуется асинхронное выполнение запросов.