Используем Retrofit 2 в Android-приложении
Retrofit — это известная среди Android-разработчиков библиотека для сетевого взаимодействия, некоторые даже считают её в каком-то роде стандартом. Причин для такой популярности масса: библиотека отлично поддерживает REST API, легко тестируется и настраивается, а запросы по сети с её помощью выполняются совсем просто. В этой статье я покажу вам, как настроить и использовать Retrofit, чтобы реализовать работу с сетью в вашем приложении.
Настройка Retrofit
Добавьте следующую зависимость в файл build.gradle:
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
Мы будем использовать Gson для преобразования JSON в POJO. Retrofit предоставляет зависимость, которая автоматически конвертирует JSON в POJO. Для этого добавьте ещё одну зависимость в файл build.gradle:
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
Если в вашем приложении ещё не разрешена работа с сетью, то обязательно добавьте соответствующую строку в файл AndroidManifest:
<uses-permission android:name="android.permission.INTERNET"/>
После того как зависимости добавлены, нам необходимо написать код для настройки библиотеки Retrofit.
Создайте класс-объект с именем NetworkService (он будет синглтоном):
object NetworkService { }
Для тестирования мы используем JSONPlaceholder, который предоставляет фейковый онлайн REST API для разработчиков:
https://jsonplaceholder.typicode.com
Теперь мы объявим и инициализируем Retrofit в инициализаторе NetworkService:
object NetworkService { private const val BASE_URL = "https://jsonplaceholder.typicode.com" private retrofit: Retrofit init { retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() } }
Настройка завершена, теперь нам нужно определить конечные точки (endpoints), которые будут возвращать данные.
Добавление конечных точек
Создайте интерфейс с именем JSONPlaceHolderApi:
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" }
Сначала мы создадим соответствующий data-класс для JSON-ответа:
data class Post( var userId: Int, var id: Int, var title: String, var body: String )
Как вы видите, это простой data-класс. Имена свойств фактически являются ключами в возвращаемых из API JSON-данным, поэтому не стоит изменять имя переменной. Если же хотите его изменить - используйте аннотацию @SerializedName() над нужным свойством, передав туда точное название поля в JSON. В интерфейсе, созданном выше, определите конечные точки с требуемыми параметрами:
interface JSONPlaceHolderApi { @GET("/posts/{id}") fun getPostWithID(@Path("id") id: Int): Call<Post> }
Поскольку мы отправляем 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():
object NetworkService { private const val BASE_URL = "https://jsonplaceholder.typicode.com" private retrofit: Retrofit init { retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() } fun getJSONApi(): JSONPlaceHolderApi { return mRetrofit.create(JSONPlaceHolderApi.class) } }
Далее нужно получить JSONPlaceHolderApi из NetworkService и отправить запрос:
NetworkService.getJSONApi() .getPostWithID(1) .enqueue(object : Callback<Post>() { override fun onResponse(call: Call<Post>, response: Response<Post>) { val post = response.body() textView.append(post.getId() + "\n") textView.append(post.getUserId() + "\n") textView.append(post.getTitle() + "\n") textView.append(post.getBody() + "\n") } override fun onFailure(call: Call<Post>, t: Throwable) { textView.append("Error occurred while getting request!") t.printStackTrace() } })
Возвращённый объект Call содержит метод с именем enqueue, который принимает в качестве параметра Callback<T>. В onResponse мы получаем результат Response<Post>, содержащий возвращённый с сервера объект Post. Чтобы получить сам объект Post, используем метод response.body(). Остальная часть кода понятна без дополнительных пояснений.
Отправка различных типов запросов
В API JSONPlaceHolder есть много разных конечных точек, которые можно использовать.
Получение списка сообщений
Для получения списка всех сообщений изменим конечную точку и возвращаемый тип функции.
@GET("/posts") fun getAllPosts(): Call<List<Post>>
Отправка запроса с параметром
Если вы хотите отправить запрос с параметром, то нужно всего лишь использовать аннотацию @Query() для соответствующего параметра в методе:
@GET("/posts") fun getPostOfUser(@Query("userId") id: Int): Call<List<Post>>
Поэтому если мы передадим значение 6 в параметре метода, то конечная точка будет следующей — /posts?userId=6
.
Отправка POST запроса
Для отправки POST-запроса нужно просто изменить аннотацию метода.
@POST("/posts") fun postData(@Body data: Post): Call<Post>
Чтобы сформировать тело запроса для данного метода, мы используем аннотацию @Body для передаваемого параметра. Retrofit будет использовать Gson для конвертации @Body в JSON.
Существует ещё несколько различных типов запросов, которые вы можете использовать, но это уже тема для отдельной статьи.
Перехват запросов
Retrofit предоставляет способ перехвата запросов и их логирования в Logcat. Давайте настроим перехватчик и посмотрим на эту магию. Добавьте следующую зависимость в файл build.gradle:
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
Обновите класс NetworkService таким образом:
object NetworkService { private const val BASE_URL = "https://jsonplaceholder.typicode.com" private retrofit: Retrofit init { val interceptor = HttpLoggingInterceptor() interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) val client = OkHttpClient.Builder() .addInterceptor(interceptor) val retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(client.build()) .build() } fun getJSONApi(): JSONPlaceHolderApi { 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: { "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.