Всемирная паутина является готовой платформой для создания и использования распределенных систем на основе веб-служб. Веб-сервер выступает в качестве сервера приложений, к которым обращаются не конечные пользователи, а сторонние приложения. Это позволяет многократно использовать функциональные элементы, устранить дублирование кода, упростить решение задач интеграции приложений.
Веб-служба или веб-сервис (web-service) – сетевая технология, обеспечивающая межпрограммное взаимодействие на основе веб-стандартов. W3C определяет веб-службу как «программную систему, разработанную для поддержки интероперабельного межкомпьютерного (machine-to-machine) взаимодействия через сеть».
К моменту появления веб-служб уже существовали технологии, позволяющие приложениям взаимодействовать на расстоянии, где одна программа могла вызвать какой-нибудь другой метод в другой программе, которая при этом могла быть запущена на компьютере, расположенном в другом городе или даже стране. Это сокращенно называется RPC(RemoteProcedureCalling– удаленный вызов процедур). В качестве примеров можно привести технологии CORBA, а для Java – RMI (Remote Method Invoking – удаленный вызов методов).
Идея веб-службы заключалась в создании такого RPC, который будет упаковываться в HTTP пакеты. Такой подход стал очень популярным, т.к. HTTP был хорошо известен, прост, понятен и обеспечивал лучшее «прохождение» через различные firewall`ы. Именно с появлением веб-сервисов развилась идея SOA– сервис-ориентированной архитектуры веб-приложений (Service Oriented Architecture).
Протоколы веб-сервисов
На сегодняшний день наибольшее распространение получили следующие протоколы реализации веб-служб:
SOAP (Simple Object Access Protocol) – тройка стандартов SOAP/WSDL/UDDI. Сообщения упаковываются в виде структуры, которая называется конверт (envelope), которая включает идентификатор сообщения, заголовок и тело сообщения;
REST (Representational State Transfer) – архитектурный стиль, который использует концепцию ресурсов и определяет операции через методы HTTP-протокола;
XML-RPC (XML Remote Procedure Call) – вызов удаленных процедур, использующий XML для кодирования своих сообщений и HTTP в качестве транспортного механизма.
Архитектурный стиль REST
Передача состояния представления (Representational State Transfer (REST)) является архитектурным стилем, в котором веб-службы рассматриваются, как ресурсы и могут быть идентифицированы Унифицированными идентификаторами ресурсов (Uniform Resource Identifiers (URI)).
Веб-службы, разработанные в стиле REST и с учетом ограничений REST, известны как RESTfulвеб-службы.
Каждая единица информации в REST называется ресурсом и имеет однозначный URI, который является ее, своего рода, первичным ключом. То есть, например, третья книга с книжной полки будет иметь URI /book/3, а 35-ая страница в этой книге – /book/3/page/35/. Отсюда и получается строго заданный формат. Причем совершенно не имеет значения, в каком формате находятся данные по адресу /book/3/page/35/ – это может быть и HTML, и отсканированная копия книги в виде jpeg-файла и документ Microsoft Word.
Над ресурсами выполняется ряд простых четко определенных операций. В качестве протокола передачи данных используется stateless-протокол, обычно HTTP.
При использовании протокола HTTP действия над данными выполняются с помощью HTTP-методов: GET (получить), PUT (добавить, заменить), POST (добавить, изменить, удалить), DELETE (удалить). Таким образом, действия CRUD (Create-Read-Update-Delete) могут выполняться как со всеми 4-мя методами, так и только с помощью GET и POST. Примеры запросов:
GET /book/ – получить список всех книг;
GET /book/3 – получить книгу номер 3;
PUT /book/ – добавить книгу (данные в теле запроса);
POST /book/3 – изменить книгу (данные в теле запроса);
DELETE /book/3 – удалить книгу.
Как правило, необязательно поддерживать все методы, но, как правило, веб-служба должна поддерживать:
GET – используется для получения существующих ресурсов;
POST – используется для создания/обновления нового ресурса;
PUT – используется для обновления/замены ресурса;
DELETE – используется для удаления ресурса.
кроме этого, служба может поддерживать такие методы как PATCH (обновление части ресурса), HEAD (возвращение заголовка ресурса, то есть метаданных) и так далее.
Библиотека Retrofit
Retrofit — это известная среди Android-разработчиков библиотека для сетевого взаимодействия, некоторые даже считают её в каком-то роде стандартом. Она является незаменимым инструментом для работы с API в клиент-серверных приложениях.
Откроем учебное приложения для создания и редактирования задач. Прежде всего необходимо добавить зависимости в build.gradle. Нам необходимо добавить библиотеку Retrofit, а также библиотеку Gson для преобразования данных в формате json в объектный вид и обратно.
Прежде чем перейти к настройке Retrofit, немного изменим сущность Task в нашем проекте. Изменим тип и название поля, которое хранит дату создания задачи (Date date --> long timestamp). Также измени сигнатуры конструктор и геттер\сеттер для поля даты.
За работу с источниками данных несет ответственность репозиторий, поэтому основные изменения коснутся класса AppRepository.
Перейдем в класс AppRepository и в методах работы с данными изменим тела методов, вместо вызова DAO поставим "заглушки".
AppRepository.java
publicclassAppRepository {privatestaticAppRepository ourInstance;publicLiveData<List<Task>> taskList;privateExecutor executor =Executors.newSingleThreadExecutor();publicstaticAppRepositorygetInstance(Context context) {if (ourInstance ==null) { ourInstance =newAppRepository(context); }return ourInstance; }privateAppRepository(Context context) { taskList =newMutableLiveData<>(); }privateLiveData<List<Task>> getAllTasks() {// TODO: получение списка всех задачreturnnull; }publicvoidaddSampleData() {//TODO: добавление списка тестовых задач на сервер }publicvoiddeleteAllTasks() {// TODO: удаление всех задач }publicvoiddeleteTask(Task task) {// TODO: удаление определенной задачи }publicTaskgetTaskById(long id) {// TODO: получение задачи по idreturnnull; }publicvoidinsertTask(Task t) {// TODO: добавление новой задачи }
Далее займемся настройкой Retrofit.
Для начала разберемся с REST API на сервере.
Method
URI
Описание конечной точки
GET
/task/all
Получить список всех задач
GET
/task?id=XXX
Получить задачу с указанным id
POST
/task
Добавить новую задачу
PUT
/task
Изменить существующую задачу
DELETE
/task?id=XXX
Удалить задачу с указанным id
DELETE
/task/all
Удалить все задачи
Создадим интерфейс TaskClient. Методы этого интерфейса соответствует определенному запросу на сервер. С помощью аннотаций и входных аргументов указываем различные параметры запроса.
Для начала реализуем запросы на получение списка всех задач и добавление новой задачи. Создадим пакет retrofit, в котором создадим интерфейс.
Давайте разберемся в написанном коде. С помощью аннотации GET и указания пути в параметре аннотации мы указываем метод и uri ресурса. Список задач List<Task> необходимо "обернуть" в объект обобщенного типа Call. Во втором методе указываем входной аргумент - ссылка на объект задачи, которую необходимо добавить на сервер. Так как в методе POST данные передаются в теле метода, то указываем аннотацию @Body.
Теперь вернемся в класс AppRepository. Нам необходимо настроить Retrofit для отправки данных. Добавим поле типа TaskClient, в конструкторе класса создаем объект Retrofit, после чего используем его для генерации класса, реализующего интерфейс TaskClient.
Давайте рассмотрим подробнее создание объекта Retrofit. Создание объекта происходит с помощью паттерна Builder. В билдере мы указываем URL сервера, указываем фабрику для конвертации данных из объектного вида в json и обратно.
Объект TaskClient получен, теперь реализуем метод получения списка всех задач.
public class AppRepository {
private TaskClient client;
public MutableLiveData<List<Task>> taskList;
public static AppRepository getInstance(Context context) {
if (ourInstance == null) {
ourInstance = new AppRepository(context);
}
return ourInstance;
}
private AppRepository(Context context) {
taskList = new MutableLiveData<>();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("172.168.88.135:8081")
.addConverterFactory(GsonConverterFactory.create())
.build();
client = retrofit.create(TaskClient.class);
}
private void getAllTasks() {
Call<List<Task>> listCall = client.getAllTasks();
listCall.enqueue(new Callback<List<Task>>() {
@Override
public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
if (response.body() != null) {
taskList.postValue(response.body());
}
}
@Override
public void onFailure(Call<List<Task>> call, Throwable t) {
Log.d("RETROFIT","Connection to server failed!");
}
});
}
public void addSampleData() {
//TODO: добавление списка тестовых задач на сервер
}
public void deleteAllTasks() {
// TODO: удаление всех задач
}
public void deleteTask(Task task) {
// TODO: удаление определенной задачи
}
public Task getTaskById(long id) {
// TODO: получение задачи по id
return null;
}
public void insertTask(Task t) {
// TODO: добавление новой задачи
}
}