【问题标题】:How to call again LiveData Coroutine Block如何再次调用 LiveData Coroutine Block
【发布时间】:2020-02-03 08:11:59
【问题描述】:

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

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

我有一个片段,我在其中观察我的 LiveData,一个带有 LiveData 和 Repository 的 ViewModel:

视图模型:

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

   val userData = usersRepo.getUser(userId)

   emit(userData) 
}

片段:

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

然后我试图实现这样的期望行为:

viewModel.deleteUser()

viewModel.getUserLiveData()

根据 LiveData 块下面的文档,如果它已成功完成,并且如果我在 LiveData 块中放置 while(true),则我的数据会刷新,但我不会想要这样做,因为我需要响应式更新我的视图。

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

也许我错过了如何重用相同的 LiveDataScope 来实现这一点?任何帮助将不胜感激。

【问题讨论】:

  • 你找到解决办法了吗
  • 很遗憾,没有。
  • 你的仓库叫什么?是房间数据库还是服务器调用?
  • @MojtabaHaddadi 它从服务器获取数据。
  • 祝你好运@JanasC12,我现在处于同样的位置。所有这些花哨的 api 都是白费的,哈哈

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


【解决方案1】:

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

MyViewModel() : ViewModel() {
   val commandsChannel = Channel<Command>()

   val liveData = livedata {
      commandsChannel.consumeEach { command -> 
            // you could have different kind of commands 
             //or emit just Unit to notify, that refresh is needed
           val newData = getSomeNewData()
           emit(newData)
       }
   }

  fun deleteUser() {
   .... // delete user
   commandsChannel.send(RefreshUsersListCommand)
  }
}

你应该问自己的问题:也许改用普通的 MutableLiveData 会更容易,自己改变它的值?

livedata { ... } 构建器运行良好,当您可以收集一些数据流(例如来自 Room DB 的 Flow / Flowable),而对于普通的非流源(您需要通过自己。

【讨论】:

    【解决方案2】:

    我找到了解决方案。我们可以使用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()
        }
    }
    
    
    

    【讨论】:

      【解决方案3】:

      首先将 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() 您将获取数据到观察者块。

      【讨论】:

        【解决方案4】:
        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 块 {} 中的命令不会再次执行。 好的,是的,视图模型中的观察者持有活动被触发,但使用旧数据。 没有进一步的网络调用。

        伤心。很伤心。与 Channel 和 SwitchMap 机制的其他建议相比,“解决方案”似乎很有希望,而且样板化程度较低。

        【讨论】:

        • 您的代码示例对我来说不清楚!但额外的描述是好的。如果您对您的代码进行更多解释,我会为您投票。角色之间的关系是什么? roles.postValue(repo.findRolesByAll()) 是什么?
        【解决方案5】:

        您可以为此使用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(),其余的应该照常工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-30
          • 2016-03-07
          相关资源
          最近更新 更多