我实现了一个 Room DB,现在我的状态遇到了这个问题:
我的视图模型
@HiltViewModel
class WorkingDayViewModel @Inject constructor(
private val repository: WorkingDayRepository
) : ViewModel() {
companion object{
private const val MILLS = 5_000L
}
val allWorkingDaysState : StateFlow<WorkingDayState> = repository.getAllWorkingDays()
.map { WorkingDayState(it) }
.onEach {
println("All Working Days: $it")
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(MILLS),
initialValue = WorkingDayState()
)
fun upsertWorkingDay(workingDay: WorkingDay) {
viewModelScope.launch {
repository.upsertWorkingDay(workingDay)
}
}
}
我的@Composable
val workingDays by viewModelWorkingDays.allWorkingDaysState.collectAsState()
LaunchedEffect(key1 = workingDays) {
Log.d("workingDays", workingDays.toString())
}
工作日.kt
@Entity(tableName = "WorkingDays")
data class WorkingDay(
@PrimaryKey(autoGenerate = false)
val date: LocalDate,
val startTime: LocalTime,
val endTime: LocalTime,
val pauseTime: Int,
val fixedHourDayDuration: Int,
val isFixedHourDay: Boolean,
var isActive: Boolean,
)
WorkingDayState.kt
data class WorkingDayState(
val workingDays: List<WorkingDay> = emptyList(),
val date: LocalDate? = null,
val startTime: LocalTime? = null,
val endTime: LocalTime? = null,
val pauseTime: String = "",
val fixedHourDayDuration: Int = 0,
val isFixedHourDay: Boolean = false,
val isActive: Boolean = true,
)
WorkingDayDao.kt
@Dao
interface WorkingDaysDao {
@Upsert
fun upsertWorkingDay(workingDay: WorkingDay)
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun initInsertWorkingDay(workingDay: WorkingDay)
@Delete
fun deleteWorkingDay(workingDay: WorkingDay)
@Query("SELECT * FROM workingdays WHERE date = :date")
fun getWorkingDayByDate(date: LocalDate): Flow<WorkingDay>
@Query("SELECT * FROM workingdays")
fun getAllWorkingDays(): Flow<List<WorkingDay>>
}
WorkingDayRepository.kt
class WorkingDayRepository @Inject constructor(
private val dao: WorkingDaysDao
) {
fun upsertWorkingDay(workingDay: WorkingDay) = dao.upsertWorkingDay(workingDay)
fun initInsertWorkingDay(workingDay: WorkingDay) = dao.initInsertWorkingDay(workingDay)
fun deleteWorkingDay(workingDay: WorkingDay) = dao.deleteWorkingDay(workingDay)
fun getWorkingDayByDate(date: LocalDate) = dao.getWorkingDayByDate(date)
fun getAllWorkingDays() = dao.getAllWorkingDays()
}
类型转换器
class LocalDateConverter {
@TypeConverter
fun fromTimestamp(value: Long?): LocalDate? {
return value?.let {
ofEpochMilli(it).atZone(ZoneId.systemDefault()).toLocalDate()
}
}
@TypeConverter
fun dateToTimestamp(date: LocalDate?): Long? {
return date?.atStartOfDay(ZoneId.systemDefault())?.toInstant()?.toEpochMilli()
}
}
class LocalTimeConverter {
@TypeConverter
fun fromLocalTime(value: LocalTime?): String? {
return value?.toString()
}
@TypeConverter
fun toLocalTime(value: String?): LocalTime? {
return value?.let { LocalTime.parse(it) }
}
}
Git 上的项目: https://github.com/SkAppCoding/DebugTests
现在问题如下:
当我调用 upsertWorkingDay 并更改一些值时。这些值保存在数据库中。 allWorkingDaysState 也会发出一个新状态。打印 Log in .onEach。 但这就是问题开始的地方。我的 LaunchedEffect 未触发。
另一件事是。当我直接使用 Android Studio 中的“App Inspection”更改数据库中的值时,allWorkingDaysState 也会发出一个新值。打印 Log in .onEach。 而且我的 LaunchedEffect 也被触发了。
差别在哪里。我做错了什么?我希望你能帮助我。
我尝试了很多日志消息。但没有任何帮助我找到问题所在。
我希望如果 allWorkingDaysState 在 upsertWorkingDay 之后发出新值,我的 LaunchedEffect 也会被触发。
示例代码中缺少最重要的部分,即如何更新数据库。
从您提供的代码来看,只有一件事看起来很明显:您的 Room 实体包含声明为
isActive
的属性 var
。这可能是一个意外,因为所有其他字段都被正确声明为 val
(因为在使用 StateFlows 和 Compose 时这是必要的,对象必须是不可变),但是这个可能会诱使您实际 更改 该值。这是我可以想象您所描述的行为可能发生的唯一情况。
将其更改为
val
,一切都应该没问题。当您想要更改 isActive
时,请按照与所有其他 val
属性相同的方式进行操作:使用 copy
创建一个 new 对象。
val changedWorkingDay = workingDay.copy(isActive = !workingDay.isActive)
如果您要修改旧的
workingDay
对象,Compose 会错误地假设新值与旧值相同,因为它们的所有属性都匹配,并且不会触发重组,也不会更新 UI。