如何让LiveData在另外两个LiveData之间切换

How to get LiveData to switch between two other LiveData

我有以下场景。播客可以来自互联网或本地 (db) 都是 LiveData

// Live
private val _live = MutableLiveData<List<Podcast>>()
val live: LiveData<List<Podcast>> = _live

// Local
val local: LiveData<List<Podcast>> = dao.observePodcasts()

// Combined
val podcasts: LiveData<List<Podcast>> = ...

我的问题是:- 我怎样才能只使用一个 LiveData podcasts 以便按需从实时或本地获取数据

fun search(query: String) {
    // podcasts <- from live
}

fun subcribed() {
    // podcasts <- from local
}

MediatorLiveData

我个人在 projects 中使用 MediatorLiveData 来实现您所描述的相同功能。

直接从文档中引用,因为它们非常简单...

Consider the following scenario: we have 2 instances of LiveData, let's name them liveData1 and liveData2, and we want to merge their emissions in one object: liveDataMerger. Then, liveData1 and liveData2 will become sources for the MediatorLiveData liveDataMerger and every time onChanged callback is called for either of them, we set a new value in liveDataMerger.

LiveData liveData1 = ...;
LiveData liveData2 = ...;

MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));

在这种情况下,您可以使用 MediatorLiveData。 您需要使用 MediatorLiveData 做的是需要 LiveData 源能够侦听对 LiveData 源的更改。

尝试以下操作:

YourViewModel.kt

private val _podcasts = MediatorLiveData<List<Podcast>>().apply {
    addSource(_live) { dataApi ->
        // Or you can do something when `_live` has a change in value.
        if(local.value == null) {
            this.value = dataApi  
        }
    }

    addSource(local) { dataLocal ->
        // Or you can do something when `local` has a change in value.
        if(_live.value == null) {
            this.value = dataLocal
        }
    }
}
val podcasts: LiveData<List<Podcast>> = _podcasts

如前所述,这可以通过 MediatorLiveData 完成。另一种选择是使用 Flows 而不是组合 LiveData。

    val podcasts = combine(local, live) { local, live ->
        // Add your implementation of how you would like to combine them
        live ?: local
    }.asLiveData(viewModelScope.coroutineContext)

如果您使用的是 Room,只需将 return 类型更改为 Flow 即可获得 Flow 结果。对于 MutableLiveData,您可以将其替换为 MutableStateFlow。

使用MediatorLiveData并没有像我预期的那样满足我的需求,因为我希望能够随时在本地和实时之间切换!

所以我做了如下实现

enum class Source {
    LIVE, LOCAL
}

private val _live = MutableLiveData<List<Podcast>>()
private val _local = dao.observePodcasts()
private val source = MutableLiveData<Source>(Source.LOCAL)

// Universal
val podcasts: LiveData<List<Podcasts>> = source.switchMap {
    liveData {
        when (it) {
            Source.LIVE -> emitSource(_live)
            else -> emitSource(_local)
        }
    }
}

emitSource() removes the previously-added source.

然后我实现了下面两个方法

fun goLocal() {
    source.postValue(Source.LOCAL)
}

fun goLive() {
    source.postValue(Source.LIVE)
}

然后我会在实时或本地存储中向观察者调用受尊重的函数

用例之一

searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
    override fun onMenuItemActionExpand(p0: MenuItem?): Boolean {
        viewModel.goLive()
        return true
    }

    override fun onMenuItemActionCollapse(p0: MenuItem?): Boolean {
         viewModel.goLocal()
         return true
    }
})