【问题标题】:can I make a livedata observer in my viewmodel? or should I always observer in fragment/activity?我可以在我的视图模型中创建一个 livedata 观察者吗?还是我应该始终观察片段/活动?
【发布时间】:2020-03-21 03:17:20
【问题描述】:

我是 MVVM 的新手。所以我的片段/活动向服务器发出了 2 个请求,第一个请求的结果将用作第二个请求的输入参数。

所以首先在我的片段中,当一个按钮被点击时,我会请求检查用户是否被禁止,如果没有,那么这个用户可以创建一个帖子。

首先我检查用户是否被禁止在我的片段中使用此代码

class CreateEventFragment : Fragment() {

    lateinit var model: CreateEventViewModel


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        model = ViewModelProvider(this).get(CreateEventViewModel::class.java)

        button.setOnClickListener {
            model.checkIfUserIsBanned()
        }

    }


}

这是视图模型

class CreateEventViewModel(application: Application) : AndroidViewModel(application) {

    val mUserIsBanned :MutableLiveData<Boolean> = UserClient.mUserIsBanned

    fun checkIfUserIsBanned(userID: String) {
        UserRepository.checkIfUserIsBanned(id)
    }


}

这里是客户端(为简单起见,我跳过了存储库)

object UserClient {

    val mUserIsBanned = MutableLiveData<Boolean>()

    fun checkIfUserIsBanned(userID: String) {

        // perform networking, after getting the value then

        if (user.isBanned) {
            mUserIsBanned.postValue(true)
        } else {
            mUserIsBanned.postValue(false)
        }

    }



}

问题来了,第二个请求需要第一个结果的结果,即mUserIsBanned需要检查用户是否被禁止然后执行第二个请求(用户创建帖子)。我的问题是,我在哪里放置这个逻辑?在视图模型中还是在我的片段中?

if (userIsBanned == false) {
   createPost()
}

从我看到的教程中,似乎总是在片段中观察到实时数据。所以第一个选择是将逻辑放在这样的片段中

    model.mUserIsBanned.observe(viewLifecycleOwner, Observer { isBanned ->

        val userIsBanned = isBanned ?: return@Observer

        if (!userIsBanned) {
            model.createPost()
        }

    })

可以在片段中进行这样的代码检查吗?

其实我不需要观察isBanned,我只需要检查一次

或者第二个选项是在 viewmodel 中检查 userIsBanned 与否,但我不知道如何在 viewmodel 中进行 livedata 观察

还是我的方法全错了?我不确定使用这个 MVVM

请帮忙,java也可以。

【问题讨论】:

  • 我的意见是,检查用户是否被禁止是一次性的事情,它可能不会随时发生,所以这使它成为实时数据没有用,您可以直接将其用作普通布尔值并调用创建一个基于它的帖子。
  • 是的,你是对的 Akshay,这只是一个单一的现场活动,你能显示代码如何实现吗?我很困惑
  • 当然,你能解释一下你为什么使用客户端吗?是因为您想将所有 API 调用与视图模型分开吗?检查here
  • @AkshayNandwana 是的,将其与 viewModel 分开
  • @AkshayNandwana 可以将 API 调用放在 ViewModel 中吗?我是 MVVM 的新手,我只是按照我观看的教程通过在客户端中单独调用 API 来完成

标签: android kotlin android-livedata android-architecture-components android-viewmodel


【解决方案1】:

您可以尝试MediatorLiveData 进行第二次操作。 MediatorLiveData 所做的是,它为您的各种 LiveData 对象创建一个可侦听的容器,并在任何基础/观察值发生变化时为您提供回调。

示例:假设LiveData&lt;B&gt;LiveData&lt;A&gt;的任何值变化都需要调用,这里可以将LiveData&lt;B&gt;视为MediatorLiveData

所以LiveData&lt;B&gt; 的声明将是:

val bLiveData : LiveData<B> = MediatorLiveData<B>().apply {
    addSource(aLiveData) { aData ->
        value = convertADataToB(aData) //value is backing property for getValue()/setValue() method, use postValue() explicitly upon bg operation
    }
}

在您的情况下,将此代码放入您的ViewModel

val createPostLiveData: LiveData<Boolean> = MediatorLiveData<Boolean>().apply {
    addSource(mUserIsBanned) { flag ->
        if (!flag) {
            createPost() // Check whether you can return result from here and provide to this mediator livedata a value
        }
    }
}

参考MediatorLiveData

【讨论】:

  • 据我所知,如果未观察到(例如在片段中),将不会触发 LiveData(中介或转换)。如果未观察到 createPostLiveData ,是否会触发 addSource 内的块? cmiiw, stackoverflow.com/questions/60628371/…
  • 是的,没错,但您不应该在 UI 上观察 createPostLiveData 以提供一些有意义的错误/成功消息或 UI 更新吗?
【解决方案2】:

2021 年可行的解决方案:

要在ViewModel 中观察LiveData,请使用observeForever(observer)

class FirstViewModel(application: Application) : AndroidViewModel(application) {
    val country = MutableLiveData<String>()

    private val countryObserver = Observer<String> { country ->     //for removing it later
        //do your stuff
    }

    init {
        country.value = "xxx"

        country.observeForever(countryObserver)     //key point
    }

    override fun onCleared() {
        super.onCleared()

        country.removeObserver(countryObserver)     //【must remove!!】 See below
    }
}

【讨论】:

    【解决方案3】:

    我们可以考虑三种方法,

    1. 每次尝试创建帖子时都必须获取 isBanned
    2. 您在 5 分钟内获取一次或一次 isBanned(在 5 分钟缓存过期后)
    3. 您永远不会检查它,如果用户isBanned 尝试创建帖子,API 将返回错误响应。 API 还返回特定的模型/http_code,因此您也可以了解用户 isBanned

    方法 1 可以,如果 isBanned 也用于其他地方并且您将其存储在本地(直到下一个应用程序打开或一段时间),则方法 2 可以。方法 3 必须始终存在,isBanned 也必须始终由服务器检查。

    方法2

    视图/片段/活动:

    // has no idea about detail; isBanned check or other kinds of validations
    // new type of validations can be added without modifying this code (max post characters)
    observeCreatePost()
    viewModel.createPost(params)
    

    视图模型:

    // does not know about validations, checks. But if you create Post only using this viewModel, then validation can be here as well
    
    val createPostState = MutableLiveData<Result<Boolean>>()
    
    fun createPost(params:String){
       createPostState.postValue(UIResult.Loading)
    
       createPostUseCase(params)
         // or .subscribe()
         .observe{ result->
            // ideally you convert Exceptions to (String) representation here
            createPostState.postValue(result.data)
         }
    }
    

    创建后用例:

    operator fun invoke(params:String):Result<Boolean>{
        // validations are here, multiple ViewModels can use this UseCase
        // validate if params are valid, if not return Result.Error()
    
        // still does not know if userBanned comes from local data or API
        if(repository.isUserBanned()){
           return Result.Error()
        }else{
           return repository.createPost(params)
        }
    }
    

    后存储库:

    fun isUserBanned():Boolean{
       if(userBanned exist locally and not expired)
           return userBanned locally
       else
           isUserBanned = api.isUserBanned()
           storeLocal(isUserBanned)
           return isUserBanned
    }
    
    fun createPost(params):Result<Boolean>{
       response = api.createPost(params)
       // return Result wrapped response 
    }
    

    【讨论】:

      【解决方案4】:

      或者第二个选项是在 viewmodel 中检查 userIsBanned 与否,但我不知道如何在 viewmodel 中进行 livedata 观察

      不要,文档声明不要在任何 ViewModel 中使用 observe

      ViewModel 对象旨在比视图或 LifecycleOwners 的特定实例化更长寿。这种设计还意味着您可以更轻松地编写测试来覆盖 ViewModel,因为它不了解视图和生命周期对象。 ViewModel 对象可以包含 LifecycleObservers,例如 LiveData 对象。 但是,ViewModel 对象绝不能观察生命周期感知可观察对象的更改,例如 LiveData 对象。如果 ViewModel 需要 Application 上下文,例如查找系统服务,它可以扩展 AndroidViewModel 类,并在构造函数中有一个接收 Application 的构造函数,因为 Application 类扩展了 Context。

      https://developer.android.com/topic/libraries/architecture/viewmodel

      【讨论】:

        猜你喜欢
        • 2021-09-25
        • 1970-01-01
        • 1970-01-01
        • 2019-05-25
        • 1970-01-01
        • 1970-01-01
        • 2011-06-17
        • 1970-01-01
        • 2011-01-07
        相关资源
        最近更新 更多