В настоящее время я использую DataStore для хранения и сбора значений для создания пользовательского интерфейса.
Есть потоки для сбора Volume и Media Path.
Я понимаю, что когда я сохраняю значение, оно должно быть собрано в соответствующем потоке, и в настоящее время оно ведет себя именно так.
Однако, когда я сохраняю Volume, также генерируется поток медиа-пути, чего не должно происходить.
suspend fun storeVolume(volume: Int) {
dataStore.edit {
it[VOLUME] = volume
}
}
fun getVolume(): Flow<Int> = dataStore.data.map {
it[VOLUME] ?: DEFAULT_VOLUME_SIZE
}
suspend fun storeMediaPath(path: String) {
dataStore.edit {
it[MEDIA] = path
}
}
fun getMediaFile(): Flow<File?> = dataStore.data.map {
it[MEDIA]?.let { path -> File(path) }
}
init {
viewModelScope.launch {
dataStoreHelper.getVolume().collectLatest { volume ->
_mainUiState.update { it.copy(volume = volume) }
}
}
viewModelScope.launch {
dataStoreHelper.getMediaFile().collectLatest { file ->
_mainUiState.update { it.copy(alarmMedia = file) }
}
}
}
Я не смог найти никакой документации, связанной с этим.
Кажется, что когда я сохраняю значение, оно передается всем потокам, собирающим это значение.
Поэтому кажется, что он выполняет ненужные задачи, и мне хотелось бы это оптимизировать.
Поток из хранилища данных (dataStore.data) будет генерироваться каждый раз, когда происходят какие-либо изменения в резервной карте данных. Вам необходимо фильтровать данные в нисходящих потоках, чтобы они отправлялись только тогда, когда отправляются интересующие их данные. Это можно сделать с помощью distinctUntilChanged. Обратите внимание, что при этом по-прежнему разрешена первоначальная эмиссия, имеющая текущее значение, сохраненное на момент начала сбора данных.
fun getVolume(): Flow<Int> = dataStore.data.map {
it[VOLUME] ?: DEFAULT_VOLUME_SIZE
}.distinctUntilChanged()
fun getMediaFile(): Flow<File?> = dataStore.data.map {
it[MEDIA]?.let { path -> File(path) }
}.distinctUntilChanged()
Но использование get____() для функций, которые не имеют аргументов и не приостанавливаются, является антипаттерном в Котлине. Это должны быть свойства:
val volume: Flow<Int> = dataStore.data.map {
it[VOLUME] ?: DEFAULT_VOLUME_SIZE
}.distinctUntilChanged()
val mediaFile: Flow<File?> = dataStore.data.map {
it[MEDIA]?.let { path -> File(path) }
}.distinctUntilChanged()
Такое поведение ожидается для потоков.
dataStore.data — исходный поток, представляющий текущее состояние DataStore. getVolume и getMediaFile отображают этот первоначальный поток.
Когда вы вызываете dataStore.edit, dataStore.data генерирует новый элемент, который затем передается всем сопоставленным потокам. Это заставляет их испускать преобразованные элементы, даже если новый элемент такой же, как и последний созданный. Это происходит потому, что потоки по умолчанию не являются distinctUntilChanged.
Чтобы это исправить, используйте distinctUntilChanged() перед сбором сопоставленных потоков. Это гарантирует, что блок collectLatest запускается только тогда, когда данные сопоставленных потоков действительно изменяются.
Обновленный код:
init {
viewModelScope.launch {
dataStoreHelper.getVolume().distinctUntilChanged().collectLatest { volume ->
_mainUiState.update { it.copy(volume = volume) }
}
}
viewModelScope.launch {
dataStoreHelper.getMediaFile().distinctUntilChanged().collectLatest { file ->
_mainUiState.update { it.copy(alarmMedia = file) }
}
}
}