我在我的应用程序中创建了一个房间数据库,我可以使用我编写的函数向其中添加数据,但我无法检索它们。 我的目标只是保存用户名和电子邮件,并在修改它们时检索它们,以便我可以在我的 UI 中显示更改。 问题是我的函数不检索信息,只是返回默认值,如 null 或空字符串。
这是我的实体:
@Entity
data class Info(
val email: String,
val userName: String,
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
)
这是我的道:
interface InfoDao {
@Upsert
suspend fun upsertInfo(info: Info)
@Delete
suspend fun deleteInfo(info: Info)
@Query("SELECT userName FROM info LIMIT 1")
fun getUsernameAsLiveData(): LiveData<String>
@Query("SELECT email FROM info LIMIT 1")
fun getEmailAsLiveData(): LiveData<String>
}
我的存储库:
class Repo(
private val db: InfoDatabase,
) {
fun openDatabase() {
db.openHelper.writableDatabase
}
suspend fun upsertInfo(info: Info) {
db.dao.upsertInfo(info)
}
suspend fun deleteInfo(info: Info) {
db.dao.deleteInfo(info)
}
fun getUsername(): LiveData<String> {
val usernameLiveData = db.dao.getUsernameAsLiveData()
return usernameLiveData
}
fun getEmail(): LiveData<String> = db.dao.getEmailAsLiveData()
}
我的视图模型:
class RoomViewModel(
private val repo: Repo,
) : ViewModel() {
private val _isOpen = MutableStateFlow(false)
val isOpen: StateFlow<Boolean> = _isOpen
private val _usernameState = MutableLiveData<String?>()
val usernameState: LiveData<String?> = _usernameState
private val _emailState = MutableLiveData<String?>()
val emailState: LiveData<String?> = _emailState
fun openDatabase() {
viewModelScope.launch {
repo.openDatabase()
_isOpen.value = true
}
}
suspend fun fetchData() {
viewModelScope.launch {
if (!isOpen.value) {
repo.openDatabase()
}
val username = repo.getUsername().value
_usernameState.value = username
val email = repo.getEmail().value
_emailState.value = email
}
}
suspend fun upsertInfo(info: Info) {
viewModelScope.launch {
repo.upsertInfo(info)
}
}
}
我的可组合项:
@Composable
fun DeviceScreen(
navController: NavHostController,
lifecycleOwner: LifecycleOwner,
roomViewModel: RoomViewModel,
) {
val username by roomViewModel.usernameState.observeAsState()
val email by roomViewModel.emailState.observeAsState()
LaunchedEffect(Unit) {
roomViewModel.openDatabase() // before this my roomDatabase was closed
}
LaunchedEffect(roomViewModel.isOpen) {
roomViewModel.fetchData()
}
}
我原本希望在 logcat 中看到正常的电子邮件和用户名,也许是一个可以使用的列表,但我得到的只是 null 或空字符串。我到处都放了日志语句,试图找出问题所在。起初我以为是因为我的数据库在数据库检查器中关闭了,但我解决了这个问题,现在它已经打开了。我可以看到我创建的虚拟用户名和电子邮件,但仍然无法访问它们。我尝试过将其转换为流程或简单字符串,但没有成功。我完全没有主意了。
使用 Kotlin 和 Compose 时,LiveData 不再适用。您应该改用Flows。
道:
存储库:
我还调整了函数名称,使其更符合 Kotlin 的命名约定(不要使用
get
,不要在名称中包含类型信息)。最大的变化是在视图模型中。您不需要打开数据库,也可以从存储库中删除(
_isOpen
、isOpen
、openDatabase
、fetchData
)。此外,切勿在视图模型中公开挂起函数。upsertInfo
作为常规的非挂起函数,它工作得很好。现在将其余属性替换为以下内容:这会将数据库流转换为 StateFlows。这是 LiveData 的现代替代品:它们还包含一个可以随时间变化且可观察的值。
在您的可组合项中移除 LaunchedEffects。然后,您可以简单地将 StateFlows 转换为 Compose State,该状态始终在数据库中发生更改时更新:
androidx.lifecycle:lifecycle-runtime-compose
为此,您需要 gradle 依赖项。就是这样:现在一切都应该按预期工作。
永远不要读取
value
LiveData 的属性(除非您在根据其先前值自动将其值更改为其他值的上下文中执行此操作)。LiveData 直到将来的某个时刻才会包含从数据库读取的值,因此您必须观察它才能获得所需的值。ViewModel 中的
openDatabase()
函数也有问题,因为它会启动一个协程并并行打开数据库,但您在实际打开之前就将其标记为打开。但这并不重要,因为您根本不需要这样做就可以使用 Room。删除
openDatabase()
与isOpen
流一起命名的两个函数。也删除该fetchData()
函数。这是不必要的卷积。相反,只需在声明站点直接将 LiveData 转换为 Flows,如下所示:或者,为了消除更多复杂性,请将这些 LiveData 更改为 DAO 和 repo 函数中的 Flows,这样您就可以删除
asFlow()
此处的部分。或者,将 ViewModel 属性改为 LiveData 而不是 StateFlows,这样您就根本不需要进行任何转换。