Pull to refresh

Работа с данными в WinRT. Часть 1. Хранение настроек и файлов

Reading time8 min
Views13K
В WinRT изменилась работа работа с данными и файловой системой и она немного отличается как от десктопного .NET так и Silverlight.

Для доступа к пользовательским файлам в системе нужны соответствующие разрешения и надо использовать контракты и расширения для работы с файлами (которые будут рассматриваться в отдельной статье). Однако каждое приложение получает доступ к изолированному хранилищу для хранения служебных данных и настроек. Все что необходимо для хранения данных находится в пространстве имен Windows.Storage в объекте ApplicationData.

В этой статье мы рассмотрим работу со следующими объектами:

Хранение простых данных в LocalSettings и RoamingSettings. (Хранение простых данных)

Хранение бинарных данных в LocalFolder, TemporaryFolder, RoamingFolder. (Хранение данных в файловой системе)

Особенности работы с RoamingSettings и RoamingFolder (Хранение данных в облаке. Синхронизация данных между устройствами).

Версионность данных в роуминге.

Прямой доступ к данным через Uri.


В следующей части будет рассматриваться работа с БД.


Официальный пример для работы с локальными данными можно скачать по адресу: code.msdn.microsoft.com/windowsapps/ApplicationData-sample-fb043eb2

В этой статье будут рассмотрены пример работы локальными данными FileStorage.zip

1. Хранение простых данных в LocalSettings и RoamingSettings


Работа с LocalSettings и RoamingSettings идентична. Отличительные особенности RoamingSettings будут рассмотрены ниже.

В первую очередь эти объекты предназначены для хранения простых данных с простыми типами. В нем можно хранить данные типа string, bool, int, float и т.д. Эти объекты удобно использовать для хранения настроек вроде уровня громкости звука и т.п.

API реализовано очень просто. Следующий код сохраняет текст с ключем “customKey”:

ApplicationData.Current.LocalSettings.Values["customKey"] = "mytext";

Читать данные также просто как и сохранять (так как значения типа object, необходимо проводить к исходному типу):
var customText = (string) ApplicationData.Current.LocalSettings.Values["customKey"];

Желательно также проверить наличие ключа перед его извлечением, что бы не получить ошибку NullReferenceException для значимых типов данных (bool, int, float и т.д.).

var settings = ApplicationData.Current.LocalSettings;
if (settings.Values.ContainsKey(«pageNumber»))
{
var number= (int)settings.Values[«pageNumber»];
}

Зачастую нам необходимо хранить более сложные типы данных и/или бинарные данные. В этом случае мы можем воспользоваться xFolder объектами.

2. Хранение бинарных данных в LocalFolder, TemporaryFolder, RoamingFolder.


xFolder объекты предназначены для хранения данных в файловой системе. Работа с ними практически идентичная с небольшими особенностями.

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

TemproryFolder хорошо подходит для временных данных. К примеру, для приложения — клиента социальных сетей эта папка хорошо подходит для кеширования фотографий.

RoamingFolder как и RoamingSettings можно использовать для данных которые надо синхронизировать между устройствами и/или хранить в облаке. Детально работа с ним будет рассмотрено позже, но в них можно хранить данные небольшого объема (до 100 кб).

Для работы с файлами можно также использовать методы объекта FileIO упрощающую работу с данными.

К примеру, следующий код создает текстовый файл my.txt с содержанием Hello! World!

var storageFolder = ApplicationData.Current.LocalFolder;
var file = await storageFolder.CreateFileAsync("my.txt", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, "Hello! World!");

Аналогично можно прочитать текстовый файл:

var storageFolder = ApplicationData.Current.LocalFolder;
var file = await storageFolder.GetFileAsync("my.txt");
var text = await FileIO.ReadTextAsync(file);

В тех случаях когда нам нужно сохранить сложные типы данных, мы можем воспользоваться каким либо сериализатором. Например DataContractSerializer (в пространстве имен System.Runtime.Serialization) или сторонней библиотекой Json.net.

В прикрепленном к статье примере есть пример сохранения и чтения книги с помощью библиотеки Json.NET:

public static async Task Save(Book book)
{
    var serializedString = JsonConvert.SerializeObject(book);
    var storageFolder = ApplicationData.Current.LocalFolder;
    var file = await storageFolder.CreateFileAsync("book.dat", CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(file, serializedString);
}

public static async Task<Book> GetBook()
{
    var file = await ApplicationData.Current.LocalFolder.GetFileAsync("book.dat");
    var serializedString = await FileIO.ReadTextAsync(file);
    return await JsonConvert.DeserializeObjectAsync<Book>(serializedString);   
}

Теперь когда мы рассмотрели работу с Settings и Folder мы можем рассмотреть особенности при работае Roaming данных.

3. Особенности работы с RoamingSettings и RoamingFolder


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

Эти объекты предоставляют бесплатную возможность сохранить прохождение игры не только между переустановками приложений, но и между разными устройствами, без необходимости содержать собственную серверную инфраструктуру!

Кроме того, как уже было сказано выше, так как данные которые хранятся в этих объектах сохраняются между установками приложений, следует тщательно проектировать логику работы с данными в этих хранилищах, так как если ваше приложение, к примеру, падает при некорректных значениях в этих объектах, то будет продолжать падать даже после переустановки. Что может привести к тому что пользователь потеряет всякую возможность работы с вашим приложением.

3.1. Ограничения

Объем доступных данных ограничено и его значение можно получить из свойства RoamingStorageQuota (значение указано в килобайтах – по умолчанию 100 кб)

ApplicationData.Current.RoamingStorageQuota

Также при сохранении данных в RoamingFolder, нельзя использовать пробелы в именах файлов.

3.2. Время синхронизации

При проектировании приложения работающее через Roaming Folder надо учитывать что время синхронизации данных между устройствами составляет около 10-15 минут. Однако для RoamingSettings есть служебный ключ, который следует использовать для данных с высоким приоритетом синхронизации:

HighPriority


Время синхронизации данных для этого ключа составляет где то 15-30 секунд.

ApplicationData.Current.LocalSettings.Values["HighPriority"] = "high priority data";

Во время исполнения приложения можно также перехватывать событие изменений данных DataChanged:.

ApplicationData.Current.DataChanged += Current_DataChanged;

Так как событие вызывается не в UI потоке, обновление UI следует делать через диспетчер:

void Current_DataChanged(ApplicationData sender, object args)
{
    Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        MyTextBox.Text = (string)ApplicationData.Current.RoamingSettings.Values["HighPriority"];
    });
}

Для отладки приложения мы можем вызывать метод ApplicationData.Current.SignalDataChanged(); который вызовет срабатывание события DataChanged.

Что делать когда, в новой версии приложения требуется новая структура данных, которая не совместимо со структурой данных предыдущего приложения?

4. Версионность данных в роуминге.


Любое приложение которое продолжает развиваться со временем требует новой структуры данных. Для данных которые локально хранятся мы можем предусмотреть свои механизмы контроля версий и обновлений данных.

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

WinRT предоставляет готовое API для разделения данных на разные версии. Роуминговые данные в приложение будут приходить данные только от версий с идентичным номером. Событие DataChanged срабатывает только в тех приложениях, где установлена идентичная версия данных.

Текущую версию можно получить из свойства ApplicationData.Version:

ApplicationData.Current.Version

По умолчанию номер версии равен 0.

Настоятельно рекомендуется изначально предусматривать возможность изменений в будущем и начинать не с версии по умолчанию а с версии 1.

Можно установить новую версию данных через метод SetVersionAsync:

await ApplicationData.Current.SetVersionAsync(versinoNumber, UpgradeToVersionHandler); 
TextBlockDataVersion.Text = ApplicationData.Current.Version.ToString();


где:

versionNumber — целочисленное значение номера версии

UpgradeToVersionHandler – обработчик обновления данных:

private void UpgradeToVersionHandler(SetVersionRequest setversionrequest)
{
    var defferal = setversionrequest.GetDeferral();
    if (setversionrequest.DesiredVersion > setversionrequest.CurrentVersion)
    {
        //UpgradeCodeFromOldToNewVersion();
    }
    defferal.Complete();
}

Так как при установке новой версии приложения мы так же должны обновить данные, необходимо воспользоваться методом GetDefferral() для того что бы приложение не было закрыто в момент обновления данных.

Как и данные в роуминге, версия данных остается даже после переустановки приложения. Номер версии не синхронизируется между устройствами (Для каждого устройства свой номер версии).

Зачастую в приложениях необходимо получать прямой доступ к данным в папке. В следующем разделе рассмотрим как можно это сделать.

5. Прямой доступ к данным через Uri и Path.



В WinRT появились префиксы для прямого доступа к данным. Для этого можно использовать следующие префиксы:

ms-appx:/// – доступа к файлам, включенным в проект.

ms-appdata:///local/ – доступ к файлам, сохраненными в папке LocalFolder

ms-appdata:///roaming/ – доступ к файлам, сохраненными в папке RoamingFolder

ms-appdata:///temp/ – доступ к файлам в папке TemporaryFolder

В официальных примерах есть пример доступа к картинам с использованиям этих префиксов:

LocalImage.Source = new BitmapImage(new Uri("ms-appdata:///local/appDataLocal.png"));
RoamingImage.Source = new BitmapImage(new Uri("ms-appdata:///roaming/appDataRoaming.png"));
TempImage.Source = new BitmapImage(new Uri("ms-appdata:///temp/appDataTemp.png"));

При необходимости мы также можем получить доступ к папке где хранится приложение. Путь к папке можно получить из свойства Path для всех xFolder объектов. К примеру путь к локальной папке можно получить из свойства:

ApplicationData.Current.LocalFolder.Path

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

c:\users\UserName\AppData\Local\Packages\e965c8d4-0dff-4f2e-8340-24041aabca05_5mvwcwnjebzdj\LocalState\

где

UserName – имя пользователя в системе

e965c8d4-0dff-4f2e-8340-24041aabca05 – идентификатор приложения.

Соответственно полный путь к файлу в этой папке можно получить как простым соединением строк так и с помощью вспомогательного метода Path.Combine (Пространство имен System.IO). Так, путь к файлу my.txt будет выглядет следующим образом:

var fullPath=Path.Combine(ApplicationData.Current.RoamingFolder.Path, "my.txt");

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

Итоги


Как мы можем видеть, Microsoft существенно упростило возможность работы с данными, предоставив готовые инструменты для хранения локальных и временных данных и предоставив простой и бесплатный механизм синхронизации состояния приложения между устройствами (которые можно при необходимости использовать как облачный бекап для данных небольшого объема). Теперь вы сможете бесплатно предоставить пользователям возможность сохранить прохождение их любимой игры. :)
Tags:
Hubs:
Total votes 19: ↑16 and ↓3+13
Comments12

Articles