Я читал большую часть документации по Android и многое другое, но не могу найти информацию по этой теме. Я знаю, как создавать и редактировать состояние внутри моей модели представления, как описано во многих лабораторных работах по коду Android. Но я не уверен, как связать свое состояние внутри viewModel с данными моей БД.
Это моя модель представления:
@HiltViewModel
class MyViewModel @Inject constructor(
private val myRepository: MyRepository
) : ViewModel() {
private val _list = getList().toMutableStateList()
val List: List<MyEntity>
get() = _list
private fun getList(): List<MyEntity> {
return myRepository.getList()
}
fun addEntity(entity: MyEntity) {
myRepository.upsertMyEntity(entity)
}
}
Внутри моего Composable я инициализирую viewModle следующим образом:
myViewModel: MyViewModel = hiltViewModel<MyViewModel>()
и внутри составного элемента я получаю значение списка следующим образом:
myViewModel.List
С данным кодом моя ViewModel получает состояние базы данных списка только один раз при запуске.
private val _list = getList().toMutableStateList()
Как MutableStateList может узнать, что я добавил объект в БД? Или наоборот, как БД может узнать, что я добавил объект в Список?
Я мог бы отредактировать функцию «addEntity» внутри моей модели представления следующим образом:
fun addEntity(entity: MyEntity) {
myRepository.upsertMyEntity(entity)
_list.add(entity)
}
Но является ли это лучшей практикой? Разве MutableStateList не должен быть каким-то образом напрямую подключен к базе данных? Таким образом, создается впечатление, что я добавляю что-то вручную в единственный источник истины (мою базу данных) и вручную обновляю свое состояние, которое, возможно, может оказаться в другом состоянии, чем моя база данных. Например, если база данных выдает исключение, но состояние пользовательского интерфейса все равно меняется.
Если вы измените свой Dao так, чтобы он возвращал Flow<List<MyEntity>>
, Room создаст поток, который автоматически обновит свое значение, когда что-то изменится в базе данных. Ваша модель представления просто преобразует это в StateFlow
:
val list: StateFlow<List<MyEntity>> = myRepository.getList()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = emptyList(),
)
Теперь ваши составные элементы могут просто собрать этот поток в Compose State
вот так:
val list: List<MyEntity> by myViewModel.list.collectAsStateWithLifecycle()
(нужна зависимость androidx.lifecycle:lifecycle-runtime-compose
в Gradle)
Этот список в Compose теперь всегда будет содержать текущее состояние вашей базы данных: если вы добавите новое значение, оно также будет автоматически обновляться. addEntity
может оставаться как есть, его единственная обязанность — обновлять базу данных, а не состояние пользовательского интерфейса.
Вы можете получить список из своего Дао как Поток и наблюдать за ним. Например:
@Dao
interface RoomDao{
@Query("SELECT * FROM table)
fun getList(): Flow<List<MyEntity>>
}
В репозитории используйте dao
class Repository(val roomDao: RoomDao){
fun getList(): Flow<List<MyEntity>> = roomDao.getList()
В представленииModel
@HiltViewModel
class MyViewModel @Inject constructor(
private val myRepository: MyRepository
) : ViewModel() {
private val list = myRepository.getList().stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = emptyList(),
)}
Для Room можно использовать либо Flow, либо LiveData.
Эй, спасибо за ваш ответ, но я не смог вызвать .asStateFlow() в своем списке типа Flow<List<MyEntity>>
Ничего страшного, поменял. Рад, что вы получили ответ, который вам нужен
Приятно знать, что комната может создавать потоки! Большое спасибо за ответ, Левиафан :)