每当您在 LiveData 上调用
.observe()
时,Observer 都会收到该 LiveData 的最后一个值。这在某些情况下可能有用,但在我的情况下没有用。
每当我调用
.observe()
时,我希望观察者仅接收未来的 LiveData 更改,而不是调用 .observe()
时它所持有的值。对于一个 LiveData 实例,我可能有多个 Observer。我希望他们都在发生时收到 LiveData 更新。
我希望每个 LiveData 更新只被每个观察者使用一次。 我认为这只是对第一个要求的重新措辞,但我的头已经在旋转,我不确定。
在谷歌搜索这个问题时,我想到了两种常见的方法:
将数据包装在一个
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
不幸的是,这些例子都没有解决all我的要求。大多数时候,问题是任何新的观察者在订阅时仍然会收到最后的 LiveData 值。这意味着每当用户在屏幕之间导航时,已经显示的 Snackbar 会一次又一次地显示。
为了让您了解我在说什么/我在编码什么:
我正在关注 Android Architecture Componentns 的 LiveData MVVM 设计:
Repository.delete()
RepositoryEvents
观察存储库。所以当删除完成后,Repository 通知 ViewModel,ViewModel 通知 ListFragment。
现在,当用户切换到第二个 ListFragment 时,会发生以下情况:
.observe()
ViewModel 被创建并在 Repository 上调用
.observe()
存储库将其当前
RepositoryEvent
发送到ViewModel这里是一些简化的代码:
片段:
viewModel.dataEvents.observe(viewLifecycleOwner, Observer { showSnackbar() })
viewModel.deleteEntry()
视图模型:
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
}
存储库:
private val _events = MutableLiveData<RepositoryEvent>()
val events: LiveData<RepositoryEvent>
get() = _events
fun deleteEntry() {
// delete it from database
_events.postValue(RepositoryEvent.OnDeleteSuccess)
}
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()
}
}
}
}
}
}
MainViewModel
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"))
}
}
对我来说问题是这样解决的:
事件包装类来保存事件相关数据(从谷歌示例中复制)
public class Event<T> {
private T mContent;
private boolean hasBeenHandled = false;
public Event( T content) {
if (content == null) {
throw new IllegalArgumentException("null values in Event are not allowed.");
}
mContent = content;
}
@Nullable
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return mContent;
}
}
public boolean hasBeenHandled() {
return hasBeenHandled;
}
}
接下来,我创建事件观察器类,它处理数据检查(空等):
public class EventObserver<T> implements Observer<Event<T>> {
@Override
public void onChanged(Event<T> tEvent) {
if (tEvent != null && !tEvent.hasBeenHandled())
onEvent(tEvent.getContentIfNotHandled());
}
protected void onEvent(@NonNull T content) {}
}
以及事件处理程序类,以简化从视图模型的访问:
public class EventHandler<T> {
private MutableLiveData<Event<T>> liveEvent = new MutableLiveData<>();
public void observe(@NonNull LifecycleOwner owner, @NonNull EventObserver<T> observer){
liveEvent.observe(owner, observer);
}
public void create(T content) {
liveEvent.setValue(new Event<>(content));
}
}
例子:
在 ViewModel.class 中:
private EventHandler<Boolean> swipeEventHandler = new EventHandler<>();
public EventHandler<Boolean> getSwipeEventHandler() {
return swipeEventHandler;
}
在活动/片段中:
开始观察:
viewModel
.getSwipeEventHandler()
.observe(
getViewLifecycleOwner(),
new EventObserver<Boolean>() {
@Override
protected void onEvent(@NonNull Boolean content) {
if(content)confirmDelete(modifier);
}
});
创建事件:
viewModel.getSwipeEventHandler().create(true);
根据需要创建了一个基本的密封类标志:
sealed class Event(private var handled: Boolean = false) {
val coldData: Event?
get() {
return if (handled) null else {
handled = true
this
}
}
class ShowLoader() : Event()
class HideLoader() : Event()
class ShowErrorAlert(@StringRes val message: Int) : Event()
}
然后可以在不同的片段观察
viewModel.eventFlow.observe(this) { event ->
val data = event.coldData
when (data) {
is Event.ShowLoader -> {
progressBar.visible = true
}
is Event.HideLoader -> {
progressBar.visible = false
}
is Event.ShowErrorAlert -> {
showAlert(data.message)
}
else -> {
// do nothing
}
}
}
或者使用具有相同目的的
MutableLiveData
的子类来单独处理它们。
您可以在扩展功能中添加此功能。只需用它替换对
observe
的调用即可。它只会发出在观察LiveData对象后发出的事件。
fun <T> LiveData<T>.observeFutureEvents(owner: LifecycleOwner, observer: Observer<T>) {
observe(owner, object : Observer<T> {
var isFirst = true
override fun onChanged(value: T) {
if (isFirst) {
isFirst = false
} else {
observer.onChanged(value)
}
}
})
}