Что такое модернизация?
Retrofit — это безопасный для типов HTTP-клиент для Android и Java. Модернизация позволяет легко подключаться к веб-службе REST путем перевода API в интерфейсы Java. В этом уроке я покажу вам, как использовать одну из самых популярных и часто рекомендуемых библиотек HTTP, доступных для Android.
Эта мощная библиотека позволяет легко использовать данные JSON или XML, которые затем анализируются в простые старые объекты Java (POJO). Запросы GET
, POST
, PUT
, PATCH
и DELETE
могут быть выполнены.
Как и большинство программного обеспечения с открытым исходным кодом, Retrofit был построен поверх некоторых других мощных библиотек и инструментов. За кулисами Retrofit использует OkHttp (от того же разработчика) для обработки сетевых запросов. Кроме того, Retrofit не имеет встроенного конвертера JSON для анализа объектов JSON в Java. Вместо этого он поддерживает следующие библиотеки JSON Converter для этого:
- Gson:
com.squareup.retrofit:converter-gson
- Джексон:
com.squareup.retrofit:converter-jackson
- Моши:
com.squareup.retrofit:converter-moshi
Для буферов протокола Retrofit поддерживает:
- Protobuf:
com.squareup.retrofit2:converter-protobuf
- Провод:
com.squareup.retrofit2:converter-wire
А для XML Retrofit поддерживает:
- Простая структура:
com.squareup.retrofit2:converter-simpleframework
Так зачем использовать модернизацию?
Разработка собственной безопасной HTTP-библиотеки для взаимодействия с REST API может быть очень сложной задачей: вам приходится иметь дело со многими аспектами, такими как создание соединений, кэширование, повторение неудачных запросов, многопоточность, анализ ответов, обработка ошибок и многое другое. С другой стороны, дооснащение — это хорошо спланированная, документированная и протестированная библиотека, которая сэкономит вам много драгоценного времени и головной боли.
В этом руководстве я объясню, как использовать Retrofit 2 для обработки сетевых запросов, создав простое приложение, которое будет выполнять запросы POST
, PUT
(для обновления сущностей) и DELETE
. Я также покажу вам, как интегрироваться с RxJava и как отменять запросы. Мы будем использовать API, предоставляемый JSONPlaceholder — это поддельный онлайн REST API для тестирования и создания прототипов.
Прочтите мою предыдущую статью «Начало работы с HTTP- клиентом Retrofit 2» , чтобы узнать, как выполнять запросы GET
и как интегрировать Retrofit с RxJava.
1. Создайте проект Android Studio
MainActivity
Android Studio и создайте новый проект с пустым действием MainActivity
.
2. Объявление зависимостей
После создания нового проекта объявите следующие зависимости в вашем build.gradle
. Зависимости включают библиотеку Retrofit, а также библиотеку Google Gson для преобразования JSON в POJO (простые старые объекты Java), а также интеграцию Retrofit Gson.
1
2
3
4
5
6
|
// Retrofit
compile ‘com.squareup.retrofit2:retrofit:2.1.0’
// JSON Parsing
compile ‘com.google.code.gson:gson:2.6.1’
compile ‘com.squareup.retrofit2:converter-gson:2.1.0’
|
Убедитесь, что вы синхронизируете свой проект после добавления зависимостей.
3. Добавление интернет-разрешения
Для выполнения сетевых операций нам необходимо включить разрешение INTERNET
в манифест приложения: AndroidManifest.xml .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?xml version=»1.0″ encoding=»utf-8″?>
<manifest xmlns:android=»http://schemas.android.com/apk/res/android»
package=»com.chikeandroid.retrofittutorial2″>
<uses-permission android:name=»android.permission.INTERNET» />
<application
android:allowBackup=»true»
android:icon=»@mipmap/ic_launcher»
android:label=»@string/app_name»
android:supportsRtl=»true»
android:theme=»@style/AppTheme»>
<activity android:name=».PostActivity»>
<intent-filter>
<action android:name=»android.intent.action.MAIN»/>
<category android:name=»android.intent.category.LAUNCHER»/>
</intent-filter>
</activity>
</application>
</manifest>
|
4. Генерация моделей автоматически
Мы собираемся создавать модели автоматически из данных ответов JSON, используя очень полезный инструмент: jsonschema2pojo . Мы хотели бы сделать запрос POST
(создать новый ресурс) на API. Но прежде чем выполнить этот запрос, нам нужно знать ответ JSON, который мы должны ожидать, когда он будет выполнен успешно, чтобы Retrofit мог проанализировать ответ JSON и десериализовать его для объектов Java. Согласно API , если мы отправим следующие данные в запросе POST
:
1
2
3
4
5
|
data: {
title: ‘foo’,
body: ‘bar’,
userId: 1
}
|
Мы должны получить следующий ответ:
1
2
3
4
5
6
|
{
«title»: «foo»,
«body»: «bar»,
«userId»: 1,
«id»: 101
}
|
Сопоставьте данные JSON с Java
Скопируйте пример ответа из предыдущего раздела. Теперь посетите jsonschema2pojo и вставьте ответ JSON в поле ввода. Выберите тип источника JSON , стиль аннотации Gson , снимите флажок Разрешить дополнительные свойства и измените имя класса с Пример на Пост .
Затем нажмите кнопку « Просмотр» , чтобы сгенерировать объекты Java.
Вам может быть интересно, что @SerializedName
аннотации @SerializedName
и @Expose
в этом сгенерированном коде! Не волнуйся, я все объясню!
Аннотация @SerializedName
необходима Gson для сопоставления ключей JSON с полями объектов Java.
1
2
3
|
@SerializedName(«userId»)
@Expose
private Integer userId;
|
В этом случае ключ userId
JSON сопоставляется с полем класса userId
. Но обратите внимание, что, поскольку они одинаковы, нет необходимости включать аннотацию @SerializedName
в поле, потому что Gson отобразит ее автоматически для нас.
Аннотация @Expose
указывает, что член класса должен быть @Expose
для сериализации или десериализации JSON.
Импорт моделей данных в Android Studio
Теперь вернемся к Android Studio. Создайте новый подпакет внутри main
пакета и назовите его data
. Внутри нового пакета создайте другой пакет и назовите его model
. Внутри этого пакета создайте новый класс Java и назовите его Post
. Теперь скопируйте класс Post
, созданный jsonschema2pojo, и вставьте его в класс Post
вы создали.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package com.chikeandroid.retrofittutorial2.data.model;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Post {
@SerializedName(«title»)
@Expose
private String title;
@SerializedName(«body»)
@Expose
private String body;
@SerializedName(«userId»)
@Expose
private Integer userId;
@SerializedName(«id»)
@Expose
private Integer 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;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String toString() {
return «Post{» +
«title=’» + title + ‘\» +
«, body=’» + body + ‘\» +
«, userId=» + userId +
«, id=» + id +
‘}’;
}
}
|
Помимо методов получения и установки, я также включил метод toString()
. (В Intellij вы можете использовать команду Generate, чтобы упростить это: Alt-Insert в Windows или Command-N в macOS.)
5. Создание экземпляра модернизации
Чтобы отправлять сетевые запросы к RESTful API с Retrofit, нам нужно создать экземпляр с помощью класса Retrofit Builder и настроить его с помощью базового URL.
Создайте новый подпакет внутри пакета data
и назовите его remote
. Теперь внутри этого пакета создайте класс Java и назовите его RetrofitClient
. Этот класс создаст синглтон Retrofit в методе getClient(String baseUrl)
и вернет его вызывающей стороне.
Как я упоминал ранее, Retrofit нужен базовый URL для создания своего экземпляра, поэтому мы передадим ему URL при вызове RetrofitClient.getClient(String baseUrl)
. Затем этот URL будет использоваться для построения экземпляра в строке 12. Мы также указываем нужный нам JSON-конвертер (Gson) в строке 13.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
package com.chikeandroid.retrofittutorial2.data.remote;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl) {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
|
6. Создание интерфейса API
Внутри удаленного пакета создайте интерфейс и назовите его APIService
. Этот интерфейс содержит методы, которые мы собираемся использовать для выполнения HTTP-запросов, таких как POST
, PUT
и DELETE
. Начнем с запроса POST
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
package com.chikeandroid.retrofittutorial2.data.remote;
import com.chikeandroid.retrofittutorial2.data.model.Post;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
public interface APIService {
@POST(«/posts»)
@FormUrlEncoded
Call<Post> savePost(@Field(«title») String title,
@Field(«body») String body,
@Field(«userId») long userId);
}
|
Глядя на класс APIService
, у нас есть метод с именем savePost()
. Сверху метода находится аннотация @POST
, которая указывает, что мы хотим выполнить запрос POST
при вызове этого метода. Значением аргумента для аннотации @POST
является конечная точка, то есть /posts
. Таким образом, полный URL будет http://jsonplaceholder.typicode.com/posts .
Хорошо, а как насчет @FormUrlEncoded
? Это будет указывать на то, что у запроса будет свой тип MIME (поле заголовка, которое определяет формат тела HTTP-запроса или ответа), установленный на application/x-www-form-urlencoded
а также что его имена и значения будут UTF-8 кодируется перед URI-кодированием. @Field("key")
с именем параметра должна соответствовать имени, @Field("key")
API. Модернизация неявно преобразует значения в строки, используя String.valueOf(Object)
, и строки затем формируются в URL-кодированном виде. null
значения игнорируются.
Например, вызов APIService.savePost("My Visit To Lagos", "I visited...", 2)
выдает тело запроса с title=My+Visit+To+Lagos&body=I+visited...&userId=2
.
Использование аннотации @Body
Мы также можем использовать аннотацию @Body
для параметра метода службы вместо указания тела запроса в форме формы с несколькими отдельными полями. Объект будет сериализован с использованием Converter
экземпляра Retrofit
указанного при создании. Это используется только при выполнении операции POST
или PUT
.
1
2
3
|
@POST(«/posts»)
@FormUrlEncoded
Call<Post> savePost(@Body Post post);
|
7. Создание утилит API
Мы собираемся создать служебный класс. Поэтому создайте класс в data.remote
и назовите его ApiUtils
. Этот класс будет иметь базовый URL-адрес в качестве статической переменной, а также предоставит интерфейс APIService
с помощью статического метода getAPIService()
для остальной части нашего приложения.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
package com.chikeandroid.retrofittutorial2.data.remote;
public class ApiUtils {
private ApiUtils() {}
public static final String BASE_URL = «http://jsonplaceholder.typicode.com/»;
public static APIService getAPIService() {
return RetrofitClient.getClient(BASE_URL).create(APIService.class);
}
}
|
Убедитесь, что в конце базового URL-адреса указан символ /
.
8. Создание макета
Файл activity_main.xml — это макет нашей MainActivity
. Этот макет будет иметь одно поле для редактирования текста для заголовка сообщения и другое для тела сообщения. Также имеется кнопка для отправки сообщения в API.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout
xmlns:android=»http://schemas.android.com/apk/res/android»
xmlns:tools=»http://schemas.android.com/tools»
android:id=»@+id/activity_post»
android:orientation=»vertical»
android:layout_width=»match_parent»
android:layout_height=»match_parent»
android:paddingLeft=»@dimen/activity_horizontal_margin»
android:paddingRight=»@dimen/activity_horizontal_margin»
android:paddingTop=»@dimen/activity_vertical_margin»
android:paddingBottom=»@dimen/activity_vertical_margin»
tools:context=»com.chikeandroid.retrofittutorial2.AddEditPostActivity»>
<TextView
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:gravity=»center_horizontal»
android:textAppearance=»@style/TextAppearance.AppCompat.Title»
android:text=»@string/title_enter_post»/>
<EditText
android:id=»@+id/et_title»
android:layout_marginTop=»18dp»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:hint=»@string/hint_title»/>
<EditText
android:id=»@+id/et_body»
android:lines=»4″
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:hint=»@string/hint_body»/>
<Button
android:id=»@+id/btn_submit»
android:layout_marginTop=»18dp»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:background=»@color/colorAccent»
android:textColor=»@android:color/white»
android:text=»@string/action_submit»/>
<TextView
android:id=»@+id/tv_response»
android:layout_marginTop=»35dp»
android:visibility=»gone»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»/>
</LinearLayout>
|
9. Выполнение запроса POST
В onCreate()
в MainActivity
мы инициализируем экземпляр интерфейса APIService
(строка 14). Мы также инициализируем поля EditText
и кнопку отправки, которая при нажатии sendPost()
метод sendPost()
(строка 22).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private TextView mResponseTv;
private APIService mAPIService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText titleEt = (EditText) findViewById(R.id.et_title);
final EditText bodyEt = (EditText) findViewById(R.id.et_body);
Button submitBtn = (Button) findViewById(R.id.btn_submit);
mResponseTv = (TextView) findViewById(R.id.tv_response);
mAPIService = ApiUtils.getAPIService();
submitBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String title = titleEt.getText().toString().trim();
String body = bodyEt.getText().toString().trim();
if(!TextUtils.isEmpty(title) && !TextUtils.isEmpty(body)) {
sendPost(title, body);
}
}
});
}
|
В sendPost(String, String)
в классе MainActivity
мы передали заголовок и тело сообщения этому методу. Этот метод будет вызывать наш метод интерфейса службы API savePost(String, String)
, задачей которого является выполнение запроса POST
отправляющего заголовок и тело в API. Метод showResponse(String response)
отобразит ответ на экране.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public void sendPost(String title, String body) {
mAPIService.savePost(title, body, 1).enqueue(new Callback<Post>() {
@Override
public void onResponse(Call<Post> call, Response<Post> response) {
if(response.isSuccessful()) {
showResponse(response.body().toString());
Log.i(TAG, «post submitted to API.» + response.body().toString());
}
}
@Override
public void onFailure(Call<Post> call, Throwable t) {
Log.e(TAG, «Unable to submit post to API.»);
}
});
}
public void showResponse(String response) {
if(mResponseTv.getVisibility() == View.GONE) {
mResponseTv.setVisibility(View.VISIBLE);
}
mResponseTv.setText(response);
}
|
Наш метод savePost(String, String)
mAPIService
savePost(String, String)
вернет экземпляр Call
которого есть метод с именем enqueue(Callback<T> callback)
.
Понимание enqueue()
enqueue()
асинхронно отправляет запрос и уведомляет ваше приложение обратным вызовом, когда возвращается ответ. Поскольку этот запрос асинхронный, Retrofit обрабатывает выполнение в фоновом потоке, чтобы основной поток пользовательского интерфейса не блокировался и не мешал.
Чтобы использовать метод enqueue()
, вы должны реализовать два метода обратного вызова: onResponse()
и onFailure()
. Только один из этих методов будет вызван в ответ на данный запрос.
-
onResponse()
: вызывается для полученного ответа HTTP. Этот метод вызывается для ответа, который можно правильно обработать, даже если сервер возвращает сообщение об ошибке. Поэтому, если вы получите код состояния 404 или 500, этот метод все равно будет вызываться. Чтобы получить код состояния для обработки ситуаций на их основе, вы можете использовать методresponse.code()
. Вы также можете использовать методisSuccessful()
чтобы выяснить, находится ли код состояния в диапазоне 200-300, что указывает на успех. -
onFailure()
: вызывается, когда при подключении к серверу возникло сетевое исключение или произошло непредвиденное исключение при обработке запроса или обработке ответа.
Синхронные Запросы
Для выполнения синхронного запроса вы можете использовать метод execute()
в экземпляре Call
. Но имейте в виду, что синхронные методы в основном / пользовательском интерфейсе будут блокировать любые действия пользователя. Поэтому не выполняйте синхронные методы в основном потоке Android / пользовательском интерфейсе! Вместо этого запустите их в фоновом потоке.
Использование RxJava
RxJava был интегрирован в Retrofit 1 по умолчанию, но в Retrofit 2 вам необходимо включить некоторые дополнительные зависимости. Модификация поставляется с адаптером по умолчанию для выполнения экземпляров Call
. Таким образом, вы можете изменить механизм выполнения Retrofit, включив в него RxJava, включив RxJava CallAdapter
. Вот эти шаги:
Шаг 1
Добавьте зависимости.
1
2
3
|
compile ‘io.reactivex:rxjava:1.1.6’
compile ‘io.reactivex:rxandroid:1.2.1’
compile ‘com.squareup.retrofit2:adapter-rxjava:2.1.0’
|
Шаг 2
Добавьте новый CallAdapter RxJavaCallAdapterFactory.create()
при создании экземпляра Retrofit (строка 5).
01
02
03
04
05
06
07
08
09
10
|
public static Retrofit getClient(String baseUrl) {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
|
Шаг 3
Обновите метод APIService
savePost(String title, String body, String userId)
чтобы он стал Observable.
1
2
3
4
5
|
@POST(«/posts»)
@FormUrlEncoded
Observable<Post> savePost(@Field(«title») String title,
@Field(«body») String body,
@Field(«userId») long userId);
|
Шаг 4
При выполнении запросов наш анонимный подписчик отвечает на поток наблюдаемой, который генерирует события, в нашем случае Post
. Затем метод onNext
вызывается, когда наш подписчик получает какое-либо событие, которое затем передается в наш showResponse(String response)
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public void sendPost(String title, String body) {
// RxJava
mAPIService.savePost(title, body, 1).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Post>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Post post) {
showResponse(post.toString());
}
});
}
|
Чтобы узнать больше о RxJava и RxAndroid, ознакомьтесь с разделом Начало работы с ReactiveX на Android от Ашрафа Хатибелагала.
10. Тестирование приложения
На этом этапе вы можете запустить приложение и нажать кнопку отправки, когда вы ввели заголовок и текст. Ответ от API будет показан ниже кнопки отправки.
11. Выполнение запроса PUT
Теперь, когда мы знаем, как выполнить запрос POST
, давайте посмотрим, как мы можем выполнить запрос PUT
который обновляет сущности. Добавьте следующий новый метод в класс APIService
.
1
2
3
4
5
6
|
@PUT(«/posts/{id}»)
@FormUrlEncoded
Call<Post> updatePost(@Path(«id») long id,
@Field(«title») String title,
@Field(«body») String body,
@Field(«userId») long userId);
|
Чтобы обновить сообщение из API, у нас есть конечная точка /posts/{id}
где {id}
является заполнителем для идентификатора сообщения, которое мы хотим обновить. Аннотация @Path
— это именованная замена в сегменте пути URL {id}
. Помните, что значения преобразуются в строку с использованием String.valueOf(Object)
и закодированного URL-адреса. Если значение уже закодировано, вы можете отключить кодировку URL следующим образом: @Path(value="name", encoded=true)
.
12. Выполнение запроса УДАЛИТЬ
Давайте также посмотрим, как выполнить запрос DELETE
. Используя API-интерфейс JSONPlaceholder , для удаления ресурса публикации требуется конечная точка /posts/{id}
с методом HTTP DELETE
. Возвращаясь к нашему интерфейсу APIService
, нам просто нужно включить метод deletePost()
который будет его выполнять. Мы передаем id сообщения методу, и он заменяется в сегменте пути URL {id}
.
1
2
|
@DELETE(«/posts/{id}»)
Call<Post> deletePost(@Path(«id») long id);
|
13. Отмена запроса
Допустим, вы хотите дать своим пользователям возможность отменить или отменить запрос. Это очень легко сделать в Retrofit. В классе Retrofit Call
есть метод cancel()
который будет делать именно это (строка 32 ниже). Этот метод вызовет метод onFailure()
в onFailure()
.
Этот метод может быть вызван, например, если нет подключения к Интернету или когда произошло непредвиденное исключение при создании запроса или обработке ответа. Чтобы узнать, был ли запрос отменен, используйте метод isCanceled()
в классе Call
(строка 20).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
private Call<Post> mCall;
…
public sendPost(String title, String body) {
mCall = mAPIService.savePost(title, body, 1);
mCall.enqueue(new Callback<Post>() {
@Override
public void onResponse(Call<Post> call, Response<Post> response) {
if(response.isSuccessful()) {
showResponse(response.body().toString());
Log.i(TAG, «post submitted to API.» + response.body().toString());
}
}
@Override
public void onFailure(Call<Post> call, Throwable t) {
if(call.isCanceled()) {
Log.e(TAG, «request was aborted»);
}else {
Log.e(TAG, «Unable to submit post to API.»);
}
showErrorMessage();
}
});
}
public void cancelRequest() {
mCall.cancel();
}
|
Вывод
В этом уроке вы узнали о Retrofit: почему вы должны его использовать и как интегрировать его в ваш проект для выполнения запросов POST
, PUT
, DELETE
и отмены. Вы также узнали, как интегрировать RxJava с ним. В моем следующем сообщении об использовании Retrofit я покажу вам, как загружать файлы.
Чтобы узнать больше о модернизации, обратитесь к официальной документации . И ознакомьтесь с некоторыми другими нашими учебными пособиями и курсами по разработке Android здесь на Envato Tuts +!