Разработчикам на Xamarin доступен богатый выбор компонентов для работы с сетью, и в сегодняшней нашей статье мы рассмотрим набор модулей, которые также могут быть использованы в PCL-проектах на Xamarin.Forms.
Все статьи из колонки можно найти и прочитать по ссылке #xamarincolumn, или в конце материала под катом.
Самым популярным в настоящее время протоколом для общения мобильных приложений с сервером является REST в связке с Json. Поэтому наше сегодняшнее знакомство начнем с библиотеки Refit.
Refit позволяет описать спецификации для работы с REST-сервисом в виде простого Interface с понятным набором входных и выходных параметров, включая возможность манипулировать HTTP-заголовками для отдельных запросов. Для примера возьмем демо-API сервиса httpbin.org:
После описания данного интерфейса, он подается на вход для Refit:
Сами данные можно при необходимости (конвертации camel case или snake eyes, преобразование из множества в строковые значения) можно расширить аттрибутами из библиотеки Json.net, так как именно она используется в Refit:
В Refit в качестве выходного значения можно получить уже преобразованные объекты DTO или HttpResponseMessage. Последний позволяет получить информацию о запросе и ответе, что может быть полезно при отладке. При желании также может использоваться ModernHttpClient при создании HttpClient. В целом, Refit — достаточно удобный и универсальный инструмент для Xamarin-разработчиков в том числе.
Примечание 1: для установки Refit 3.0 в PCL-проект Xamarin.Forms потребуется перевести проект на .NET Standard.
Примечание 2: в документации Refit нет упоминания на использование CancelationToken для отмены активных операций, но данный механизм работает и описан в тикете.
При разработке мобильных приложений часто приходится учитывать фактор нестабильности сигнала в сотовых сетях, поэтому при выполнении сетевых запросов часто возникает необходимости делать повторные попытки. Это позволяет не отвлекать лишний раз пользователя просьбой повторить запрос.
Интересный подход по использованию Refit и Polly описал Rob Gibbens в своем блоге (дополнительно там показан пример приоретизацией сетевых запросов с помощью Fusillade).
Вот так мы совершаем запрос:
Вместо loadingFunction необходимо передать ваш код обращения к Refit:
Итак, мы интегрировали Refit, Polly и ModernHttpClient.
И в завершении статьи можно рассмотреть использование кэша при работе с сетью. Xamarin-разработчику доступны все возможности целевых платформ, поэтому для реализации кэша можно использовать различные СУБД. Одним из самых популярных кэшеров выступает Akavache, работающий поверх SQLite.
Также можно использовать для реализации кэша очень удобную мобильную СУБД Realm. Ниже представлен пример кэшера на базе Realm:
Итак, сегодня мы рассмотрели использование Refit, Json.net, ModernHttpClient, Polly и Realm при интеграции с REST API. В следующей статье мы рассмотрим вопросы интеграции Xamarin с внешними сервисами и Azure.
Вячеслав Черников — руководитель отдела разработки компании Binwell. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone.
1. Быстрое создание MVP (minimum viable product) на базе Microsoft Azure и Xamarin.Forms.
2. Готовим Xamarin.Forms: настройка окружения и первые шаги.
3. Повышаем эффективность работы в Xamarin.Forms.
4. Работаем с состояниями экранов в Xamarin.Forms.
Все статьи из колонки можно найти и прочитать по ссылке #xamarincolumn, или в конце материала под катом.
Refit для удобного описания клиента REST API
Самым популярным в настоящее время протоколом для общения мобильных приложений с сервером является REST в связке с Json. Поэтому наше сегодняшнее знакомство начнем с библиотеки Refit.
Refit позволяет описать спецификации для работы с REST-сервисом в виде простого Interface с понятным набором входных и выходных параметров, включая возможность манипулировать HTTP-заголовками для отдельных запросов. Для примера возьмем демо-API сервиса httpbin.org:
[Headers("Accept: application/json")]
public interface IHttpbinApi
{
[Get("/basic-auth/{username}/{password}")]
Task<AuthResult> BasicAuth(string username, string password, [Header("Authorization")] string authToken, CancellationToken ctx);
[Get("/cache")]
Task<HttpResponseMessage> CheckIfModified([Header("If-Modified-Since")] string lastUpdateAtString, CancellationToken ctx);
[Post("/post")]
Task<HttpResponseMessage> FormPost([Body(BodySerializationMethod.UrlEncoded)] FormData data, CancellationToken ctx);
}
После описания данного интерфейса, он подается на вход для Refit:
var client = new HttpClient(new NativeMessageHandler())
{
BaseAddress = new Uri("http://httpbin.org")
};
_httpbinApiService = RestService.For<IHttpbinApi>(client);
Сами данные можно при необходимости (конвертации camel case или snake eyes, преобразование из множества в строковые значения) можно расширить аттрибутами из библиотеки Json.net, так как именно она используется в Refit:
public class AuthResult
{
[JsonProperty("authenticated")]
public bool IsAuthenticated { get; set; }
[JsonProperty("user")]
public string Login { get; set; }
}
В Refit в качестве выходного значения можно получить уже преобразованные объекты DTO или HttpResponseMessage. Последний позволяет получить информацию о запросе и ответе, что может быть полезно при отладке. При желании также может использоваться ModernHttpClient при создании HttpClient. В целом, Refit — достаточно удобный и универсальный инструмент для Xamarin-разработчиков в том числе.
Примечание 1: для установки Refit 3.0 в PCL-проект Xamarin.Forms потребуется перевести проект на .NET Standard.
Примечание 2: в документации Refit нет упоминания на использование CancelationToken для отмены активных операций, но данный механизм работает и описан в тикете.
Polly
При разработке мобильных приложений часто приходится учитывать фактор нестабильности сигнала в сотовых сетях, поэтому при выполнении сетевых запросов часто возникает необходимости делать повторные попытки. Это позволяет не отвлекать лишний раз пользователя просьбой повторить запрос.
Интересный подход по использованию Refit и Polly описал Rob Gibbens в своем блоге (дополнительно там показан пример приоретизацией сетевых запросов с помощью Fusillade).
Вот так мы совершаем запрос:
protected async Task<RequestResult> MakeRequest<T>(Func<CancellationToken, Task<T>> loadingFunction, CancellationToken cancellationToken)
{
Exception exception = null;
var result = default(T);
try
{
result = await Policy.Handle<WebException>().Or<HttpRequestException>()
.WaitAndRetryAsync(3, i => TimeSpan.FromMilliseconds(300), (ex, span) => exception = ex)
.ExecuteAsync(loadingFunction, cancellationToken);
}
catch (Exception e)
{
// Сюда приходят ошибки вроде отсутствия интернет-соединения или неправильной работы DNS
exception = e;
}
//TODO: Обработать исключения или передать их дальше
return result;
}
Вместо loadingFunction необходимо передать ваш код обращения к Refit:
var authToken = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
return await MakeRequest(ct => _httpbinApiService.BasicAuth(username, password, authToken, ct), cancellationToken);
Итак, мы интегрировали Refit, Polly и ModernHttpClient.
Кэш
И в завершении статьи можно рассмотреть использование кэша при работе с сетью. Xamarin-разработчику доступны все возможности целевых платформ, поэтому для реализации кэша можно использовать различные СУБД. Одним из самых популярных кэшеров выступает Akavache, работающий поверх SQLite.
var cache = BlobCache.LocalMachine;
var cachedObjects = cache.GetAndFetchLatest("objects", GetRemoteObjectAsync,
offset =>
{
TimeSpan elapsed = DateTimeOffset.Now - offset;
return elapsed > new TimeSpan(hours: 0, minutes: 30, seconds: 0);
});
Также можно использовать для реализации кэша очень удобную мобильную СУБД Realm. Ниже представлен пример кэшера на базе Realm:
public static class LocalCache
{
private class CachedObject : RealmObject
{
[PrimaryKey]
public string Key { get; set; }
public string Value { get; set; }
public DateTimeOffset UpdatedAt { get; set; }
}
private static readonly RealmConfiguration Configuration = new RealmConfiguration("cache.realm", true);
private static Realm Db => Realm.GetInstance(Configuration);
public static async Task WriteToCache<T>(string key, T data, DateTimeOffset timeStamp)
{
if (String.IsNullOrEmpty(key) || data == null || timeStamp == DateTimeOffset.MinValue) return;
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
if (currentValue == null)
await Db.WriteAsync(db =>
{
var newValue = db.CreateObject<CachedObject>();
newValue.Key = key;
newValue.UpdatedAt = timeStamp;
newValue.Value = JsonConvert.SerializeObject(data);
});
else
using (var transaction = Db.BeginWrite())
{
currentValue.Value = JsonConvert.SerializeObject(data);
currentValue.UpdatedAt = timeStamp;
transaction.Commit();
}
}
public static DateTimeOffset CacheLastUpdated(string key)
{
if (String.IsNullOrEmpty(key)) return DateTimeOffset.MinValue;
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
return currentValue?.UpdatedAt ?? DateTimeOffset.MinValue;
}
public static void RemoveCache(string key)
{
if (String.IsNullOrEmpty(key)) return;
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
if (currentValue == null) return;
using (var transaction = Db.BeginWrite())
{
Db.Remove(currentValue);
transaction.Commit();
}
}
public static T GetFromCache<T>(string key)
{
if (String.IsNullOrEmpty(key)) return default(T);
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
return currentValue?.Value == null ? default(T) : JsonConvert.DeserializeObject<T>(currentValue.Value);
}
public static void ClearCache()
{
Realm.DeleteRealm(Configuration);
}
}
Заключение
Итак, сегодня мы рассмотрели использование Refit, Json.net, ModernHttpClient, Polly и Realm при интеграции с REST API. В следующей статье мы рассмотрим вопросы интеграции Xamarin с внешними сервисами и Azure.
Об авторах
Вячеслав Черников — руководитель отдела разработки компании Binwell. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone.
Предыдущие части
1. Быстрое создание MVP (minimum viable product) на базе Microsoft Azure и Xamarin.Forms.
2. Готовим Xamarin.Forms: настройка окружения и первые шаги.
3. Повышаем эффективность работы в Xamarin.Forms.
4. Работаем с состояниями экранов в Xamarin.Forms.
Полезные ссылки
- Анонс бесплатных инструментов Xamarin для разработки кроссплатформенных приложений на С# по ссылке
- Visual Studio 2015 Community, Visual Studio Team Services, Visual Studio Code: бесплатные предложения для разработчиков
- Дополнительные бесплатные инструменты и службы в программе Visual Studio Dev Essentials
Поделиться с друзьями
Комментарии (7)
artem_drozdoff
22.09.2016 16:51Мне кажется в статье не хватает RestSharp.
devious
23.09.2016 12:17+1У RestSharp традиционные проблемы с PCL-проектами, поэтому для Xamarin.Forms он не очень хорошо подходит. Да и Refit на наш взгляд более удобное и простое решение. Но это все IMHO, а так RestSharp — да, хороший инструмент.
Vilyx
Не совсем по теме, но хотелось бы задать вопрос специалистам Xamarin. Недавно хотел попробовать эту кроссплатформенную среду разработки, но столкнулся с тем, что по факту кроссплатформенностью и не пахнет, для каждой целевой ОС надо делать всё отдельно в том числе интерфейс, действительно кроссплатформенной остаётся только бизнеслогика, которая не всегда является самой сложной частью приложения.
Подскажите, может я не в ту сторону смотрел?
BOOMik
Посмотрите в сторону Xamarin.Forms — благодаря ему можно интерфейс описывать сразу для всех поддерживаемых платформ. Конечно, если приложение достаточно сложное, то многие вещи придется описывать отдельно для каждой платформы, но количество кроссплатформенного кода в первой статье цикла было про это https://habrahabr.ru/company/microsoft/blog/281897/
Если не использовать Xamarin.Forms, то кроссплатформенной идет бизнес-логика приложения. Благодаря библиотекам типо MVVM-cross можно уменьшить количество платформенного кода описании UI и прочих вещей.
Vilyx
Спасибо, с интерфейсом прояснили, а что с работой с камерой, GPS, с файловой системой, аудио?
BOOMik
Как написал S_A — для многих вещей есть уже готовые плагины, которые позволяют обращаться к ним из кроссплатформенного кода. Хотя некоторые вещи могут быть не покрыты плагинами (сразу и не вспомню таких) или реализованы не совсем так, как требуется, тогда можно реализовать самому. Доступ ко всем системным API имеется.
S_A
есть плагины для xamarin.forms. с последнего пререлиза xaml в xamarin.forms также поддерживает и нативные компоненты. так что чем дальше в лес, тем меньше нативного кода. молодцы вообще они.