В этом разделе приводятся примеры способов совместного использования кода для распространенных сценариев приложений.
Уровень данных
Уровень данных состоит из подсистемы хранилища и методов для чтения и записи информации. Для производительности, гибкости и кроссплатформенной совместимости СУБД SQLite рекомендуется для кроссплатформенных приложений Xamarin. Он работает на разных платформах, включая Windows, Android, iOS и Mac.
SQLite
SQLite реализовано в виде ПО с открытым исходным кодом. Источник и документации можно найти на SQLite.org. SQLite поддержка доступна на каждой мобильной платформе:
- iOS – Встроено в операционную систему.
- Android – Встроено в операционную систему начиная с Android 2.2 (API Level 10).
- Windows – Смотрите SQLite расширение для Universal Windows Platform .
Даже у СУБД доступной на всех платформах, нативные методы доступа к базе данных различны. iOS и Android предлагают встроенные API для доступа к SQLite, которые могут быть использованы Xamarin.iOS или Xamarin.Android. Однако использование встроенных методов SDK не позволяет совместно использовать код (кроме, возможно, SQL-запросов, предполагая, что они хранятся в виде строк). Для получения дополнительной информации о нативном функционале использования SQLite следует искать в CoreData для iOS или в классе SQLiteOpenHelper для Android; Поскольку эти функции не являются кроссплатформенными, они выходят за рамки настоящего документа.
ADO.NET
Xamarin.iOS и Xamarin.Android поддерживают System.Data и Mono.Data.Sqlite (см. Xamarin.iOS документацию для получения дополнительной информации). Использование этих пространств имен позволяет писать код ADO.NET, который работает на обеих платформах. Для использования нужно добавить System.Data.dll и Mono.Data.Sqlite.dll в ссылки проекта и в код с помощью оператора using:
using System.Data; using Mono.Data.Sqlite;
Тогда следующий пример кода будет работать:
string dbPath = Path.Combine ( Environment.GetFolderPath (Environment.SpecialFolder.Personal), "items.db3"); bool exists = File.Exists (dbPath); if (!exists) SqliteConnection.CreateFile (dbPath); var connection = new SqliteConnection ("Data Source=" + dbPath); connection.Open (); if (!exists) { // This is the first time the app has run and/or that we need the DB. // Copy a "template" DB from your assets, or programmatically create one like this: var commands = new[]{ "CREATE TABLE [Items] (Key ntext, Value ntext);", "INSERT INTO [Items] ([Key], [Value]) VALUES ('sample', 'text')" }; foreach (var command in commands) { using (var c = connection.CreateCommand ()) { c.CommandText = command; c.ExecuteNonQuery (); } } } // use `connection`... here, we'll just append the contents to a TextView using (var contents = connection.CreateCommand ()) { contents.CommandText = "SELECT [Key], [Value] from [Items]"; var r = contents.ExecuteReader (); while (r.Read ()) Console.Write("\n\tKey={0}; Value={1}", r ["Key"].ToString (), r ["Value"].ToString ()); } connection.Close ();
Реальные реализации ADO.NET, очевидно, будут разделены между различными методами и классами (этот пример для демонстрационных целей).
SQLite-NET – Кроссплатформенный ORM
ORM (или Object-Relational Mapper) позволяет упростить хранение данных, смоделированных в классах. Вместо того, что бы писать вручную SQL-запросы, создания таблиц или выборки, вставки и удаления данных, которые вручную извлечены из полей и свойств класса, ORM добавляет слой кода, который делает это за вас. Использую рефлексию при исследовании структуры классов, ORM может автоматически создавать таблицы и столбцы, которые соответствуют классу и создавать запросы для чтения и записи данных. Это позволяет коду приложения просто отправлять и получать экземпляры объектов к ORM, который заботится о всех SQL-операциях.
SQLite-NET действует как простой ORM, который позволит вам сохранить и восстановить свои классы в SQLite. Он скрывает сложность кроссплатформенного доступа к SQLite при совмещении директив компилятора и других хитростей.
Особенности SQLite-NET:
- Таблицы определяются путем добавления атрибутов моделей классов.
- Экземпляр базы данных представлен подклассом SQLiteConnection, основного класса в SQLite-Net библиотеке.
- Данные могут быть вставлены, запрошены и удалены, используя объекты. Никаких SQL-запросов не требуется (хотя вы можете написать SQL-запросы в случае необходимости).
- Основные Linq-запросы могут быть выполнены на коллекциях возвращенных SQLite-NET.
Исходный код и документация SQLite-NET доступна на SQLite-Net на GitHub и был реализован в обоих тематических исследованиях. Простой пример кода SQLite-NET (на примере Tasky Pro) приведены ниже.
Во-первых класс TodoItem использует атрибуты для определения полей для первичного ключа базы данных:
public class TodoItem : IBusinessEntity { public TodoItem () {} [PrimaryKey, AutoIncrement] public int ID { get; set; } public string Name { get; set; } public string Notes { get; set; } public bool Done { get; set; } }
Это позволяет создать таблицу TodoItem с помощью следующей строки кода (и без каких-либо SQL-запросов) на экземпляре SQLiteConnection:
CreateTable<TodoItem> ();
Данными в таблице также можно манипулировать использую другие методы на SQLiteConnection (опять же, не требуя SQL-запросов):
Insert (TodoItem); // 'task' is an instance with data populated in its properties Update (TodoItem); // Primary Key field must be populated for Update to work Table<TodoItem>.ToList(); // returns all rows in a collection
Доступ к файлам
Доступ к файлам, несомненно, будет ключевой частью любого приложения. Типичные примеры файлов, которые могут быть частью приложения:
- Файлы базы данных SQLite.
- Данные созданные пользователем (техт, изображения, аудио- и видео-файлы).
- Загруженные данные для кэширования (изображения, html или PDF файлы).
System.IO Прямой доступ
Xamarin.iOS и Xamarin.Android разрешают доступ к файловой системе с помощью классов в пространстве имен System.IO.
Каждая платформа имеет ограничения доступа, которые должны быть приняты во внимание:
- iOS приложения выполняются в изолированной программной среде с очень ограниченным доступом к файловой системе. Apple также диктует, как вы должны использовать файловую систему, указав определенные места, которые являются резервными (и другие, которые таковыми не являются). Обратитесь к руководству Работа с файловой системой в Xamarin.iOS для получения более подробной информации.
- Android также ограничивает доступ к некоторым каталогам, относящиеся к приложению, но он поддерживает внешние носители (например, SD карты) и доступ к общим данным.
- Windows Phone 8 (Silverlight) не допускает прямого доступа к файлам – файлами можно манипулировать только с помощью IsolatedStorage.
- Проекты Windows 8.1 WinRT и Windows 10 UWP предлагают только асинхронные файловые операции через Windows.Storage, которые отличаются от других платформ.
Примеры для iOS и Android
Ниже показан тривиальный пример, который пишет и читает текстовый файл. Использование Environment.GetFolderPath позволяет один и тот же код использовать в iOS и Android, которые возвращают действительный каталог на основе соглашений их файловой системы.
string filePath = Path.Combine ( Environment.GetFolderPath (Environment.SpecialFolder.Personal), "MyFile.txt"); System.IO.File.WriteAllText (filePath, "Contents of text file"); Console.WriteLine (System.IO.ReadAllText (filePath));
Обратитесь к документации Xamarin.iOS Работа с файловой системой для получения дополнительной информации о функциях iOS-специфичной файловой системы. При написании кроссплатформенного кода доступа к файлам, помните, что некоторые файловые системы учетываюь регистр символов и имеют разные разделители каталогов. Рекомендуется всегда использовать один регистр для имен файлов и метод Path.Combine() при построении пути к файлу или каталогу.
Windows.Storage в Windows 8 и Windows 10
В книге по создании мобильных приложения с Xamarin.Forms Глава 20. Асинхронный и файловый ввод/вывод (Async and File I/O) включает в себя примеры для ОС Windows 8.1 и Windows 10.
Используя DependencyService можно читать и писать файлы на этих платформах, используя поддерживаемые API-интерфейсы:
StorageFolder localFolder = ApplicationData.Current.LocalFolder; IStorageFile storageFile = await localFolder.CreateFileAsync("MyFile.txt", CreationCollisionOption.ReplaceExisting); await FileIO.WriteTextAsync(storageFile, "Contents of text file");
Изолированное хранилище в Windows Phone 7 и 8 (Silverlight)
Изолированное хранилище (Isolated Storage) — общий API для сохранения и загрузки файлов во всех iOS, Android и старых платформах Windows Phone.
Это механизм по умолчанию для доступа к файлам в Windows Phone (Silverlight), который был реализован в Xamarin.iOS и Xamarin.Android, для обеспечения общего кода доступа к файлам для записи. На класс System.IO.IsolatedStorage можно ссылаться во всех трех платформах, в Shared Project.
См. Обзор Изолированного хранилища (Isolated Storage) для Windows Phone для получения дополнительной информации.
API интерфейсы Isolated Storage недоступны в Portable Class Libraries. Единственной альтернативой для PCL является PCLStorage NuGet
Кроссплатформенный доступ к файлам в PCL
Существует также PCL-совместимый кроссплатформенный Nuget компонент доступа к файлам — PCLStorage для платформ, поддерживаемых Xamarin и новейшими Windows API.
Примеры кода доступны на странице соответствующих компонентов.
Сетевые операции
Большинству мобильных приложений необходимо иметь сетевой компонент, например:
- Загрузка изображений, видео и аудио (например, эскизы, фотографии, музыка).
- Загрузка документов (например, HTML, PDF).
- Загрузка данных пользователя (например, фотографии или текст).
- Доступ к веб-службам или API-интерфейсам сторонних разработчиков (включая SOAP, XML или JSON).
Платформа .NET Framework предоставляет несколько различных классов для доступа к сетевым ресурсам: HttpClient, WebClient, и HttpWebRequest.
HttpClient
Класс HttpClient в пространстве имен System.Net.Http доступен в Xamarin.iOS, Xamarin.Android и большинстве платформ Windows. Существует Microsoft HTTP Client Library Nuget, который может быть использован, чтобы принести этот API в PCL и Windows Phone 8 Silverlight.
var client = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Get, "https://xamarin.com"); var response = await myClient.SendAsync(request);
WebClient
Класс WebClient предоставляет простой API для получения данных с удаленных серверов.
Операции Universal Windows Platofrm (UWP) должны быть обязательно асинхронными, а Xamarin.iOS и Xamarin.Android поддерживают только синхронные операции (которые могут быть сделаны в фоновых потоках).
Код для простой асинхронной операции WebClient:
var webClient = new WebClient (); webClient.DownloadStringCompleted += (sender, e) => { var resultString = e.Result; // do something with downloaded string, do UI interaction on main thread }; webClient.Encoding = System.Text.Encoding.UTF8; webClient.DownloadStringAsync (new Uri ("http://some-server.com/file.xml"));
WebClient также имеет DownloadFileCompleted и DownloadFileAsync для получения двоичных данных.
HttpWebRequest
HttpWebRequest предлагает больше настроек, чем WebClient и в результате требует большего количества кода для использования.
Код для простой синхронной операции HttpWebRequest:
var request = HttpWebRequest.Create(@"http://some-server.com/file.xml "); request.ContentType = "text/xml"; request.Method = "GET"; using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { if (response.StatusCode != HttpStatusCode.OK) Console.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode); using (StreamReader reader = new StreamReader(response.GetResponseStream())) { var content = reader.ReadToEnd(); // do something with downloaded string, do UI interaction on main thread } }
Доступность
Мобильные устройства работают в различных условиях сети от быстрого Wi-Fi или 4G подключения до районов с плохим приемом и медленных подключений EDGE. Поэтому хорошая практика, для начала определить, доступна ли сеть и если да, какой тип сети доступен, прежде чем подключаться к удаленным серверам.
Действия, которые мобильные приложения должны выполнять в таких ситуациях включают в себя:
- Если сеть недоступна, сообщите об этом пользователю. Если он вручную отключил её (например, включен Режим полета или выключен Wi-Fi), то пользователи смогут решить эту проблему.
- Если имеется соединение 3G, то приложения могут вести себя по-разному (например, Apple не поддерживает в приложениях загрузки через 3G больше 20Mb данных). Приложения могут использовать эту информацию, чтобы предупредить пользователя о чрезмерном времени загрузки при получении больших файлов.
- Даже если сеть доступна, то хорошей практикой будет проверка соединения с целевым сервером перед запуском других запросов. Это предотвратит неоднократные сетевые операции приложения с истечением времени ожидания и также позволят отображать для пользователя более информативное сообщение об ошибке.
Здесь пример кода Xamarin.iOS (который основан на примере доступности от Apple), чтобы помочь обнаружить доступность сети.
Веб-сервисы
Смотрите нашу документацию по Работе с веб-службами, которая охватывает доступ к REST, SOAP и WCF конечных точек с помощью Xamarin.iOS. Можно вручную создавать кустарные запросы к веб-сервисам и анализировать ответы, однако есть доступные библиотеки, чтобы сделать это намного проще, в том числе Azure, RestSharp и ServiceStack. Даже базовые операции WCF доступны в Xamarin приложениях.
Azure
Microsoft Azure — облачная платформа, которая предоставляет широкий спектр услуг для мобильных приложений, включая хранение данных, синхронизации и push-уведомлений.
Посетите azure.microsoft.com, чтобы попробовать его бесплатно и включите компонент Azure, чтобы начать работу.
RestSharp
RestSharp — библиотека .NET, которые могут быть включены в мобильные приложения клиента REST, который упрощает доступ к веб-службам. Она помогает, предоставляя простой API для запроса данных и проанализировать ответ REST. RestSharp может быть полезным.
Сайт RestSharp содержит документацию о том, как реализовать клиента REST с помощью RestSharp. RestSharp предоставляет примеры для Xamarin.iOS и Xamarin.Android на GitHub.
Существует также фрагмент кода Xamarin.iOS в нашей документации веб-служб.
ServiceStack
В отличие от RestSharp ServiceStack является серверным решением для организации веб-службы, а также клиентской библиотекой, которая может быть реализована в мобильных приложениях для доступа к этому сервису.
Веб-сайт ServiceStack объясняет цель проекта, имеет ссылки на документацию и примеры кода. Примеры включают полную реализацию на стороне сервера веб-службы, а также в различных клиентских приложениях, которые могут получить доступ к ней.
Существует пример Xamarin.iOS на сайте ServiceStack, и фрагмент кода в нашей документации веб-служб.
WCF
Xamarin инструменты могут помочь вам использовать некоторые службы Windows Communication Foundation (WCF). В общем Xamarin поддерживает то же подмножество на стороне клиента WCF, который поставляется вместе со средой выполнения Silverlight. Это включает в себя наиболее распространенные реализации кодирования и протокола WCF: текст в кодировке сообщения SOAP через транспортный протокол HTTP с использованием BasicHttpBinding.
Из-за размера и сложности платформы WCF может быть реализации текущих и будущих сервисов, которые выходят за пределы сферы действия поддерживаемого домене подмножества клиента в Xamarin. Кроме того, поддержка WCF требует использования инструментов, доступных только в среде Windows, чтобы сгенерировать прокси-сервер.
Многопоточность
Скорость реакции приложений имеет важное значение для мобильных приложений – пользователи ожидают, что приложения будут быстро и загружаться, и выполняться. А если появляется «замороженный» экран, который прекращает принимать входные данные пользователя, то это указывает на то, что в приложении произошёл сбой, поэтому очень важно не объединять поток пользовательского интерфейса с длительной блокировки вызовов, например сетевыми запросами или медленными локальными операциями (например, разархивирования файла). Также процесс запуска не должен содержать долго выполняющихся задач – все мобильные платформы будут убивать приложение, которое занимает слишком много времени для загрузки.
Это означает, что ваш пользовательский интерфейс должен реализовать ‘индикатор прогресса’ или иной ‘годный к употреблению’ пользовательский интерфейс, который быстр для отображения и предполагает использование асинхронных задач для выполнения фоновых операций. Выполнение фоновых задач требует использования потоков, то есть фоновым задачам нужен способ передачи информации обратно в основной поток, для того, чтобы указать прогресс выполнения или сообщить об окончании выполнения.
Библиотека параллельных задач (Parallel Task Library)
Задачи, созданные с Parallel Task Library могут выполняться асинхронно и передавать упраление вызывающему их потоку, что делает их очень полезными для запуска долго выполняющихся операций без блокирования пользовательского интерфейса.
Простая задача параллельной операции может выглядеть следующим образом:
using System.Threading.Tasks; void MainThreadMethod () { Task.Factory.StartNew (() => wc.DownloadString ("http://...")).ContinueWith ( t => label.Text = t.Result, TaskScheduler.FromCurrentSynchronizationContext() ); }
Ключ TaskScheduler.FromCurrentSynchronizationContext который будет повторно использовать SynchronizationContext.Current трэды, вызывающий метод (это основной поток, тот который работает — MainThreadMethod) как способ управления обратными вызовами на этот поток. То есть, если метод вызывается в потоке пользовательского интерфейса, он будет обрабатывать операцию ContinueWith с возвращением управления обратно в поток пользовательского интерфейса.
Если в коде задачи запускаются из других потоков, используйте следующий шаблон для создания ссылки на поток пользовательского интерфейса и задача по-прежнему может обращаться к нему:
static Context uiContext = TaskScheduler.FromCurrentSynchronizationContext();
Вызов потока пользовательского интерфейса
Для кода, который не использует Parallel Task Library, каждая платформа имеет свой собственный синтаксис для возврата управления в поток пользовательского интерфейса:
- iOS – owner.BeginInvokeOnMainThread(new NSAction(action))
- Android – owner.RunOnUiThread(action)
- Xamarin.Forms – Device.BeginInvokeOnMainThread(action)
- Windows – Deployment.Current.Dispatcher.BeginInvoke(action)
И iOS, и Android синтаксис требуют доступный класс «контекста», чтобы код мог передать этот объект в любые методы, которые требуют обратного вызова в поток пользовательского интерфейса.
Для того, чтобы реализовать вызовы потока пользовательского интерфейса в общем коде, следуйте примеру IDispatchOnUIThread (любезно предоставлено @follesoe). Создайте интерфейс IDispatchOnUIThread в совместно используемом коде, а затем реализуйте классы специфичные для платформы, как показано здесь:
// program to the interface in shared code public interface IDispatchOnUIThread { void Invoke (Action action); } // iOS public class DispatchAdapter : IDispatchOnUIThread { public readonly NSObject owner; public DispatchAdapter (NSObject owner) { this.owner = owner; } public void Invoke (Action action) { owner.BeginInvokeOnMainThread(new NSAction(action)); } } // Android public class DispatchAdapter : IDispatchOnUIThread { public readonly Activity owner; public DispatchAdapter (Activity owner) { this.owner = owner; } public void Invoke (Action action) { owner.RunOnUiThread(action); } } // WP7 public class DispatchAdapter : IDispatchOnUIThread { public void Invoke (Action action) { Deployment.Current.Dispatcher.BeginInvoke(action); } }
Xamarin.Forms разработчики должны использовать Device.BeginInvokeOnMainThread в общем коде (Shared Projects или PCL).
Оригинал статьи
<= Часть 4 — Работа с несколькими платформами | Часть 6 — Тестирование и одобрение в App Store => |