При разработке мобильного приложения важно учитывать архитектуру хранения данных, которая наилучшим образом соответствует потребностям приложения. Во многих случаях, особенно если приложение интенсивно использует данные, база данных является лучшим вариантом. У меня был большой опыт использования пакета SQLite.NET для Xamarin, в частности. Однако в некоторых случаях база данных не является лучшим вариантом для хранения данных. Например, простое хранилище ключ-значение часто является отличным решением для небольших фрагментов данных, таких как пользовательские настройки и настройки приложения, которые можно легко получить с помощью уникального ключа.
Введение в SimpleStorage
SimpleStorage — это кроссплатформенная реализация хранилища данных типа ключ-значение, использующая собственный API-интерфейс Preferences каждой платформы. Для iOS это означает NSUserDefaults, а для Android — SharedPreferences.
SimpleStorage предоставляет простой синхронный API. Это упрощает хранение произвольных пар данных «ключ-значение» как в iOS, так и в Android. SimpleStorage также использует метод двоичной сериализации, который позволяет сериализовать объекты на диск и десериализовать их при обратном считывании. Это упрощает хранение произвольных объектов, как встроенных (например, DateTime), так и пользовательских. Я рекомендую SimpleStorage как надежное, простое и легко интегрируемое решение для вашего проекта Xamarin.
Некоторые ключевые расширения
При использовании SimpleStorage в моем последнем проекте я заметил, что ему не хватает нескольких ключевых функций. Среди них были возможности:
- Получить все ключи в заданной группе хранения
- Получить все группы хранения
- Получение полной группы хранения за один раз
В частности, невозможность получить все ключи была для меня препятствием. Я решил реализовать этот функционал как расширение для SimpleStorage.
Желаемые интерфейсы
Я хотел сохранить интерфейс, аналогичный реальному SimpleStorage, в своем классе-оболочке. Поскольку мы работаем на C#, я решил создать интерфейс IPersistentStorage для определения API этого модуля. Использование интерфейса также позволяет нам реализовать другую версию этих расширений для работы с модульными тестами (мы увидим это позже).
See the code in the full post.
Этот интерфейс похож на SimpleStorage API, за исключением нескольких отличий. Первое отличие — это свойство Groups. Этот API вернет IEnumerable всех имен групп хранения. Следующее отличие — метод KeysForGroup, возвращающий список ключей для данной группы хранения. Наконец, последнее большое отличие — API GetGroup, который возвращает словарь всех пар ключ-значение для данной группы хранения. Это действительно отличный метод полезности в некоторых ситуациях.
Реализация расширений
Основа реализации основана на частной структуре словаря, которая используется внутри для хранения групп и их ключей. Это реализовано как ConcurrentDictionary, чтобы обеспечить потокобезопасное манипулирование структурой. Ниже приведено определение класса и конструктор:
See the code in the full post.
Конструктор инициализирует словарь groupMappings на основе сохраненной схемы хранилища значений ключа. Схема обновляется по мере добавления и удаления ключей, что вы увидите чуть позже. Группа схемы хранит список групп хранения. Тогда у каждой группы хранения будет соответствующая группа хранения *Keys, которая содержит все ключи, связанные с этой группой. С помощью обеих этих предопределенных структур данных мы можем реконструировать наши группы хранения и соответствующие им ключи.
Теперь, когда мы определили базовую архитектуру, следующим шагом будет реализация отдельных интерфейсов. В некоторых случаях реализация обернутого метода проста: просто переход к эквиваленту SimpleStorage. Например, метод Get определен ниже:
See the code in the full post.
В API SimpleStorage вы можете выполнять операции Get/Set/Delete только в контексте объекта SimpleStorage (возвращенного из EditGroup). Этот API-оболочка просто позволяет вызывающей стороне передавать группу и ключ в одном и том же вызове метода, что, на мой взгляд, является хорошей абстракцией.
Однако сложная часть этой реализации заключается в отслеживании сохраняемых групп и ключей. Возьмем, например, метод Set:
See the code in the full post.
Вы заметите вызов метода TryAddGroupAndKey, который в основном принимает комбинацию группы и ключа и пытается добавить новую группу и/или ключ в хранилище. Метод TryAddGroupAndKey определен ниже:
See the code in the full post.
Помимо обновления словаря groupMappings, нам также необходимо обновить ключи для этой группы. Если это новая группа, то нам нужно добавить ее в хранилище схем. Вы можете представить очень похожую (но противоположную) реализацию для TryRemoveGroupAndKey, которая используется в методе Delete. Каждый раз, когда словарь изменяется, мы соответствующим образом обновляем сохраняемую схему, что позволяет нам всегда поддерживать правильную схему хранения. Всю реализацию PersistentStorage можно найти здесь.
Что насчет тестов?
Я также написал реализацию IPersistentStorage для тестирования под названием TestStorage. Вы также можете найти эту реализацию здесь. Эта реализация вообще не поддерживается SimpleStorage, но она хранит локальный словарь групп, ключей и значений в оперативной памяти. Он имеет все те же интерфейсы и функции, что и настоящая версия, но обычно вам не нужно беспокоиться о фактическом сохранении данных во время модульного тестирования.
Ну это все! Я весело провел время, расширяя возможности SimpleStorage, и я надеюсь, что вы узнали кое-что о том, как хранить простые данные пары «ключ-значение» в Xamarin/C#.
Первоначально опубликовано на spin.atomicobject.com 12 сентября 2016 г.