问题
I am new to MVVM. so I have 2 requests to the server from my fragment/activity, the result from the first request will be used as an input parameter for the second request.
so first in my fragment, when a button is clicked then I make a request to check whether the user is banned or not, if not then this user can create a post.
so first I check if a user is banned or not using this code in my fragment
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()
}
}
}
and here is the viewmodel
class CreateEventViewModel(application: Application) : AndroidViewModel(application) {
val mUserIsBanned :MutableLiveData<Boolean> = UserClient.mUserIsBanned
fun checkIfUserIsBanned(userID: String) {
UserRepository.checkIfUserIsBanned(id)
}
}
and here is the client ( I skip the repository for simplicity)
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)
}
}
}
here is the problem, the second request needs the result of the first result, i.e the mUserIsBanned
is need to check if the user is not banned then perform the second request (user create a post). my question is, where do I place this logic ? in viewmodel or in my fragment?
if (userIsBanned == false) {
createPost()
}
from the tutorial I have seen, it seems the livedata is always observed in a fragment. so the first option is to place the logic in fragment like this
model.mUserIsBanned.observe(viewLifecycleOwner, Observer { isBanned ->
val userIsBanned = isBanned ?: return@Observer
if (!userIsBanned) {
model.createPost()
}
})
is it okay to place code checking like that in a fragment?
actually I don't need to observed the isBanned, I just need to check it once
or the second option is to check userIsBanned or not in viewmodel, but I don't know how to do livedata observation in viewmodel
or my approach is all wrong ? I am not sure using this MVVM
please help, java is also ok.
回答1:
You can try MediatorLiveData
for your second operation. What MediatorLiveData
does is, it creates a listenable container for your various LiveData
objects & provide you callback once any of the underlying/observing value changes.
Example: Assume that LiveData<B>
needs to be called on any value changes from LiveData<A>
, here you can consider LiveData<B>
as MediatorLiveData
.
So declaration for LiveData<B>
would be:
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
}
}
In your case, put this code inside your 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
}
}
}
Refer MediatorLiveData
回答2:
We can think about three approaches,
- you have to fetch
isBanned
every time you try to create a post - you fetch isBanned once or once in 5min (after 5min cache expires)
- you never check it, API will return error response when trying to create a post if user
isBanned
. API also returns specific model/http_code so you can also understand that userisBanned
Approach 1 is not OK, approach 2 is OK if isBanned
is used in other places as well and if you store it locally (either until next app open or some period of time). Approach 3 has to be there always, isBanned
must always be checked by server as well.
Approach 2:
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)
ViewModel:
// 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)
}
}
CreatePostUseCase:
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)
}
}
PostRepository:
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
}
来源:https://stackoverflow.com/questions/60784137/can-i-make-a-livedata-observer-in-my-viewmodel-or-should-i-always-observer-in-f