Методы оценки эффективности сохранения данных в Android-приложениях

Сучков О.С.1, Ефимова Е.В.1, Ожиганова М.И.2
1 Ростовский Государственный Экономический Университет (РИНХ), Россия, Ростов-на-Дону
2 Севастопольский государственный университет, Россия, Севастополь

Статья в журнале

Информатизация в цифровой экономике (РИНЦ, ВАК)
опубликовать статью | оформить подписку

Том 3, Номер 1 (Январь-март 2022)

Цитировать эту статью:

Эта статья проиндексирована РИНЦ, см. https://elibrary.ru/item.asp?id=49852348

Аннотация:
Для хранения локальных данных в приложениях, написанных под операционную систему Android существует множество вариантов реализации хранилищ. Каждый тип хранилища предназначен для определённого типа данных. Такой подход к разделению хранилищ данных был принят из за спецификации самих данных. О разных типах хранилищ и способах их применения и пойдёт речь в данной статье.

Ключевые слова: android, хранение данных, SQL, NOSQL, ROOM, REALM

JEL-классификация: O3, Q16



Введение

Хранение данных при явных успехах в развитии вызывает все большее количество сложностей экономического характера при использовании [13, 14] (Erokhina, Erokhin, 2020; Novikova, 2020). Глобально хранилища можно разделить на внутренние и внешние. Во внутреннем хранилище вы можете сохранять файлы непосредственно во внутренней памяти устройства. По умолчанию файлы, сохраненные во внутреннем хранилище, являются приватными для вашего приложения, и другие приложения не могут получить к ним доступ (как и сам пользователь). Когда пользователь удаляет приложение, эти файлы удаляются [1].

К внутренним хранилищам относятся: SharedPreferences, базы данных, файлы и кеш.

Sharedpreferences – это облегченный класс хранилища на платформе Android, используемый для хранения различной информации о конфигурации приложения. Его суть – XML-файл, который сохраняет данные в виде пар «ключ – значение». Файл хранится в / data / data // каталог shared_prefs. С точки зрения глобальных переменных, его преимущество состоит в том, что он не создает проблем с приложениями, статическими переменными OOM (нехватка памяти) и нулевым указателем [2].

На рисунке 1 представлен пример сохранения целочисленного значения в SharedPreferences.

Рисунок 1. Сохранение целочисленного значения в SharedPreferences

На рисунке 2 представлен пример получения строкового значения из SharedPreferences.

Рисунок 2. Получение строкового значения из SharedPreferences

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

SharedPreferences сохраняются в виде файла в файловой системе устройства. По умолчанию они хранятся в каталоге данных приложения с установленными разрешениями файловой системы, которые разрешают доступ к ним только UID, с которым запускается конкретное приложение. Таким образом, они являются частными в той мере, в какой права доступа к файлам Linux ограничивают доступ к ним, как и в любой системе Linux/Unix.

Любой, у кого есть доступ на уровне root к устройству, сможет их увидеть, так как root имеет доступ ко всему в файловой системе. Кроме того, любое приложение, которое запускается с тем же UID, что и создающее приложение, сможет получить к ним доступ (обычно это не делается, и вам нужно предпринять определенные действия, чтобы два приложения запускались с одним и тем же UID, так что это, вероятно, не вызывает большой проблемы). Наконец, если кто-то смог смонтировать файловую систему вашего устройства без использования установленной ОС Android, он также может обойти разрешения, ограничивающие доступ.

Если вас беспокоит такой доступ к вашим настройкам (или любым данным, записанным вашим приложением), то вы захотите зашифровать их. Если вы так беспокоитесь о них, вам нужно будет точно определить, какая степень защиты необходима для того уровня риска, который вы видите. Обычно для шифрования данных используется EncryptedSharedPreferences – модифицированная версия SharedPreferences. Однако использование этого класса приведет к еще большему замедлению работы приложения при чтении/записи данных.

На рисунке 3 представлен пример инициализации классов для шифрования данных с помощью EncryptedSharedPreferences на языке программирования Java. После этого через объект editor следует работать с данными, как с обычным SharedPreferences.

Рисунок 3. Инициализация классов для шифрования SharedPreferences

На текущий момент Google выпустила библиотеку DataStore, которая призвана заменить заботу с SharedPreferences. Данная библиотека имеет ряд преимуществ [3]:

· предоставляет функционал для асинхронной работы при записи и чтении данных, в отличие от Shared Preference, который предоставляет асинхронный функционал только при чтении данных;

  • безопасен для работы на главном потоке;
  • защищает от ошибок парсинга в рантайме, в то время как Shared Preference может выбросить ошибку;
  • Proto DataStore предоставляет лучшую безопасность без особых настроек.
  • База данных – это упорядоченный набор структурированной информации или данных, которые обычно хранятся в электронном виде в компьютерной системе. База данных обычно управляется системой управления базами данных (СУБД). Данные вместе с СУБД, а также приложения, которые с ними связаны, называются системой баз данных или, для краткости, просто базой данных [4].

    Чаще всего в качестве SQL базы данных на устройствах с операционной системой Android используется SQLite – это встраиваемая кроссплатформенная БД, которая поддерживает достаточно полный набор команд SQL и доступна в исходных кодах (на языке C). В интернете есть очень много жалоб на скорость работы данной БД, и причин этому две. Первая – по умолчанию настроена на надежность, а не на производительность. Вторая – после любой команды SQlite будет фиксировать транзакцию (то есть ожидать, пока БД окажется в целостном состоянии для отключения питания). В зависимости от режима паранойи, SQLite потратит на это от 50 до 300 мс (ожидая окончания записи данных на диск) [5].

    Однако в чистом виде SQLite сейчас используется редко, в большинстве своем только в легаси-проектах. На текущий момент корпорацией Google была разработана библиотека Room.

    Room – это библиотека, которая является частью архитектурных компонентов Android. Она облегчает работу с объектами SQLiteDatabase в приложении, уменьшая объем стандартного кода и проверяя SQL-запросы во время компиляции [6].

    На рисунке 4 представлен класс, являющийся таблицей базы данных в библиотеке Room.

    Рисунок 4. Описание класса-таблицы в библиотеке Room

    На рисунке 5 представлен интерфейс, описывающий SQL-запросы над таблицей User с помощью библиотеки Room.

    Рисунок 5. Описание SQL-запросов библиотеки Room

    На рисунке 6 представлен код инициализации базы данных с помощью библиотеки Room.

    Рисунок 6. Инициализация базы данных с помощью библиотеки Room

    На рисунке 7 представлен код получения списка данных из таблицы User с помощью библиотеки Room.

    Рисунок 7. Получение списка данных из таблицы User

    Также при хранении данных используются NoSQL базы данных. Они специально созданы для определенных моделей данных и обладают гибкими схемами, что позволяет разрабатывать современные приложения [7].

    Список NoSQL баз данных, используемых в Android-приложениях, очень велик – это связано с тем, что каждая из них имеет разную реализацию хранения данных, а также адаптирована под определенные условия работы, что делает ее более эффективной в определенных ситуациях.

    В качестве примера NoSQL базы данных можно привести базу данных Realm – это нативная NoSQL база данных для Android (Java, Kotlin). Созданная с самого начала как настоящая объектно-ориентированная база данных, Realm отличается от других подобных библиотек тем, что обрабатывает объекты данных как живые объекты, что означает, что объекты обновляются синхронно. Они мгновенно реагируют на изменения и легко сохраняются. Мобильная база данных Realm запрашивает и синхронизирует объекты намного быстрее, чем SQLite, и без проблем обращается к данным одновременно. Это значит, что несколько источников могут получить доступ к одному и тому же объекту без необходимости управлять блокировкой или каких-либо проблем с несогласованностью данных. Данная особенность является серьезным конкурентным преимуществом [8].

    На рисунке 8 представлен график скорости обработки запросов Realm по сравнению с другими базами данных.

    Рисунок 8. График скорости обработки запросов

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

    Под термином «кэширование» могут пониматься два принципиально разных варианта сохранения данных. Первый – сохраняет временные данные, которые удалятся, как только вы закроете приложение. Второй – это сохранение данных на устройстве пользователя. В данном случае могут использоваться механизмы по хранению данных, описанные выше [9].

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

    Для получения доступа к внешнему хранилищу нам необходимо в файле-манифесте приложения указать необходимость доступа к ExternalStorage.

    На рисунке 9 представлен пример разрешения на доступ к внешнему хранилищу из файла манифеста.

    Рисунок 9. Разрешение на доступ к внешнему хранилищу

    Согласно текущей политике корпорации Google, все разрешения делятся на: время установки, обычные, подписи, специальные и опасные [10].

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

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

    Разрешения подписи – эти разрешения используются, если необходимо обеспечить взаимодействие двух приложений. В таком случае необходимо создать специальный сертификат, который будет являться разрешением на взаимодействие двух и более приложений, а также указать соответствующее разрешение в файле манифеста. Также стоит учитывать, что сертификат должен находиться у всех устройств, которые взаимодействуют друг с другом.

    Специальные разрешения используются, соответствуя определенным операциям приложения. Только платформа и производители оборудования могут определять специальные разрешения.

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

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

    Микрофон и камера обеспечивают доступ к особо конфиденциальной информации. Таким образом, система поможет вам объяснить, почему ваше приложение получает доступ к этой информации.

    Запрос на доступ к внешнему хранилищу как раз относится к категории опасных.

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

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

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

    Для решения данной задачи необходимо из главного потока (Main/UI) переключиться на поток ввода/вывода данных (IO) и после завершения операции вернуться в главный и продолжить работу приложения. Вернуться в главный поток необходимо обязательно, так как только в нем можно работать с пользовательским интерфейсом. Если попытаться взаимодействовать с интерфейсом не из главного потока, приложение завершит свою работу с ошибкой.

    Для решения проблемы многопоточности и асинхронного выполнения задач существует множество подходов и библиотек, наиболее популярные из них: RxJava, Coroutines, Flow.

    RxJava – это реализация принципов реактивного программирования для создания асинхронных программ, основанных на событиях, путем наблюдения (с помощью применения паттерна «наблюдатель») за потоками/последовательностями. RxAndroid – это оболочка на RxJava, предоставляющая больше служебных классов, специфичных для Android. Наблюдатель – это поведенческий паттерн проектирования, который создает механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах. Хотя наблюдатели могут быть мощным инструментом для разработчиков, они также создают возможность возникновения утечек памяти, если мы продолжаем наблюдать за потоком после того, как область его родительского объекта уничтожена [11].

    Корутины (coroutines) или сопрограммы – это облегченные потоки. Облегченный поток означает, что он не привязан к нативному потоку, поэтому он не требует переключения контекста на процессор, поэтому он быстрее. Главным преимуществом корутин является то, что при их использовании не блокируется поток, в котором была вызвана функция. Важно понимать, что вызванные в корутинах операции помимо того, что могут быть вызваны в другом потоке без смены контекста, выполняются в отдельных «легких потоках» и возвращают результат выполнения в поток, из которого были вызваны.

    API-интерфейс Flow в Kotlin предназначен для асинхронной обработки потока данных, который выполняется последовательно. По сути, Flow – это последовательность. В Kotlin с помощью Flow можно выполнять такие же операции, как с помощью Sequences: преобразовывать, фильтровать, сопоставлять и т. д. Основное различие между Sequences и Flow в Kotlin заключается в том, что Flow позволяет приостанавливать выполнение [12].

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

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

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

    Так как далеко не все созданные решения будут работать в более ранних версия операционной системы Android, приходится обдумывать логику решения задач в двух-трех вариантах. Android активно развивается, количество технологий, обязательных к изучению, растет. Со временем, после отказа ранних версий останется элегантный стек технологий, предлагающий единый вариант решения той или иной задачи, в том числе связанный с работой с данными. Однако на текущий момент перед разработчиком стоит много задач по обеспечению отказоустойчивости приложения, что пагубно влияет на сроки разработки.


    Источники:

    1. Android Storage: Internal, External, Removable. Часть 1/3. Habr.com. [Электронный ресурс]. URL: https://habr.com/ru/post/429338.
    2. Понимание и использование Android SharedPreferences. Russianblogs.com. [Электронный ресурс]. URL: https://russianblogs.com/article/42301191337.
    3. PreferStoring Data with Jatpack DataStore. Android-developers.googleblog.com. [Электронный ресурс]. URL: https://android-developers.googleblog.com/2020/09/prefer-storing-data-with-jetpack.html.
    4. Что такое база данных?. Oracle.com. [Электронный ресурс]. URL: https://www.oracle.com/ru/database/what-is-database.
    5. SQLite – замечательная встраиваемая БД(часть 1). Habr.com. [Электронный ресурс]. URL: https://habr.com/ru/post/149356.
    6. 7 шагов к использованию Room. Пошаговое руководство по миграции приложения на Room. Devcolibri.com. [Электронный ресурс]. URL: https://devcolibri.com/7-steps-to-room.
    7. Что такое NoSQL?. Aws.amazon.com. [Электронный ресурс]. URL: https://aws.amazon.com/ru/nosql/.
    8. Realm. Ru.bmstu.wik. [Электронный ресурс]. URL: https://ru.bmstu.wiki/Realm#:~:text.
    9. Как, почему и когда надо чистить кэш на Android. Androidinsider.ru. [Электронный ресурс]. URL: https://androidinsider.ru/polezno-znat/kak-pochemu-i-kogda-nado-chistit-kesh-na-android.html#i.
    10. Permission on Android. Developer.android.com. [Электронный ресурс]. URL: https://developer.android.com/guide/topics/permissions/overview.
    11. Реактивное программирование (RxJava/RxAndroid) d Glassdoor. Apptractor.ru. [Электронный ресурс]. URL: https://apptractor.ru/develop/coding/rxjava.
    12. Kotlinx.coroutines 1.4.0: представляем StateFlow и SharedFlow. Habr.com. [Электронный ресурс]. URL: https://habr.com/ru/company/otus/blog/531628.
    13. Ерохина Е.В., Ерохин И.И. Риски информатизации и интеллектуализация в системе информационной безопасности Российской Федерации // Экономическая безопасность. – 2020. – № 2. – c. 187-196. – doi: 10.18334/ecsec.3.2.110270.
    14. Новикова С.И. Особенности современных коммуникационно-информационных систем, оценка полезности и эффективности информации как ресурса // Вопросы инновационной экономики. – 2020. – № 1. – c. 497-510. – doi: 10.18334/vinec.10.1.100435.

    Страница обновлена: 26.11.2024 в 13:04:26