Retrofit — библиотека для работы с REST API

Продолжаем разбирать библиотеки от Square. Сегодня мы познакомимся поближе с замечательной библиотекой Retrofit.

Внимание: данная статья немного устарела в связи с выходом библиотеки Retrofit 2, но все же мы советуем ознакомиться с содержанием данного поста, для понимания базовых принципов работы библиотеки. Новый обзор можно почитать здесь.




Замечательна она тем, что:

1) Больше не нужно выводить запросы к API в отдельный поток в коде — Retrofit это сделает за нас.
2) Значительно сокращает длину кода и, соответсвенно, ускоряет разработку
3) Динамически строит запросы
4) Автоматически конвертирует JSON в объекты (используется библиотека Gson)
5) Обрабатывает ошибки
6) Умеет передавать файлы

Вся логика работы библиотека завязана на аннотациях. Благодаря ним можно создавать динамические запросы на сервер.

Описание API

Описание запросов к серверу происходит в интерфейсе. Над каждым методом должна стоять аннотация, с помощью которой Retrofit «узнает», какого типа запрос.
Также с помощью аннотаций можно указывать параметры запроса.

Вот так, например, выглядит описание GET-запроса:

import retrofit.client.Response; 
import retrofit.http.GET; 

public interface API { 
   @GET("/v1/users") 
   Response getUsers(); 
}

Как видите, нам не нужно указывать адрес сайта, который отправляется запрос, нужно лишь указать расположение самого PHP файла на сервере (позже объясню почему). В классе Response содержится информация о статусе запроса и ответ от сервера.

А вот так выглядит POST-запрос:

import retrofit.client.Response; 
import retrofit.http.POST; 

public interface API { 
   @POST("/v1/registration") 
   Response registerUser(); 
}

Можно изменять путь к файлу динамически:

@GET("/{version}/users")
Response getUsers(@Path("version") String version);

Retrofit заменит слово «{version}» на, то которое вы передали методу. Сам аргумент должен быть аннотирован словом Path и в скобках нужно указать ключевое слово.

Параметры запроса

Тут тоже нет ничего сложного:

@GET("/v1/users") 
Response getUsers(@Query("gender") String gender);

Для того, чтобы задать запросу параметры используется аннотация @Query. Слово указанное в скобках рядом с аннотацией будет ключом, а аннотированый аргумент значением.

Синхронные и асинхронные запросы




У вас есть два способа отправки запроса: синхронный и асинхронный. Для синхронной отправки запроса нужно вынести его в отдельный поток. Пример синхронной отправки запроса:

@GET("/users/{id}") 
Response getUserInfo(@Path("id") int id);

В ассинхронном запросе метод ничего не возвращает (void), но обязательно должен принимать на вход объект интерфейса Callback<T>. В качестве параметра интерфейса нужно передать тип возвращаемого объекта. Вот пример ассинхронного запроса:

@GET("/users/{id}") 
void getUserInfo(@Path("id") int id, Callback<Response> cb);

Превращаем интерфейс в API

После того, как мы описали все запросы к серверу в интерфейсе нужно передать этот интерфейс специальному классу, который обработает этот интерфейс и сгенерирует код запросов.

RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.site.com")//В метод setEndpoint передаем адрес нашего сайта
.build();
API service = restAdapter.create(API.class);

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

Используем API

Пример асинхронного GET-запроса:

Описываем запросы к API

public interface API {
	@GET("/users/{user}")
	void getUserInfo(@Path("user") String userName, Callback<Response> cb);
}

Создаем интерфейс для работы с API

RestAdapter restAdapter = new RestAdapter.Builder()
	    .setEndpoint("http://api.mysite.com")
	    .build();
API service = restAdapter.create(API.class);

Посылаем запрос

service.getUserInfo("Admin", new Callback<Response>(){
	public void success(Response arg0, retrofit.client.Response arg1) {
	}

	public void failure(RetrofitError arg0) {
	}
});

Пример синхронного POST-запроса:

Описываем запросы к API

@FormUrlEncoded
@POST("/reg.php")
Response saveUserInfo(@Field("login") String userName, @Field("password") String userPass);

Создаем интерфейс для работы с API

RestAdapter restAdapter = new RestAdapter.Builder()
	.setEndpoint("http://api.mysite.com")
	.build();
API api = restAdapter.create(API.class);

Посылаем запрос

Thread t = new Thread(new Runnable(){
	@Override
	public void run(){
		Response r = service.saveUserInfo("Admin", "123456789");
	}
});
t.start();

Запрос с большим количеством полей

Если вам нужно послать запрос с большим количеством параметров, то будет лучше использовать Map<String, String> и добавлять параметры туда.

Описываем API:

@GET("/retrofit/vardump.php")
Response searchUser(@QueryMap Map<String, String> parameters);

Посылаем запрос:

Map<String, String> parameters = new HashMap<String, String>();
parameters.put("name", "Alexander");
parameters.put("surname", "Ivanov");
parameters.put("age", "35");
parameters.put("city", "Moscow");
Response response = service.searchUser(parameters);

Получаем строку из Response

Данные полученные от сервера класс Response выдает в виде InputStream, чтобы пользователь сам преобразовал ответ в нужный ему тип данных. Здесь мы рассмотрим пример перевода InputStream в строку:

String stringFromResponse(Response response){
	BufferedReader reader = null; 
	StringBuilder sb = new StringBuilder(); 
		
	try { 
		reader = new BufferedReader(new InputStreamReader(
		response.getBody().in())); 
		String line; 
			try { 
				while ((line = reader.readLine()) != null) { 
					sb.append(line); 
				} 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
			
	} catch (IOException e) { 
		e.printStackTrace(); 
	} 
	
	String result = sb.toString();
	return result;
}

Multipart-запросы

Интерфейс API:

@Multipart
@POST("/upload.php")
Response saveUserFile(@Part("userfile") TypedFile file);

Описание Multipart-запроса похоже на описание POST-запроса, только вместо @FormUrlEncoded мы пишем @Multipart. На вход подается объект класса TypedFile с аннотацией @Part, указывающей на имя поля.

Thread t = new Thread(new Runnable(){
	@Override
	public void run(){
		File file = new File("D:\\myfile.mp4");
		TypedFile typedFile = new TypedFile("multipart/mixed", file);
		Response response = service.saveUserFile(typedFile);
	}
});
t.start();

TypedFile — тот же класс File, только с указанием MIME-типа.

Конвертируем ответ в объекты

Класс книги:

public class Book {

  String name, author;
  int year;

  public Book(String name, String author, int year) {
    super();

     this.year = year;
     this.name = name;
     this.author = author;
  }

  public int getYear() {
    return year;
  }

  public String getName() {
    return name;
  }

  public String getAuthor() {
    return author;
  }

}

В интерфейсе в качестве возвращаемого класса ставим класс Book:

@GET("/books/getbook.php")
Book getBook(@Query("name") String bookName, @Query("author") String bookAuthor);

Возвращаемый текст:

{"name": "Java. The Complete Reference","author": "Herbert Shildt","year": 2015}

Вызываем метод getBook:

Book book = service.getBook("Java. The Complete Reference", "Herbert Shildt");

Для конвертирования JSON в объекты Retrofit использует библиотеку Gson. Она автоматически найдет поля в классе и присвоит им соотвествующий текст. Подробнее про Gson вы можете почитать здесь.

Работа с заголовками

Управление заголовками происходит с помощью аннотаций @Header и @Headers.

Простейший пример использования аннотации @Headers:

@Headers("Cache-Control: max-age=640000")
@GET("/users/userslist/")
List<User> widgetList();

Также можно указывать несколько заголовков:

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("/users/{username}")
User getUser(@Path("username") String username);

Также заголовок можно менять динамически используя аннотацию @Header.

@GET("/user")
void getUser(@Header("Authorization") String authorization, Callback callback)

Слово «Authorization» будет использовано в качестве имени заголовка, а переменная authorization как значение. Если переменная authorization равна null, то заголовок не добавится.




Если какой-нибудь заголовок нужно добавлять в каждый запрос (например User-Agent), то это можно сделать с помощью интерфейса RequestInterceptor. В методе intercept(RequestFacade) мы добавляем нужные нам заголовки.

RequestInterceptor requestInterceptor = new RequestInterceptor() {
  @Override
  public void intercept(RequestFacade request) {
    request.addHeader("User-Agent", "Retrofit-Sample-App");
  }
};

Далее вызываем метод setRequestInterceptor у RestAdapter’а и  указываем в качестве аргумента созданный нами RequestInterceptor.

RestAdapter restAdapter = new RestAdapter.Builder()
  .setEndpoint("https://api.github.com")
  .setRequestInterceptor(requestInterceptor)
  .build();

Внимание!

Если у вас возникает ошибка

java.lang.VerifyError: retrofit/converter/GsonConverter

то, скорее всего, вы не подключили библиотеку GSON от Google или подключили ее неправильно.

Комментарии:

4 comments

  1. Pingback: Использование Retrofit с ActiveAndroid | Java-Help

  2. Павел Reply

    А для какой версии библиотеки актуально вышеописанное?

    • Admin Post authorReply

      Retrofit 1.6 до 2.0 (не включительно)

  3. Pingback: Обзор библиотеки Retrofit 2 | Java-Help

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *