如何从 Android 中的 SQLite 数据库检索值并将其分配给变量?我想进一步将之前保存的值用于代码中的一些函数。
我的数据库有3列,第一列是自动生成的int id(键),第二列名为
type
,它存储用于过滤数据的字符串。最后一个是content
,它也存储字符串。这第三个实际上是包含有用数据的一个。
道:
@Upsert
suspend fun upsert(appData: AppData)
@Delete
suspend fun delete(appData: AppData)
@Query("SELECT * from appData WHERE type = :type ORDER BY id DESC")
fun getData(type: String): Flow<List<AppData>>
@Query("SELECT * from appData WHERE type = :type ORDER BY id DESC")
fun getDataList(type: String): List<AppData>
存储库界面:
fun getDataStream(type: String): Flow<List<AppData?>>
fun getDataListStream(type: String): List<AppData?>
suspend fun upsertData(data: AppData)
suspend fun deleteData(data: AppData)
这是离线存储库的实现:
override fun getDataStream(type: String): Flow<List<AppData?>> = appDataDao.getData(type)
override suspend fun deleteData(data: AppData) = appDataDao.delete(data)
override suspend fun upsertData(data: AppData) = appDataDao.upsert(data)
override fun getDataListStream(type: String): List<AppData?> = appDataDao.getDataList(type)
我尝试直接从可组合函数检索数据并使用同步方法,但因为它们都不起作用,所以我的代码的最新版本是这样的:
var lastDateObjectList by remember { mutableStateOf(emptyList<AppData>()) }
LaunchedEffect(streakCounterViewModel) {
coroutineScope {
streakCounterViewModel.appDataRepository.getDataStream("lastDate")
.collect { newDataList ->
lastDateObjectList = newDataList as List<AppData>
}
}
}
if (lastDateObjectList.size > 1) {/*TODO*/
for (i in 1 until lastDateObjectList.size) {
LaunchedEffect(Unit) {
streakCounterViewModel.deleteData(
id = lastDateObjectList[i]!!.id,
type = lastDateObjectList[i]!!.type,
content = lastDateObjectList[i]!!.content
)
}
}
}
val lastDateObject: AppData?/* = lastDateObjectList[0] ?: null*/
lastDateObject = if (lastDateObjectList.isNotEmpty())
lastDateObjectList[0]
else null
var lastDate = lastDateObject?.content ?: "00-00-0000"
只有您的视图模型可以访问存储库,您的可组合项只能访问视图模型。
所以实际上调用
getDataStream
是在视图模型中完成的。目前,您将其 type
参数硬编码为 "lastDate"
,您可能希望它是可变的。为此,您需要在视图模型中引入另一个属性来保存当前选定的类型,如下所示:
private val selectedType: MutableStateFlow<String?> = MutableStateFlow(null)
fun selectType(type: String) {
selectedType.value = type
}
UI 现在可以通过调用
selectType
更改应选择的类型。这将改变属性selectedType
的值。我们这里需要一个流程,以便我们可以将其内容映射到存储库的 getDataStream
函数的返回值,如下所示:
@OptIn(ExperimentalCoroutinesApi::class)
val appData: StateFlow<List<AppData>> = selectedType
.flatMapLatest { type ->
if (type == null) flowOf(emptyList())
else appDataRepository.getDataStream(type)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = emptyList(),
)
最后的
stateIn
部分将流程转换为 StateFlow
。这是必需的,以便您的可组合项可以正确收集它,如下所示:
val lastDateObjectList by streakCounterViewModel.appData.collectAsStateWithLifecycle()
为此,您需要 gradle 依赖项
androidx.lifecycle:lifecycle-runtime-compose
。
这取代了您之前的
lastDateObjectList
变量。您现在还可以删除整个 LaunchedEffect
块。
在您的存储库中,出于某种原因,您更改了流的类型以包含列表中的
null
值。这永远不会发生,所以你应该改变它的返回类型
Flow<List<AppData?>>
到
Flow<List<AppData>>
关于 Dao 中的
getDataList
函数的最后一句话:该函数会阻塞当前线程,直到数据库查询完成。永远不要那样做。相反,添加 suspend
修饰符,如下所示:
suspend fun getDataList(type: String): List<AppData>
这会强制从异步运行的协程调用该函数,并且不会阻塞主线程。
由于您已经使用了返回流的 dao 函数(这是更好的选择),您无论如何都不需要 this 函数,所以只需删除它即可。