将流与一次性数据合并
Merging Flow with one shot data
我有一个 Flow
从数据库中获取项目(使用 Room 很容易),然后我有另一个数据源,它是一个 API。我可以称它为 API(他们没有实现 Flows,因为它是一个旧的 Java API),它会给我另一组项目(与数据库项目相同的类型)。
我的房间数据库 returns 已经有 Flow<List<MyItem>>
,我可以显示它,它会更新 adding/deleting 个项目,但现在我需要从中获取另一组项目API 并且我不知道如何进行。此外,我只有在用户登录后才能获取它们(即,如果用户拥有有效令牌)。
所以,为了使用 combine
,我使用函数 callbackFlow
将项目作为 Flow
获取,然后我将来自数据库的流和来自API。但是,我不知道如何在用户登录(获取项目)或注销(清除项目)时从 API 更新此流程。
在我的理想世界中,我会有一个具有会话令牌的 LiveData
,如果它为空,我将清除流,否则我将向 API 提出请求,但我不知道如何实现它,我是 Flow
概念和组件的新手。
编辑:我尝试了以下方法,但我不知道我是否走在正确的道路上。
override fun getItemsAsFlow(): Flow<List<Item>>{
return flow {
val isLoggedIn = _loggedIn.value ?: false
var id = 1000000L
if (isLoggedIn) {
val list = ItemAPIManager.getItems().map {
Item(id = id++, name = it.name)
}
emit(list)
}
}
}
谁能帮帮我?谢谢。
更新:根据@Tenfour04 的回答,我更新了我的代码。我的 API 供应商这样做:
override fun getItemsAsFlow(): Flow<List<Item>> {
val items =
_loggedIn.flatMapLatest { loggedIn ->
if(loggedIn) {
callbackFlow {
var id = 1000000L
val list = ItemAPIManager.getItems().map {
Item(id = id++, name = it.name)
}
trySend(list)
awaitClose()
}
}
else emptyFlow()
}
return items
}
我的 ViewModel,其中所有这些合并创建一个 RecyclerView
具有此功能:
private val _databaseItems: Flow<List<Item>> = databaseProvider.getItemsAsFlow()
private val _otherDatabaseItems: Flow<List<Item>> = databaseProvider.getSpecialItemsAsFlow()
private val _apiItems: Flow<List<Item>> = apiProvider.getItemsAsFlow()
private fun fetchItems(): LiveData<List<ItemViewModel>> {
val itemViewModelFlow = _databaseItems.combine<List<Item>, List<Item>, List<ItemViewModel>>(_otherDatabaseItems) { dbItems1, dbItems2 ->
val retList = arrayListOf<ItemViewModel>()
if (settingsProvider.areTheOtherItemsEnabled) {
retList.addAll(createViewData(dbItems2))
}
retList.addAll(createViewData(dbItems1))
return@combine retList
}.combine<List<ItemViewModel>, List<Item>, List<ItemViewModel>>(_apiItems) { itemViewModels, apiItems ->
val retList = arrayListOf<ItemViewModel>()
retList.addAll(itemViewModels)
retList.addAll(createViewData(apiItems))
return@combine retList
}
return itemViewModelFlow.asLiveData()
}
我有两个数据库列表这一事实与这里无关,我们可以将它们视为一个。基本上,如果启用了设置,这些将显示给用户,否则不会显示。
createViewData
函数只是将项目列表转换为 RecyclerView
中使用的项目视图模型列表。
现在,如果我在启动应用程序时登录,我可以看到两个数据库项目列表,我可以看到 API 项目,但如果我注销,我仍然可以看到 API 项。此外,如果我注销,我什至看不到数据库项目(我应该看到)。
感谢您的耐心等待,我还在学习中Flows
。
您的编辑尝试不会在登录状态更改时更改行为,除非您取消并重新收集它。
我在 phone 上,所以我可能会犯语法错误,但这会按照您的描述进行。我真的不明白你对数据库中的项目做了什么(比如它们是否是以前获取的 API 项目的缓存,或者它们是否是持久性补充项目)所以我把它忘了。
// In your API provider/manager class:
// update this when login state changes
private val isLoggedIn = MutableStateFlow(false)
// The callbackFlow that fetches API items
private val fetchedItems: Flow<List<MyItem>> = TODO()
// Flow that starts collecting and emitting from fetchedItems at
// login and emits an empty list when logout occurs.
val loggedInItems = isLoggedIn.flatMapLatest { loggedIn ->
if (loggedIn) fetchedItems else flowOf(emptyList())
}
如果回调流程依赖于登录提供的凭据,那么 isLoggedIn
流程可能会扩展为使用 class 将登录状态与这些凭据包装起来,并且回调流程可能会更多在 flatMapLatest 流中自然定义,因此它可以在这些凭据可用时使用它们。像这样:
sealed interface LoginState {
object LoggedOut: LoginState
data class LoggedIn(val token: String): LoginState
}
private val loginState = MutableStateFlow<LoginState>(LoggedOut)
val loggedInItems = loginState.flatMapLatest { loginState ->
when (loginState) {
LoggedOut -> flowOf(emptyList())
is LoggedIn -> callbackFlow {
val token = loginState.token
//...
}
}
}
我有一个 Flow
从数据库中获取项目(使用 Room 很容易),然后我有另一个数据源,它是一个 API。我可以称它为 API(他们没有实现 Flows,因为它是一个旧的 Java API),它会给我另一组项目(与数据库项目相同的类型)。
我的房间数据库 returns 已经有 Flow<List<MyItem>>
,我可以显示它,它会更新 adding/deleting 个项目,但现在我需要从中获取另一组项目API 并且我不知道如何进行。此外,我只有在用户登录后才能获取它们(即,如果用户拥有有效令牌)。
所以,为了使用 combine
,我使用函数 callbackFlow
将项目作为 Flow
获取,然后我将来自数据库的流和来自API。但是,我不知道如何在用户登录(获取项目)或注销(清除项目)时从 API 更新此流程。
在我的理想世界中,我会有一个具有会话令牌的 LiveData
,如果它为空,我将清除流,否则我将向 API 提出请求,但我不知道如何实现它,我是 Flow
概念和组件的新手。
编辑:我尝试了以下方法,但我不知道我是否走在正确的道路上。
override fun getItemsAsFlow(): Flow<List<Item>>{
return flow {
val isLoggedIn = _loggedIn.value ?: false
var id = 1000000L
if (isLoggedIn) {
val list = ItemAPIManager.getItems().map {
Item(id = id++, name = it.name)
}
emit(list)
}
}
}
谁能帮帮我?谢谢。
更新:根据@Tenfour04 的回答,我更新了我的代码。我的 API 供应商这样做:
override fun getItemsAsFlow(): Flow<List<Item>> {
val items =
_loggedIn.flatMapLatest { loggedIn ->
if(loggedIn) {
callbackFlow {
var id = 1000000L
val list = ItemAPIManager.getItems().map {
Item(id = id++, name = it.name)
}
trySend(list)
awaitClose()
}
}
else emptyFlow()
}
return items
}
我的 ViewModel,其中所有这些合并创建一个 RecyclerView
具有此功能:
private val _databaseItems: Flow<List<Item>> = databaseProvider.getItemsAsFlow()
private val _otherDatabaseItems: Flow<List<Item>> = databaseProvider.getSpecialItemsAsFlow()
private val _apiItems: Flow<List<Item>> = apiProvider.getItemsAsFlow()
private fun fetchItems(): LiveData<List<ItemViewModel>> {
val itemViewModelFlow = _databaseItems.combine<List<Item>, List<Item>, List<ItemViewModel>>(_otherDatabaseItems) { dbItems1, dbItems2 ->
val retList = arrayListOf<ItemViewModel>()
if (settingsProvider.areTheOtherItemsEnabled) {
retList.addAll(createViewData(dbItems2))
}
retList.addAll(createViewData(dbItems1))
return@combine retList
}.combine<List<ItemViewModel>, List<Item>, List<ItemViewModel>>(_apiItems) { itemViewModels, apiItems ->
val retList = arrayListOf<ItemViewModel>()
retList.addAll(itemViewModels)
retList.addAll(createViewData(apiItems))
return@combine retList
}
return itemViewModelFlow.asLiveData()
}
我有两个数据库列表这一事实与这里无关,我们可以将它们视为一个。基本上,如果启用了设置,这些将显示给用户,否则不会显示。
createViewData
函数只是将项目列表转换为 RecyclerView
中使用的项目视图模型列表。
现在,如果我在启动应用程序时登录,我可以看到两个数据库项目列表,我可以看到 API 项目,但如果我注销,我仍然可以看到 API 项。此外,如果我注销,我什至看不到数据库项目(我应该看到)。
感谢您的耐心等待,我还在学习中Flows
。
您的编辑尝试不会在登录状态更改时更改行为,除非您取消并重新收集它。
我在 phone 上,所以我可能会犯语法错误,但这会按照您的描述进行。我真的不明白你对数据库中的项目做了什么(比如它们是否是以前获取的 API 项目的缓存,或者它们是否是持久性补充项目)所以我把它忘了。
// In your API provider/manager class:
// update this when login state changes
private val isLoggedIn = MutableStateFlow(false)
// The callbackFlow that fetches API items
private val fetchedItems: Flow<List<MyItem>> = TODO()
// Flow that starts collecting and emitting from fetchedItems at
// login and emits an empty list when logout occurs.
val loggedInItems = isLoggedIn.flatMapLatest { loggedIn ->
if (loggedIn) fetchedItems else flowOf(emptyList())
}
如果回调流程依赖于登录提供的凭据,那么 isLoggedIn
流程可能会扩展为使用 class 将登录状态与这些凭据包装起来,并且回调流程可能会更多在 flatMapLatest 流中自然定义,因此它可以在这些凭据可用时使用它们。像这样:
sealed interface LoginState {
object LoggedOut: LoginState
data class LoggedIn(val token: String): LoginState
}
private val loginState = MutableStateFlow<LoginState>(LoggedOut)
val loggedInItems = loginState.flatMapLatest { loginState ->
when (loginState) {
LoggedOut -> flowOf(emptyList())
is LoggedIn -> callbackFlow {
val token = loginState.token
//...
}
}
}