如何从 SQLite Room 检索数据并将其分配给 Compose 中的变量?

问题描述 投票:0回答:1

如何从 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"
android kotlin android-jetpack-compose android-room kotlin-coroutines
1个回答
0
投票

只有您的视图模型可以访问存储库,您的可组合项只能访问视图模型。

所以实际上调用

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 函数,所以只需删除它即可。

© www.soinside.com 2019 - 2024. All rights reserved.