Часть 5. Практические стратегии совместного использования кода

В этом разделе приведены примеры совместного использования кода для распространенных сценариев приложения.

Уровень данных

Уровень данных состоит из подсистемы хранилища и методов для чтения и записи информации. Для обеспечения производительности, гибкости и кроссплатформенной совместимости ядра СУБД SQLite рекомендуется использовать для кроссплатформенных приложений Xamarin. Он работает на различных платформах, включая Windows, Android, iOS и Mac.

SQLite

SQLite — это реализация базы данных с открытым кодом. Источник и документация можно найти по адресу SQLite.org. Поддержка SQLite доступна на каждой мобильной платформе:

Даже если ядро СУБД доступно на всех платформах, собственные методы для доступа к базе данных отличаются. Как iOS, так и Android предлагают встроенные API-интерфейсы для доступа к SQLite, которые можно использовать из Xamarin.iOS или Xamarin.Android, однако использование собственных методов SDK не обеспечивает возможности совместного использования кода (кроме, возможно, сами запросы SQL, предполагая, что они хранятся в виде строк). Дополнительные сведения о функциях собственных баз данных для поиска CoreData в классе iOS или AndroidSQLiteOpenHelper, так как эти параметры не являются кроссплатформенными, они выходят за рамки область этого документа.

ADO.NET

Поддержка System.Data Xamarin.iOS и Xamarin.Android и 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 (или объектно-реляционный mapper) пытается упростить хранение данных, моделироваемых в классах. Вместо того, чтобы вручную писать запросы SQL, которые СОЗДАЮТ ТАБУЛЫ или SELECT, INSERT и DELETE данные, извлеченные вручную из полей и свойств класса, ORM добавляет слой кода, который делает это для вас. С помощью отражения для изучения структуры классов ORM может автоматически создавать таблицы и столбцы, которые соответствуют классу и создают запросы для чтения и записи данных. Это позволяет коду приложения просто отправлять и извлекать экземпляры объектов в ORM, который заботится обо всех операциях SQL под капюшоном.

SQLite-NET выступает в качестве простого ORM, который позволяет сохранять и извлекать классы в SQLite. Она скрывает сложность кроссплатформенного доступа SQLite с сочетанием директив компилятора и других трюков.

Функции SQLite-NET:

  • Таблицы определяются путем добавления атрибутов в классы Model.
  • Экземпляр базы данных представлен подклассом основного 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 Телефон 8 (Silverlight) не разрешает прямой доступ к файлам— файлы могут управляться только с помощьюIsolatedStorage.
  • Проекты Windows 8.1 WinRT и Windows 10 UWP предлагают асинхронные операции с файлами через Windows.Storage API, которые отличаются от других платформ.

Пример для 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.File.ReadAllText (filePath));

Дополнительные сведения о функциональных возможностях файловой системы см. в документе Xamarin.iOS. При написании кроссплатформенного кода доступа к файлам помните, что некоторые файловые системы чувствительны к регистру и имеют разные разделители каталогов. Рекомендуется всегда использовать один и тот же регистр для имен файлов и Path.Combine() метода при создании путей к файлу или каталогу.

Windows. служба хранилища для Windows 8 и Windows 10

Книга "Создание мобильных приложений с помощью книги Xamarin.Forms" 20. Асинхронный и файловый ввод-вывод содержат примеры для 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");

Дополнительные сведения см. в разделе книги 20 .

Изолированные служба хранилища в Windows Телефон 7 и 8 (Silverlight)

Изолированные служба хранилища — это общий API для сохранения и загрузки файлов на всех платформах iOS, Android и более старых платформ Windows Телефон.

Это механизм доступа к файлам по умолчанию в Windows Телефон (Silverlight), реализованный в Xamarin.iOS и Xamarin.Android, чтобы разрешить запись общего кода доступа к файлам. Класс System.IO.IsolatedStorage можно ссылаться на все три платформы в общем проекте.

Дополнительные сведения см. в обзоре изолированного служба хранилища для Windows Телефон.

API-интерфейсы изолированных служба хранилища недоступны в переносимых библиотеках классов. Одним из вариантов для PCL является PCL служба хранилища NuGet

Межплатформенный доступ к файлам в PCLs

Существует также PCL-совместимый NuGet — PCL служба хранилища — это обеспечивает кроссплатформенный доступ к файлам для поддерживаемых платформ Xamarin и последних API Windows.

Операции сети

Большинство мобильных приложений будут иметь сетевой компонент, например:

  • Скачивание изображений, видео и звука (например, эскизы, фотографии, музыка).
  • Скачивание документов (например, HTML, PDF).
  • Отправка данных пользователя (например, фотографий или текста).
  • Доступ к веб-службам или сторонним API-интерфейсам (включая SOAP, XML или JSON).

Платформа .NET Framework предоставляет несколько различных классов для доступа к сетевым ресурсам: HttpClient, WebClientи HttpWebRequest.

HttpClient

Класс HttpClient в System.Net.Http пространстве имен доступен в Xamarin.iOS, Xamarin.Android и большинстве платформ Windows. Существует NuGet клиентской библиотеки MICROSOFT HTTP, которую можно использовать для переноса этого API в переносимые библиотеки классов (и Windows Телефон 8 Silverlight).

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://xamarin.com");
var response = await myClient.SendAsync(request);

WebClient

Класс WebClient предоставляет простой API для получения удаленных данных с удаленных серверов.

универсальная платформа Windows операции должны быть асинхронными, хотя 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 не разрешает скачиванию приложений размером более 20 Мб по сравнению с 3G). Приложения могут использовать эти сведения, чтобы предупредить пользователя о чрезмерном времени загрузки при получении больших файлов.
  • Даже если сеть доступна, рекомендуется проверить подключение с целевым сервером перед инициированием других запросов. Это позволит предотвратить многократное время ожидания сетевых операций приложения, а также позволит отображать пользователю более информативное сообщение об ошибке.

Веб-службы

Ознакомьтесь с нашей документацией по работе с веб-службами, которая охватывает доступ к конечным точкам REST, SOAP и WCF с помощью Xamarin.iOS. Вы можете вручную создавать запросы веб-службы и анализировать ответы, однако существуют библиотеки, которые позволяют сделать это гораздо проще, включая Azure, RestSharp и ServiceStack. Даже к основным операциям WCF можно обращаться в приложениях Xamarin.

Azure

Microsoft Azure — это облачная платформа, которая предоставляет широкий спектр служб для мобильных приложений, включая хранилище данных и синхронизацию, а также push-уведомления.

Посетите azure.microsoft.com , чтобы попробовать его бесплатно.

RestSharp

RestSharp — это библиотека .NET, которая может быть включена в мобильные приложения для предоставления клиента REST, который упрощает доступ к веб-службам. Это помогает предоставить простой API для запроса данных и анализа ответа REST. RestSharp может быть полезным

Веб-сайт RestSharp содержит документацию по реализации клиента REST с помощью RestSharp. RestSharp предоставляет примеры Xamarin.iOS и Xamarin.Android на github.

В нашей документации по веб-службам также есть фрагмент кода Xamarin.iOS.

ServiceStack

В отличие от RestSharp, ServiceStack — это серверное решение для размещения веб-службы, а также клиентской библиотеки, которая может быть реализована в мобильных приложениях для доступа к этим службам.

Веб-сайт ServiceStack объясняет назначение проекта и ссылки на примеры документов и кода. Примеры включают полную реализацию веб-службы на стороне сервера, а также различные клиентские приложения, которые могут получить к нему доступ.

WCF

Средства Xamarin помогут вам использовать некоторые службы Windows Communication Foundation (WCF). Как правило, Xamarin поддерживает тот же клиентский подмножество WCF, которое поставляется с средой выполнения Silverlight. К ним относятся наиболее распространенные реализации кодировки и протокола WCF: текстовые сообщения SOAP по протоколу транспорта HTTP с помощью BasicHttpBindingпротокола .

Из-за размера и сложности платформы WCF могут существовать текущие и будущие реализации служб, которые будут находиться за пределами область, поддерживаемых доменом подмножества клиента Xamarin. Кроме того, поддержка WCF требует использования средств, доступных только в среде Windows для создания прокси-сервера.

Потоки

Скорость реагирования приложений важна для мобильных приложений— пользователи ожидают, что приложения загружают и выполняются быстро. Экран "замороженный", который перестает принимать входные данные пользователя, будет отображаться, что приложение завершится сбоем, поэтому важно не связывать поток пользовательского интерфейса с длительными блокирующими вызовами, такими как сетевые запросы или медленные локальные операции (например, распаковка файла). В частности, процесс запуска не должен содержать длительные задачи. Все мобильные платформы убьют приложение, которое занимает слишком много времени для загрузки.

Это означает, что пользовательский интерфейс должен реализовать "индикатор хода выполнения" или иначе "удобный" пользовательский интерфейс, который быстро отображается и асинхронные задачи для выполнения фоновых операций. Выполнение фоновых задач требует использования потоков, что означает, что фоновые задачи должны быть способом обратного взаимодействия с основным потоком для указания хода выполнения или завершения.

Библиотека параллельных задач

Задачи, созданные с помощью библиотеки параллельных задач, могут выполняться асинхронно и возвращать вызововый поток, что делает их очень полезными для запуска длительных операций без блокировки пользовательского интерфейса.

Простая параллельная операция задачи может выглядеть следующим образом:

using System.Threading.Tasks;
void MainThreadMethod ()
{
    Task.Factory.StartNew (() => wc.DownloadString ("http://...")).ContinueWith (
        t => label.Text = t.Result, TaskScheduler.FromCurrentSynchronizationContext()
    );
}

Ключ заключается в TaskScheduler.FromCurrentSynchronizationContext() том, что будет повторно использовать синхронизациюContext.Current потока, вызывающего метод (здесь основной поток, который выполняется MainThreadMethod) в качестве способа маршалирования обратных вызовов этого потока. Это означает, что если метод вызывается в потоке пользовательского интерфейса, он будет выполнять ContinueWith операцию обратно в потоке пользовательского интерфейса.

Если код запускает задачи из других потоков, используйте следующий шаблон, чтобы создать ссылку на поток пользовательского интерфейса, и задача по-прежнему может вернуться к нему:

static Context uiContext = TaskScheduler.FromCurrentSynchronizationContext();

Вызов в потоке пользовательского интерфейса

Для кода, который не использует библиотеку параллельных задач, каждая платформа имеет собственный синтаксис для маршалинга операций обратно в поток пользовательского интерфейса:

  • iOSowner.BeginInvokeOnMainThread(new NSAction(action))
  • Androidowner.RunOnUiThread(action)
  • Xamarin.FormsDevice.BeginInvokeOnMainThread(action)
  • Windows — Deployment.Current.Dispatcher.BeginInvoke(action)

Для синтаксического синтаксиса iOS и Android требуется доступ к классу context, что означает, что код должен передать этот объект в любые методы, требующие обратного вызова в потоке пользовательского интерфейса.

Чтобы выполнить вызовы потока пользовательского интерфейса в общем коде, следуйте примеру 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 общий код (общие проекты или PCL).

Возможности платформы и устройства и снижение производительности

Дополнительные примеры работы с разными возможностями приведены в документации по возможностям платформы. Он занимается обнаружением различных возможностей и способом корректного снижения производительности приложения для обеспечения хорошего взаимодействия с пользователем, даже если приложение не может работать с его полным потенциалом.