如何再次调用LiveData协程块

时间:2019-10-05 05:47:13

标签: android kotlin android-livedata kotlin-coroutines android-viewmodel

我正在使用LiveData的版本“ androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha05”。一旦LiveData块成功执行,我想明确触发它再次执行,例如

  1. 我导航到一个片段
  2. 用户的数据加载
  3. 我在同一片段中单击删除btn
  4. 用户数据应刷新

我有一个片段,可以观察我的LiveData,一个带有LiveData和存储库的ViewModel:

ViewModel:

  fun getUserLiveData() = liveData(Dispatchers.IO) {

   val userData = usersRepo.getUser(userId)

   emit(userData) 
}

片段:

viewModel.getUserLiveData(viewLifecycleOwner,
            androidx.lifecycle.Observer {.. 

然后我正在尝试实现所需的行为,如下所示:

viewModel.deleteUser()

viewModel.getUserLiveData()

如果成功完成LiveData块下面的文档,并且如果我在LiveData块中放入 while(true),那么我的数据将刷新,但是我不刷新希望这样做,因为我需要以被动方式更新视图。

  

如果[block]成功完成,则由于[LiveData]以外的原因而被取消    变为非活动状态,即使[LiveData]处于活动状态,它也不会重新执行    非活动周期。

也许我错过了一些东西,我该如何重用相同的LiveDataScope来实现此目标?任何帮助将不胜感激。

6 个答案:

答案 0 :(得分:1)

我找到了解决方案。我们可以使用switchMap手动调用LiveDataScope

首先,让我们看一下switchMap的官方示例:

/**
 * Here is an example class that holds a typed-in name of a user
 * `String` (such as from an `EditText`) in a [MutableLiveData] and
 * returns a `LiveData` containing a List of `User` objects for users that have
 * that name. It populates that `LiveData` by requerying a repository-pattern object
 * each time the typed name changes.
 * <p>
 * This `ViewModel` would permit the observing UI to update "live" as the user ID text
 * changes.
**/
class UserViewModel: AndroidViewModel {
    val nameQueryLiveData : MutableLiveData<String> = ...

    fun usersWithNameLiveData(): LiveData<List<String>> = nameQueryLiveData.switchMap {
        name -> myDataSource.usersWithNameLiveData(name)
    }

    fun setNameQuery(val name: String) {
        this.nameQueryLiveData.value = name;
    }
}

这个例子很清楚。我们只需要将nameQueryLiveData更改为您自己的类型,然后将其与LiveDataScope组合即可。如:

class UserViewModel: AndroidViewModel {
    val _action : MutableLiveData<NetworkAction> = ...

    fun usersWithNameLiveData(): LiveData<List<String>> = _action.switchMap {
        action -> livedata(Dispatchers.IO){
            when (action) {
                Init -> {
                    // first network request or fragment reusing
                    // check cache or something you saved.
                    val cache = getCache()
                    if (cache == null) {
                        // real fecth data from network
                        cache = repo.loadData()
                    }
                    saveCache(cache)
                    emit(cache)
                }
                Reload -> {
                    val ret = repo.loadData()
                    saveCache(ret)
                    emit(ret)
                }
            }
        }
    }

    // call this in activity, fragment or any view
    fun fetchData(ac: NetworkAction) {
        this._action.value = ac;
    }

    sealed class NetworkAction{
        object Init:NetworkAction()
        object Reload:NetworkAction()
    }
}


答案 1 :(得分:0)

您可以创建一个可观察对象,并在viewModel中设置该值,然后在片段中对其进行观察。这是您可以使用的示例:

ViewModel

  private val _myObservable = LiveData<*>
  val observable : LiveData<*> = _myObservable
  
  
  fun getUserLiveData(){

   val userData = usersRepo.getUser(userId)

  _myObservable.value = userData
}

然后在片段中:

viewModel.myObservable.observeNullsafe(owner){
//do anything you want to do with the user value
}

因此,现在无论何时调用getUserData,它都会将更改发送到myObservable中,您可以使用它。您不需要自己发射它。

希望有帮助。

答案 2 :(得分:0)

要使用liveData {..}块执行此操作,您需要定义一些命令源,然后在一个块中订阅它们。示例:

tabbrev

您应该问自己一个问题:也许改用普通的MutableLiveData并自己更改其值会容易些吗?

livedata {...}构建器可以很好地工作,因为您可以收集一些数据流(例如,来自Room DB的Flow / Flowable),而对于普通的非流源则不太理想,您需要通过以下方式来请求数据你自己。

答案 3 :(得分:0)

您可以使用MediatorLiveData

以下是您如何实现此目标的要点。

class YourViewModel : ViewModel() {

    val mediatorLiveData = MediatorLiveData<String>()

    private val liveData = liveData<String> {  }

    init {
        mediatorLiveData.addSource(liveData){mediatorLiveData.value = it}
    }

    fun refresh() {
        mediatorLiveData.removeSource(liveData)
        mediatorLiveData.addSource(liveData) {mediatorLiveData.value = it}
    }
}

mediatorLiveData暴露于您的Viewobserve()上,当您的用户被删除时,请致电refresh(),其余用户应照常工作。

答案 4 :(得分:0)

首先将implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"添加到gradle文件中。使您的ViewModel如下:

MyViewModel() : ViewModel() {
   val userList = MutableLiveData<MutableList<User>>()

   fun getUserList() {
       viewModelScope.launch {
           userList.postValue(usersRepo.getUser(userId))
       }
   }
}

然后保留用户列表:

viewModel.sessionChartData.observe(viewLifecycleOwner, Observer { users ->
    // Do whatever you want with "users" data
})

制作一个extension从userList中删除单个用户并得到通知:

fun <T> MutableLiveData<MutableList<T>>.removeItemAt(index: Int) {
    if (!this.value.isNullOrEmpty()) {
        val oldValue = this.value
        oldValue?.removeAt(index)
        this.value = oldValue
    } else {
        this.value = mutableListOf()
    }
}

调用该扩展功能以删除任何用户,并在删除一个用户后在Observer块中通知您。

viewModel.userList.removeItemAt(5) // Index 5

要从数据源获取userList时,只需调用viewModel.getUserList(),即可将数据获取到观察者块。

答案 5 :(得分:0)

private val usersLiveData = liveData(Dispatchers.IO) {
    val retrievedUsers = MyApplication.moodle.getEnrolledUsersCoroutine(course)
    repo.users = retrievedUsers
    roles.postValue(repo.findRolesByAll())
    emit(retrievedUsers)
}

init {
    usersMediator.addSource(usersLiveData){ usersMediator.value = it }
}

fun refreshUsers() {
    usersMediator.removeSource(usersLiveData)
    usersMediator.addSource(usersLiveData) { usersMediator.value = it }

liveData块{}中的命令不会再次执行。 好吧,是的,在拥有活动的viewmodel中的观察者被触发了,但是带有旧数据。 没有进一步的网络通话。

悲伤。很伤心与Channel和SwitchMap机制的其他建议相比,“解决方案”似乎很有希望,并且减少了重复性。