Retrofit — это известная среди Android-разработчиков библиотека для сетевого взаимодействия, некоторые даже считают её в каком-то роде стандартом. Причин для такой популярности масса: библиотека отлично поддерживает REST API, легко тестируется и настраивается, а запросы по сети с её помощью выполняются совсем просто. В этой статье я покажу вам, как настроить и использовать Retrofit, чтобы реализовать работу с сетью в вашем приложении.
Настройка Retrofit
Добавьте следующую зависимость в файл build.gradle
:
compile 'com.squareup.retrofit2:retrofit:2.4.0'
Мы будем использовать Gson для преобразования JSON в POJO. Retrofit предоставляет зависимость, которая автоматически конвертирует JSON в POJO. Для этого добавьте ещё одну зависимость в файл build.gradle
:
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
Если в вашем приложении ещё не разрешена работа с сетью, то обязательно добавьте соответствующуу строку в файл AndroidManifest
:
<uses-permission android:name="android.permission.INTERNET"/>
После того, как зависимости добавлены, нам необходимо написать код для настройки библиотеки Retrofit.
Создайте класс с именем NetworkService
:
public class NetworkService {
}
Этот класс должен быть singleton-объектом, поэтому объявите статическую переменную и функцию, которая создаёт и возвращает переменную того же типа, что и класс. Если вы не знаете, как работает данный паттерн, то ознакомьтесь с этой статьёй, в которой есть примеры реализации на языке Java.
public class NetworkService {
private static NetworkService mInstance;
public static NetworkService getInstance() {
if (mInstance == null) {
mInstance = new NetworkService();
}
return mInstance;
}
}
Для тестирования мы используем JSONPlaceholder, который предоставляет фейковый онлайн REST API для разработчиков:
https://jsonplaceholder.typicode.com
Теперь мы объявим и инициализируем Retrofit в конструкторе NetworkService
:
public class NetworkService {
private static NetworkService mInstance;
private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
private Retrofit mRetrofit;
private NetworkService() {
mRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static NetworkService getInstance() {
if (mInstance == null) {
mInstance = new NetworkService();
}
return mInstance;
}
}
Настройка завершена, теперь нам нужно определить конечные точки, которые будут возвращать данные.
Добавление конечных точек
Создайте интерфейс с именем JSONPlaceHolderApi
:
public interface JSONPlaceHolderApi {
}
На сайте JSONPlaceHolder URL /posts/id
— это конечная точка, которая возвращает сообщение с соответствующим идентификатором. Данная конечная точка принимает GET-запрос и возвращает данные в формате JSON в следующем виде:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Сначала мы создадим соответствующий POJO для JSON-ответа:
public class Post {
@SerializedName("userId")
@Expose
private int userId;
@SerializedName("id")
@Expose
private int id;
@SerializedName("title")
@Expose
private String title;
@SerializedName("body")
@Expose
private String body;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
Как вы видите, это простой POJO-класс. Переменные мы аннотировали с помощью @SerializedName()
, передав туда имя. Эти имена фактически являются ключами в возвращаемых из API JSON-данных, поэтому вы можете как угодно изменять имя переменной, но убедитесь, что имя, переданное в аннотацию @SerializedName()
, точно присутствует в JSON.
В интерфейсе, созданном выше, определите конечные точки с требуемыми параметрами:
public interface JSONPlaceHolderApi {
@GET("/posts/{id}")
public Call<Post> getPostWithID(@Path("id") int id);
}
Поскольку мы отправляем GET-запрос, нам нужно применить к методу аннотацию @GET
, внутри которой находится конечная точка, на которую мы хотим отправить запрос. Как вы видите, мы не добавляем полный URL, т.к. Retrofit автоматически возьмёт BASE_URL
, переданный в класс NetworkService
, и добавит его к остальной части URL-адреса.
Возвращаемый тип метода называется Call<Post>
. Call
— это класс, предоставляемый непосредственно самой библиотекой. И все методы в интерфейсе должны возвращать значения этого типа. Это generic-класс, принимающий в себя тип объекта, который мы хотим конвертировать в JSON. Мы передали Post
, т.к. это именно тот объект, в который хотим преобразовать JSON-ответ. В параметры мы передали целое число и аннотировали его с помощью @Path
, внутри которого записали id
. Retrofit возьмёт это значение и в конечной точке заменит на него {id}
. Таким образом, если мы передадим значение 1 в качестве параметра, то конечная точка будет выглядеть так — /posts/1
, если передадим значение 10, то конечная точка получится — /posts/10
.
Теперь нам нужно, чтобы Retrofit предоставил реализацию интерфейса JSONPlaceHolderApi
. Для этого используем метод create()
:
public class NetworkService {
private static NetworkService mInstance;
private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
private Retrofit mRetrofit;
private NetworkService() {
mRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static NetworkService getInstance() {
if (mInstance == null) {
mInstance = new NetworkService();
}
return mInstance;
}
public JSONPlaceHolderApi getJSONApi() {
return mRetrofit.create(JSONPlaceHolderApi.class);
}
}
Далее нужно получить JSONPlaceHolderApi
из NetworkService
и отправить запрос:
NetworkService.getInstance()
.getJSONApi()
.getPostWithID(1)
.enqueue(new Callback<Post>() {
@Override
public void onResponse(@NonNull Call<Post> call, @NonNull Response<Post> response) {
Post post = response.body();
textView.append(post.getId() + "\n");
textView.append(post.getUserId() + "\n");
textView.append(post.getTitle() + "\n");
textView.append(post.getBody() + "\n");
}
@Override
public void onFailure(@NonNull Call<Post> call, @NonNull Throwable t) {
textView.append("Error occurred while getting request!");
t.printStackTrace();
}
});
Возвращённый объект Call
содержит метод с именем enqueue
, который принимает в качестве параметра Callback<T>
. В onResponse
мы получаем результат Response<Post>
, содержащий возвращённый с сервера объект Post
. Чтобы получаем сам объект Post
, используем метод response.body()
. Остальная часть кода понятна без дополнительных пояснений.
Отправка различных типов запросов
В API JSONPlaceHolder
есть много разных конечных точек, которые можно использовать.
Получение списка сообщений
@GET("/posts")
public Call<List<Post>> getAllPosts();
Для получения списка всех сообщений мы изменили конечную точку и возвращаемый тип функции.
Отправка запроса с параметром
Если вы хотите отправить запрос с параметром, то нужно всего лишь использовать аннотацию @Query()
для соответствующего параметра в методе:
@GET("/posts")
public Call<List<Post>> getPostOfUser(@Query("userId") int id);
Поэтому если мы передадим значение 6 в параметре метода, то конечная точка будет следующей — /posts?userId=6
.
Отправка POST запроса
Для отправки POST-запроса нужно просто изменить аннотацию метода.
@POST("/posts")
public Call<Post> postData(@Body Post data);
Чтобы сформировать тело запроса для данного метода, мы используем аннотацию @Body
для передаваемого параметра. Retrofit будет использовать Gson для конвертации @Body
в JSON.
Существует ещё несколько различных типов запросов, которые вы можете использовать, но это уже тема для отдельной статьи.
Перехват запросов
Retrofit предоставляет способ перехвата запросов и их логирования в Logcat. Давайте настроим перехватчик и посмотрим на эту магию. Добавьте следующую зависимость в файл build.gradle
:
compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
Обновите класс NetworkService
таким образом:
public class NetworkService {
private static NetworkService mInstance;
private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
private Retrofit mRetrofit;
private NetworkService() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder()
.addInterceptor(interceptor);
mRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client.build())
.build();
}
public static NetworkService getInstance() {
if (mInstance == null) {
mInstance = new NetworkService();
}
return mInstance;
}
public JSONPlaceHolderApi getJSONApi() {
return mRetrofit.create(JSONPlaceHolderApi.class);
}
}
Теперь, когда вы отправляете или получаете запрос, все его данные, включая URL, заголовки, тело, будут выведены в лог:
D/OkHttp: <-- 200 https://jsonplaceholder.typicode.com/posts/1 (3030ms)
date: Tue, 24 Apr 2018 15:25:19 GMT
content-type: application/json; charset=utf-8
set-cookie: __cfduid=d16d4221ddfba20b5464e6829eed4e3d11524583519; expires=Wed, 24-Apr-19 15:25:19 GMT; path=/; domain=.typicode.com; HttpOnly
x-powered-by: Express
vary: Origin, Accept-Encoding
access-control-allow-credentials: true
cache-control: public, max-age=14400
pragma: no-cache
expires: Tue, 24 Apr 2018 19:25:19 GMT
04-24 15:25:16.204 7023-7056/com.thetehnocafe.gurleensethi.retrofitexample D/OkHttp: x-content-type-options: nosniff
etag: W/"124-yiKdLzqO5gfBrJFrcdJ8Yq0LGnU"
via: 1.1 vegur
cf-cache-status: HIT
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 410994f328963066-SIN
04-24 15:25:16.246 7023-7056/com.thetehnocafe.gurleensethi.retrofitexample D/OkHttp: {
04-24 15:25:16.247 7023-7056/com.thetehnocafe.gurleensethi.retrofitexample D/OkHttp: "userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
<-- END HTTP (292-byte body)
На этом наша статья заканчивается. Код для этого проекта вы можете найти на GitHub.
Комментарии (2)
shok96
16.11.2018 10:18Мне кажется, для новичков будет полезно узнать как развернуть сетевую часть в своем приложение.
agent10
Ну сколько можно ещё? Ждём перевод какой-нибудь статьи от индуса как создать новый Андроид проект в студии.