mue*_*flo 7 android android-lifecycle android-mvvm android-livedata android-architecture-components
每当您调用.observe()LiveData 时,观察者都会收到该 LiveData 的最后一个值。这在某些情况下可能有用,但对我来说没有用。
每当我调用 时.observe(),我希望观察者只接收未来的 LiveData 更改,而不是.observe()调用时它所持有的值。
对于一个 LiveData 实例,我可能有多个观察者。我希望他们都能在发生 LiveData 更新时收到更新。
我希望每个 LiveData 更新仅被每个观察者使用一次。我认为这只是对第一个要求的重新表述,但我的头已经开始旋转,我对此不确定。
在谷歌搜索这个问题时,我发现了两种常见的方法:
将数据包装在 an 中LiveData<SingleEvent<Data>>并检查该类SingleEvent是否已被消耗。
如果观察者已经获得事件,则扩展MediatorLiveData并使用查找映射
这些方法的示例可以在这里找到: https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#gistcomment-2783677 https://gist.github.com/hadilq/f095120348a6a14251a02aca329f1845#file-liveevent-kt https://gist .github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#file-event-kt
不幸的是,这些例子都不能满足我的所有要求。大多数时候,问题是任何新的观察者在订阅时仍然收到最后的 LiveData 值。这意味着每当用户在屏幕之间导航时,已经显示的 Snackbar 就会一次又一次地显示。
为了让您了解我正在谈论的内容/我正在编码的内容:
我遵循Android架构组件的LiveData MVVM设计:
Repository.delete()RepositoryEvents。因此,当删除完成后,Repository 会通知 ViewModel,ViewModel 也会通知 ListFragment。
现在,当用户切换到第二个 ListFragment 时,会发生以下情况:
.observe()其 ViewModelViewModel 被创建并调用.observe()存储库
存储库将其当前数据发送RepositoryEvent到 ViewModel
这是一些简化的代码:
分段:
viewModel.dataEvents.observe(viewLifecycleOwner, Observer { showSnackbar() })
viewModel.deleteEntry()
Run Code Online (Sandbox Code Playgroud)
视图模型:
val dataEvents: LiveData<EntryListEvent> = Transformations.switchMap(repository.events, ::handleRepoEvent)
fun deleteEntry() = repository.deleteEntry()
private fun handleRepoEvent(event: RepositoryEvent): LiveData<EntryListEvent> {
// convert the repository event to an UI event
}
Run Code Online (Sandbox Code Playgroud)
存储库:
private val _events = MutableLiveData<RepositoryEvent>()
val events: LiveData<RepositoryEvent>
get() = _events
fun deleteEntry() {
// delete it from database
_events.postValue(RepositoryEvent.OnDeleteSuccess)
}
Run Code Online (Sandbox Code Playgroud)
2021 年更新:
使用协程库和 Flow 现在可以很容易地通过实现来实现这一点Channels:
主要活动
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import com.plcoding.kotlinchannels.databinding.ActivityMainBinding
import kotlinx.coroutines.flow.collect
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
binding.btnShowSnackbar.setOnClickListener {
viewModel.triggerEvent()
}
lifecycleScope.launchWhenStarted {
viewModel.eventFlow.collect { event ->
when(event) {
is MainViewModel.MyEvent.ErrorEvent -> {
Snackbar.make(binding.root, event.message, Snackbar.LENGTH_LONG).show()
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
主视图模型
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
class MainViewModel : ViewModel() {
sealed class MyEvent {
data class ErrorEvent(val message: String): MyEvent()
}
private val eventChannel = Channel<MyEvent>()
val eventFlow = eventChannel.receiveAsFlow()
fun triggerEvent() = viewModelScope.launch {
eventChannel.send(MyEvent.ErrorEvent("This is an error"))
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3135 次 |
| 最近记录: |