Перейти к основному содержимому

Введение в модернизацию

· 7 мин. чтения

1. Обзор

Retrofit — это типобезопасный HTTP-клиент для Android и Java, разработанный Square ( Dagger , Okhttp ).

В этой статье мы объясним, как использовать Retrofit, уделив особое внимание его наиболее интересным функциям. В частности, мы обсудим синхронный и асинхронный API, как использовать его с аутентификацией, ведением журнала и некоторыми хорошими методами моделирования.

2. Настройка примера

Начнем с добавления библиотеки Retrofit и конвертера Gson:

<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.3.0</version>
</dependency>

Последние версии см. в разделах Retrofit и convert-gson в репозитории Maven Central.

3. API-моделирование

Модернизация моделирует конечные точки REST как интерфейсы Java, делая их очень простыми для понимания и использования.

Мы будем моделировать пользовательский API из GitHub; у этого есть конечная точка GET , которая возвращает это в формате JSON:

{
login: "mojombo",
id: 1,
url: "https://api.github.com/users/mojombo",
...
}

Модернизация работает путем моделирования базового URL-адреса и заставляет интерфейсы возвращать сущности из конечной точки REST.

Для простоты мы возьмем небольшую часть JSON, смоделировав наш класс User , который будет принимать значения, когда мы их получим:

public class User {
private String login;
private long id;
private String url;
// ...

// standard getters an setters

}

Мы можем видеть, что мы берем только подмножество свойств для этого примера. Retrofit не будет жаловаться на отсутствующие свойства — поскольку он сопоставляет только то, что нам нужно , он даже не будет жаловаться, если мы добавим свойства, которых нет в JSON.

Теперь мы можем перейти к моделированию интерфейса и объяснить некоторые аннотации Retrofit:

public interface UserService {

@GET("/users")
public Call<List<User>> getUsers(
@Query("per_page") int per_page,
@Query("page") int page);

@GET("/users/{username}")
public Call<User> getUser(@Path("username") String username);
}

Метаданные, предоставленные с аннотациями, достаточны для создания рабочих реализаций.

Аннотация @GET сообщает клиенту, какой метод HTTP использовать и на каком ресурсе, например, предоставив базовый URL-адрес «https://api.github.com», он отправит запрос на «https://api». .github.com/users».

Ведущий «/» в нашем относительном URL -адресе сообщает Retrofit, что это абсолютный путь на хосте.

Еще одна вещь, которую следует отметить, это то, что мы используем совершенно необязательные параметры @Query , которые могут быть переданы как null, если они нам не нужны, инструмент позаботится об игнорировании этих параметров, если они не имеют значений.

И последнее, но не менее важное: @Path позволяет указать параметр пути, который будет помещен вместо разметки, которую мы использовали в пути.

4. Синхронный/асинхронный API

Чтобы построить вызов HTTP-запроса, нам нужно сначала создать наш объект Retrofit:

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();

Retrofit предоставляет удобный конструктор для создания необходимого нам объекта. Ему нужен базовый URL-адрес, который будет использоваться для каждого вызова службы, и фабрика конвертеров, которая занимается анализом данных, которые мы отправляем, а также получаемых нами ответов.

В этом примере мы будем использовать GsonConverterFactory , который сопоставит наши данные JSON с классом User , который мы определили ранее.

Важно отметить, что разные фабрики служат разным целям, поэтому имейте в виду, что мы также можем использовать фабрики для XML, прото-буферов или даже создавать фабрики для пользовательского протокола. Список уже реализованных фабрик можно посмотреть здесь .

Последняя зависимость — OKHttpClient — клиент HTTP и HTTP/2 для приложений Android и Java. Это позаботится о подключении к серверу, а также об отправке и получении информации. Мы также могли бы добавить заголовки и перехватчики для каждого вызова, что мы увидим в нашем разделе аутентификации.

Теперь, когда у нас есть объект Retrofit, мы можем создать вызов службы, давайте посмотрим, как это сделать синхронно:

UserService service = retrofit.create(UserService.class);
Call<User> callSync = service.getUser("foreach");

try {
Response<User> response = callSync.execute();
User user = response.body();
} catch (Exception ex) { ... }

Здесь мы можем видеть, как Retrofit заботится о построении нашего интерфейса службы, внедряя код, необходимый для выполнения запроса, на основе наших предыдущих аннотаций.

После этого мы получаем объект Call<User> , который используется для выполнения запроса к GitHub API. Самый важный метод здесь — execute , который используется для синхронного выполнения вызова и блокирует текущий поток при передаче данных.

После успешного выполнения вызова мы можем получить тело ответа — уже на пользовательском объекте — благодаря нашей GsonConverterFactory .

Сделать синхронный вызов очень просто, но обычно мы используем неблокирующий асинхронный запрос:

UserService service = retrofit.create(UserService.class);
Call<User> callAsync = service.getUser("foreach");

callAsync.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
User user = response.body();
}

@Override
public void onFailure(Call<User> call, Throwable throwable) {
System.out.println(throwable);
}
});

Теперь вместо метода execute мы используем метод постановки в очередь , который принимает интерфейс Callback<User> в качестве параметра для обработки успешного или неудачного запроса. Обратите внимание, что это будет выполняться в отдельном потоке.

После успешного завершения вызова мы можем получить тело так же, как и раньше.

5. Создание повторно используемого класса ServiceGenerator

Теперь, когда мы увидели, как построить наш объект Retrofit и как использовать API, мы можем понять, что не хотим писать конструктор снова и снова.

Нам нужен повторно используемый класс, который позволит нам создать этот объект один раз и повторно использовать его на протяжении всего времени существования нашего приложения:

public class GitHubServiceGenerator {

private static final String BASE_URL = "https://api.github.com/";

private static Retrofit.Builder builder
= new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());

private static Retrofit retrofit = builder.build();

private static OkHttpClient.Builder httpClient
= new OkHttpClient.Builder();

public static <S> S createService(Class<S> serviceClass) {
return retrofit.create(serviceClass);
}
}

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

Вот простой пример того, как его использовать:

UserService service 
= GitHubServiceGenerator.createService(UserService.class);

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

В следующем разделе мы собираемся расширить его и добавить возможности проверки подлинности.

6. Аутентификация

Большинство API имеют некоторую аутентификацию для безопасного доступа к ним.

Принимая во внимание наш предыдущий класс генератора, мы собираемся добавить метод создания службы, который принимает токен JWT с заголовком Authorization :

public static <S> S createService(Class<S> serviceClass, final String token ) {
if ( token != null ) {
httpClient.interceptors().clear();
httpClient.addInterceptor( chain -> {
Request original = chain.request();
Request request = original.newBuilder()
.header("Authorization", token)
.build();
return chain.proceed(request);
});
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}

Чтобы добавить заголовок к нашему запросу, нам нужно использовать возможности перехватчика OkHttp ; мы делаем это, используя наш ранее определенный построитель и реконструируя объект Retrofit.

Обратите внимание, что это простой пример аутентификации, но с использованием перехватчиков мы можем использовать любую аутентификацию, такую как OAuth, пользователь/пароль и т. д.

7. Ведение журнала

В этом разделе мы собираемся еще больше расширить наш GitHubServiceGenerator для возможностей ведения журнала, которые очень важны для целей отладки в каждом проекте.

Мы собираемся использовать наши предыдущие знания о перехватчиках, но нам нужна дополнительная зависимость, которая является HttpLoggingInterceptor из OkHttp, давайте добавим ее в наш pom.xml :

<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>3.9.0</version>
</dependency>

Теперь давайте расширим наш класс GitHubServiceGenerator :

public class GitHubServiceGenerator {

private static final String BASE_URL = "https://api.github.com/";

private static Retrofit.Builder builder
= new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());

private static Retrofit retrofit = builder.build();

private static OkHttpClient.Builder httpClient
= new OkHttpClient.Builder();

private static HttpLoggingInterceptor logging
= new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BASIC);

public static <S> S createService(Class<S> serviceClass) {
if (!httpClient.interceptors().contains(logging)) {
httpClient.addInterceptor(logging);
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}

public static <S> S createService(Class<S> serviceClass, final String token) {
if (token != null) {
httpClient.interceptors().clear();
httpClient.addInterceptor( chain -> {
Request original = chain.request();
Request.Builder builder1 = original.newBuilder()
.header("Authorization", token);
Request request = builder1.build();
return chain.proceed(request);
});
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}
}

Это окончательная форма нашего класса, мы можем видеть, как мы добавили HttpLoggingInterceptor , и мы установили его для базового ведения журнала, который будет регистрировать время, затраченное на выполнение запроса, конечную точку, статус для каждого запроса и т. д.

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

8. Заключение

В этом обширном руководстве мы рассмотрели превосходную библиотеку Retrofit, сосредоточившись на ее Sync/Async API, некоторых передовых методах моделирования, аутентификации и ведения журналов.

Библиотеку можно использовать очень сложными и полезными способами; для расширенного варианта использования RxJava, пожалуйста, ознакомьтесь с этим руководством .

И, как всегда, исходный код можно найти на GitHub .