android 让每个观察者在订阅/观察时仅接收 * 新 * LiveData

wnvonmuf  于 2023-02-20  发布在  Android
关注(0)|答案(3)|浏览(192)

每当你在LiveData上调用.observe()时,Observer就会接收到LiveData的最后一个值,这在某些情况下可能有用,但在我的情况下就没用了。
1.无论何时调用.observe(),我都希望Observer只接收将来的LiveData更改,而不是它在调用.observe()时保存的值。
1.一个LiveData示例可能有多个Observer。我希望它们在发生LiveData更新时都能接收到更新。
1.**我希望每个观察者只使用一次LiveData更新。**我认为这只是第一个要求的重新措辞,但我的头已经旋转,我不确定它。
在谷歌搜索这个问题时,我发现了两种常用的方法:
1.将数据 Package 在LiveData<SingleEvent<Data>>中,并检查SingleEvent类是否已被使用。
1.扩展MediatorLiveData并使用查找图(如果观察者已获得事件)
这些方法的示例可在此处找到:https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#gistcomment-2783677https://gist.github.com/hadilq/f095120348a6a14251a02aca329f1845#file-liveevent-kthttps://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#file-event-kt
不幸的是,这些例子没有一个能满足我的所有需求。大多数时候,问题是任何新的观察者在订阅时仍然会收到最后一个LiveData值。这意味着当用户在屏幕之间导航时,已经显示的Snackbar会一次又一次地显示。
为了给予您了解我正在谈论的内容/正在编写的代码:
我遵循Android架构组件的LiveData MVVM设计:

  • 2 ListFragment显示了一个条目列表。
  • 他们使用相同ViewModel类的2个示例观察UI相关LiveData。
  • 用户可以删除这样的ListFragment中的条目,删除是通过ViewModel调用Repository.delete()完成的
  • 视图模型观察RepositoryEvents的存储库。

因此,当删除完成时,存储库通知ViewModel,ViewModel通知ListFragment。
现在,当用户切换到第二个ListFragment时,将发生以下情况:

  • 创建第二个片段并在其ViewModel上调用.observe()
  • 创建ViewModel并调用存储库上的.observe()
  • 存储库将其当前RepositoryEvent发送到ViewModel
  • ViewModel将相应的UI事件发送到片段
  • Fragment显示了在其他地方发生的删除的确认Snackbar。

下面是一些简化的代码:

片段:

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)
}
6kkfgxo0

6kkfgxo01#

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()
                    }
                }
            }
        }

    }
}
    • 主视图模型**
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"))
    }
}
vd8tlhqk

vd8tlhqk2#

对我来说,问题解决了这个:
事件 Package 类,用于保存事件相关数据(从google示例复制)

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;
    }
}

接下来,我创建事件观察器类,它处理数据检查(null等):

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类中:

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);
wfveoks0

wfveoks03#

创建了一个基本的密封类标志,需要:

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的子类来单独处理它们。

相关问题