我可以在我的视图模型中创建实时数据观察器吗?还是我应该一直观察 fragment/activity?

can I make a livedata observer in my viewmodel? or should I always observer in fragment/activity?

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

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

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

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 需要检查用户是否未被禁止然后执行第二个请求(用户创建 post).我的问题是,我应该把这个逻辑放在哪里?在视图模型中还是在我的片段中?

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

从我看过的教程来看,好像livedata一直是在fragment中观察到的。所以第一个选择是将逻辑放在这样的片段中

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

        val userIsBanned = isBanned ?: return@Observer

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

    })

可以将这样的代码检查放在片段中吗?

其实不用观察isBanned,只要看一次就可以了

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

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

求助,java也可以

您可以尝试MediatorLiveData进行第二次手术。 MediatorLiveData 所做的是,它为您的各种 LiveData 对象创建一个可监听的容器,并在任何 underlying/observing 值更改时为您提供回调。

示例:假设 LiveData<B> 需要在 LiveData<A> 的任何值更改时调用,这里您可以将 LiveData<B> 视为 MediatorLiveData.

所以 LiveData<B> 的声明将是:

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

我们可以考虑三种方法,

  1. 每次尝试创建 post
  2. 时都必须获取 isBanned
  3. 你在 5 分钟内获取 isBanned 一次或一次(在 5 分钟缓存过期后)
  4. 你永远不会检查它,如果用户 isBanned,API 在尝试创建 post 时将 return 错误响应。 API 也 return 具体 model/http_code 所以你也可以理解用户 isBanned

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

方法二:

View/Fragment/Activity:

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

or the second option is to check userIsBanned or not in viewmodel, but I don't know how to do livedata observation in viewmodel

不要,文档说明不要在任何 ViewModel

中使用 observe

ViewModel objects are designed to outlive specific instantiations of views or LifecycleOwners. This design also means you can write tests to cover a ViewModel more easily as it doesn't know about view and Lifecycle objects. ViewModel objects can contain LifecycleObservers, such as LiveData objects. However ViewModel objects must never observe changes to lifecycle-aware observables, such as LiveData objects. If the ViewModel needs the Application context, for example to find a system service, it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor, since Application class extends Context.

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

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
    }
}
  • 确保在销毁 ViewModel 后删除 Observer: