Управление настройками с помощью Jetpack DataStore: Простой подход к сохранению данных разных типов

—

от автора

Введение

Всем здравствуйте. Это моя первая статья на такую публику, поэтому просьба не судить строго! 🙂

При разработке Android-приложений часто возникает необходимость сохранять пользовательские настройки, такие как выбор темы, язык или другие данные пользователя. Jetpack DataStore — это современное решение для хранения таких данных, которое заменяет устаревший SharedPreferences. В этой статье мы рассмотрим, как использовать DataStore для хранения различных типов данных (Boolean, Integer, Float и String) и как интегрировать его с архитектурой MVVM, используя StateFlow для обновления UI.

Что такое DataStore?

Jetpack DataStore — это библиотека для хранения данных, поддерживающая асинхронное и безопасное чтение и запись данных. Она предоставляет два типа хранилищ:

1. Preferences DataStore: Используется для хранения пар «ключ-значение» (похож на SharedPreferences).

2. Proto DataStore: Используется для хранения структурированных данных на основе протокольных буферов.

В этой статье мы сосредоточимся на Preferences DataStore, которое идеально подходит для хранения настроек приложения.


Структура проекта

1. DataStoreManager — Класс, который абстрагирует логику работы с DataStore и предоставляет методы для сохранения и получения данных различных типов.

2. AppViewModel — ViewModel, который обрабатывает логику взаимодействия с DataStoreManager. Мы будем использовать StateFlow для асинхронного обновления состояния.

3. Использование в UI — Примеры того, как использовать ViewModel в Activity, Fragment или внутри @Composable для взаимодействия с данными и обновления UI.


Необходимые библиотеки

Чтобы использовать Jetpack DataStore в вашем проекте, необходимо добавить следующие зависимости в файл build.gradle:

dependencies {      // Jetpack DataStore      implementation "androidx.datastore:datastore-preferences:1.0.0"            // Поддержка Kotlin Coroutines      implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0"      implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0"      // Компоненты Lifecycle для ViewModel и LiveData      implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"      implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"  }

Эти зависимости обеспечивают поддержку для:

  • DataStore Preferences для хранения пар «ключ-значение»

  • Kotlin Coroutines для асинхронных операций

  • Компонентов Lifecycle для управления ViewModel и LiveData


1. DataStoreManager

Класс DataStoreManager абстрагирует взаимодействие с DataStore. Мы создаем универсальные методы для сохранения и чтения данных различных типов: Boolean, Integer, Float и String.

Код для `DataStoreManager`

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")  class DataStoreManager(private val context: Context) {      // Универсальный метод для сохранения данных      suspend fun <T> save(key: Preferences.Key<T>, value: T) {          context.dataStore.edit { sния данных      fun <T> read(key: Preferences.Key<T>, defaultValue: T): Flow<T> = context.dataStore.data          .map { settings ->              settings[key] ?: defaultValue          }      // Пример для работы с Boolean      private val BOOLEAN_KEY = booleanPreferencesKey("boolean_key")      suspend fun setBoolean(value: Boolean) = save(BOOLEAN_KEY, value)      fun getBoolean(): Flow<Boolean> = read(BOOLEAN_KEY, false)      // Пример для работы с Integer      private val INTEGER_KEY = intPreferencesKey("integer_key")      suspend fun setInteger(value: Int) = save(INTEGER_KEY, value)      fun getInteger(): Flow<Int> = read(INTEGER_KEY, 0)      // Пример для работы с Float      private val FLOAT_KEY = floatPreferencesKey("float_key")      suspend fun setFloat(value: Float) = save(FLOAT_KEY, value)      fun getFloat(): Flow<Float> = read(FLOAT_KEY, 0f)      // Пример для работы с String      private val STRING_KEY = stringPreferencesKey("string_key")      suspend fun setString(value: String) = save(STRING_KEY, value)      fun getString(): Flow<String> = read(STRING_KEY, "")  }

Описание методов:

  • save(key: Preferences.Key<T>, value: T) — Сохраняет данные в `DataStore`. Метод принимает ключ и значение для сохранения

  • read(key: Preferences.Key<T>, defaultValue: T) — Читает данные из DataStore. Если данные отсутствуют, возвращает значение по умолчанию

  • Методы для каждого типа данных (setBoolean, getBoolean, setInteger, getInteger и т. д.) облегчают работу с Boolean, Integer, Float, String


2. AppViewModel

AppViewModel обрабатывает логику взаимодействия с DataStoreManager и предоставляет реактивное поведение с использованием StateFlow. Каждый тип данных (Boolean, Integer, Float, String) представляет поток данных, который можно наблюдать в UI.

Код для AppViewModel

class AppViewModel(application: Application) : AndroidViewModel(application) {      private val dataStoreManager = DataStoreManager(application)      // Обработчик для Boolean      val booleanValue: StateFlow<Boolean> = dataStoreManager.getBoolean()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)      fun setBoolean(value: Boolean) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setBoolean(value)          }      }      // Обработчик для Integer      val integerValue: StateFlow<Int> = dataStoreManager.getInteger()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0)      fun setInteger(value: Int) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setInteger(value)          }      }      // Обработчик для Float      val floatValue: StateFlow<Float> = dataStoreManager.getFloat()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0f)      fun setFloat(value: Float) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setFloat(value)          }      }      // Обработчик для String      val stringValue: StateFlow<String> = dataStoreManager.getString()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "")      fun setString(value: String) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setString(value)          }      }      // ViewModelFactory для создания AppViewModel      class AppViewModelFactory(private val application: Application) : ViewModelProvider.Factory {          override fun <T : ViewModel> create(modelClass: Class<T>): T {              if (modelClass.isAssignableFrom(AppViewModel::class.java)) {                  return AppViewModel(application) as T              }              throw IllegalArgumentException("Unknown ViewModel class")          }      }  }
  • StateFlow для каждого типа данных (например, booleanValue, integerValue, floatValue, stringValue) позволяет наблюдать изменения состояния

  • setBoolean(value: Boolean) — Метод для сохранения нового значения для Boolean

  • setInteger(value: Int) — Метод для сохранения нового значения для Integer

  • setFloat(value: Float) — Метод для сохранения нового значения для Float

  • setString(value: String) — Метод для сохранения нового значения для String


3. Использование в приложении

Чтобы использовать AppViewModel в Activity, Fragment или же внутри Composable, создайте экземпляр ViewModel и подпишитесь на изменения состояния.

Пример использования

class AppViewModel(application: Application) : AndroidViewModel(application) {      private val dataStoreManager = DataStoreManager(application)      // Обработчик для Boolean      val booleanValue: StateFlow<Boolean> = dataStoreManager.getBoolean()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)      fun setBoolean(value: Boolean) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setBoolean(value)          }      }      // Обработчик для Integer      val integerValue: StateFlow<Int> = dataStoreManager.getInteger()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0)      fun setInteger(value: Int) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setInteger(value)          }      }      // Обработчик для Float      val floatValue: StateFlow<Float> = dataStoreManager.getFloat()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0f)      fun setFloat(value: Float) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setFloat(value)          }      }      // Обработчик для String      val stringValue: StateFlow<String> = dataStoreManager.getString()          .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "")      fun setString(value: String) {          viewModelScope.launch(Dispatchers.IO) {              dataStoreManager.setString(value)          }      }      // ViewModelFactory для создания AppViewModel      class AppViewModelFactory(private val application: Application) : ViewModelProvider.Factory {          override fun <T : ViewModel> create(modelClass: Class<T>): T {              if (modelClass.isAssignableFrom(AppViewModel::class.java)) {                  return AppViewModel(application) as T              }              throw IllegalArgumentException("Unknown ViewModel class")          }      }  }

Заключение

В этой статье мы рассмотрели, как использовать Jetpack DataStore для управления настройками приложения, предоставляя простой способ работы с различными типами данных, такими как Boolean, Integer, Float и String. Мы продемонстрировали, как интегрировать DataStore с архитектурой MVVM, используя StateFlow для асинхронных обновлений UI. Этот подход помогает сделать хранение и доступ к данным более эффективным, безопасным и реактивным.


Контакты автора статьи  


ссылка на оригинал статьи https://habr.com/ru/articles/874034/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *