嘿,我正在学习 kotlin 中的流程。我正在学习MutableStateFlow和MutableSharedFlow。我尝试在现实世界的例子中学习MutableStateFlow。但我无法获得MutableSharedFlow示例,它更适合。我尝试了一些MutableStateFlow
例如,当我们从 api 获取数据时,我们可以使用 seal 类来进行相应的填充。
LoggedState.kt
sealed class LoggedState {
data class OnSuccess(val data: List<XYZ>) : LoggedState()
object OnEmpty : LoggedState()
data class IsLoading(val isLoading: Boolean = true) : LoggedState()
data class OnError(val message: String) : LoggedState()
}
Run Code Online (Sandbox Code Playgroud)
设置ViewModel.kt
class SettingsViewModel : ViewModel() {
var loggedMutableStateFlow = MutableStateFlow<LoggedState>(LoggedState.OnEmpty)
fun fetchData(){
val result = dataRepository.getLogged()
result.handleResult(
onSuccess = { response ->
val data = response?.items
if (!data.isNullOrEmpty()) {
loggedMutableStateFlow.value …
Run Code Online (Sandbox Code Playgroud) android kotlin kotlin-flow kotlin-stateflow kotlin-sharedflow
MutableSharedFlow 有 3 个参数:replay、extraBufferCapacity 和 onBufferOverflow。replay 和 extraBufferCapacity 有什么区别?
该文档提到以下内容:
replay - 重播给新订阅者的值的数量(不能为负数,默认为零)。
extraBufferCapacity - 除了重放之外缓冲的值的数量。当还有剩余缓冲区空间时,emit 不会暂停(可选,不能为负数,默认为零)。
我不明白两者之间的区别以及何时需要 extraBufferCapacity > 0。 extraBufferCapacity 只是发射器的额外重播能力吗?
android kotlin kotlin-coroutines kotlin-flow kotlin-sharedflow
过去,我LocalBroadcastManager and EventBus
在聊天和出租车应用程序中使用这些应用程序,但现在这些应用程序要么已弃用,要么不建议使用它们。
我打算用新的数据结构替换它们,例如mutablesharedflow or channel
,我想知道哪一个更适合我的情况?或者也许是另一种数据结构?
我有一个登录表格。我用来StateFlow
发送LoginResult
(调用 API 之后)从ViewModel
到Activity
。在活动中,如果登录失败,我将显示错误对话框。
第一次运行良好,但从第二次登录失败后,错误对话框将不再显示。我测试了.value
两者.emit
StateFlow
private val _loginResult = MutableStateFlow(LoginResult())
val loginResult: StateFlow<LoginResult> = _loginResult
fun login(email: String, password: String) {
viewModelScope.launch {
when (val result = loginRepository.login(email, password)) {
is Result.Fail-> {
_loginResult.value = LoginResult(error = "Login failed")
// _loginResult.emit(LoginResult(error = "Login failed")) same issue
}
...
}
}
}
Run Code Online (Sandbox Code Playgroud) 最近,我一直在使用 StateFlow、SharedFlow 和 Channels API,但在尝试将代码从 LiveData 迁移到表示层中的 StateFlow 时,我遇到了一个常见用例。
我面临的问题是,当我发出数据并将其收集到 viewModel 中时,我可以将值设置为 mutableStateFlow,当它最终到达片段时,它会使用 Toast 显示一些信息性消息,让用户知道是否发生错误发生了或者一切都很顺利。接下来,有一个按钮可以导航到另一个片段,但是如果我返回到已经有失败意图结果的上一个屏幕,它会再次显示 Toast。这正是我想要弄清楚的。如果我已经收集了结果并向用户显示了消息,我不想继续这样做。如果我导航到另一个屏幕并返回(当应用程序从后台返回时也会发生这种情况,它会再次收集最后一个值)。LiveData 没有发生这个问题,我只是做了完全相同的事情,公开来自存储库的流并通过 ViewModel 中的 LiveData 收集。
代码:
class SignInViewModel @Inject constructor(
private val doSignIn: SigninUseCase
) : ViewModel(){
private val _userResult = MutableStateFlow<Result<String>?>(null)
val userResult: StateFlow<Result<String>?> = _userResult.stateIn(viewModelScope, SharingStarted.Lazily, null) //Lazily since it's just one shot operation
fun authenticate(email: String, password: String) {
viewModelScope.launch {
doSignIn(LoginParams(email, password)).collect { result ->
Timber.e("I just received this $result in viewmodel")
_userResult.value = result
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在我的片段中: …
当导航回片段时,如何避免再次执行collect{}代码。
视图模型类
private val _commitResult = MutableStateFlow<Map<String, Any>>(mapOf())
val commitResult: StateFlow<Map<String, Any>> = _commitResult
Fragment code like this:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED){
viewModel.commitResult.collect { data ->
Logger.i("commitResult $data")
//navigate to another fragment
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我首先更改 viewModel 中的 _commitResult 值时,跳转到另一个片段效果很好。不幸的是,当我回到片段时。collect{ // navigate to another fragment}
将再次执行。
我知道什么时候回到片段。onCreateView 再次执行,viewModel 会发出之前的数据存储,因此执行collect { // navigate to another fragment}
。我怎样才能避免这种情况?
与 LiveData 相同,我使用 Event 来修复 LiveData 的问题。
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow …
Run Code Online (Sandbox Code Playgroud) 在我的 ViewModel 中,我正在发出 API 请求,并且正在使用 FragmentStateFlow
并SharedFlow
与 Fragment 进行通信。在发出 API 请求时,我可以轻松更新状态流的值,并将其成功收集到片段中。
但在发出请求之前,我发出了一些布尔值,SharedFlow
并且它没有被收集在片段中。有人可以帮助我为什么会发生这种情况吗?
class MainViewModel: ViewModel() {
private val _stateFlow = MutableStateFlow(emptyList<Model>())
val stateFlow = _stateFlow.asStateFlow()
private val _loading = MutableSharedFlow<Boolean>()
val loading = _loading.asSharedFlow()
suspend fun request() {
_loading.emit(true)
withContext(Dispatchers.IO) {
/* makes API request */
/* updates _stateFlow.value */
/* stateFlow value is successfully collected */
}
_loading.emit(false) // emitting boolean value
}
}
Run Code Online (Sandbox Code Playgroud)
class MyFragment : Fragment(R.layout.fragment_my) {
// ...
override …
Run Code Online (Sandbox Code Playgroud) android kotlin kotlin-coroutines kotlin-flow kotlin-sharedflow
StateFlow / SharedFlow 中此实时数据转换的等效代码是什么?
val myLiveData: LiveData<MyLiveData> = Transformations
.switchMap(_query) {
if (it == null) {
AbsentLiveData.create()
} else {
repository.load()
}
Run Code Online (Sandbox Code Playgroud)
基本上,我想监听每个查询更改以对返回的内容做出反应。因此,任何类似于使用 StateFlow / SharedFlow 的东西都是受欢迎的。
kotlin android-livedata kotlin-flow kotlin-stateflow kotlin-sharedflow
我有一个返回流的 API,我使用 shareIn() 将其转换为共享流。我有一个用例,我想重置共享流中的缓存值并强制流获取新数据。我们有一个resetReplayCache()
,但它仅适用于 MutableSharedFlow。
class User(val id: String) {
private val _flow = SharedFlow<Data> by lazy {
Api.observeData(id).map {result-> transformData(result) } . shareIn(scope, timeout, 1)
}
val flow = _flow
fun forceFetchNewData() {
_flow.resetReplayCache()// how can I do this?
}
}
Run Code Online (Sandbox Code Playgroud)
我真的很感激一些关于将 shareFlow 转换为 mutableSharedFlow 或清空共享流缓存的指示
SharedFlow
我认为收集数据是可以的onViewCreated
。但是当我替换片段 n 次然后我触发一些事件时SharedFlow
,它会向我发出 n 次事件而不是一个事件。
为了解决这个问题,我将代码放入onCreate
片段中。我找不到任何相关文档。我是否遗漏了一些东西,或者我应该继续收集SharedFlow
片段onCreate
?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launchWhenResumed {
viewModel.uiEffect.collect {
when (it) {
is ViewEffectWrapper.PageEffect -> {
if (it.pageEvent is LoginViewEffect.OpenHomePage) {
startActivity(
Intent(
this@LoginFragment.context,
HomeActivity::class.java
)
)
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的SharedFlow
定义ViewModel
private val _uiEffect = MutableSharedFlow<ViewEffectWrapper<LoginViewEffect>>(replay = 0)
val uiEffect: SharedFlow<ViewEffectWrapper<LoginViewEffect>> = _uiEffect
Run Code Online (Sandbox Code Playgroud)