MutableLiveData的状态。仅http调用发生,并且值显示在日志中,没有观察到与LiveData状态相对应的操作
我想调用Http方法,该方法的逻辑写在LoginViewModel中,该方法是BaseViewModel的子类。在BaseViewModel中,我创建了一些以MutableLiveData作为参数的通用方法,在LoginViewModel的方法中调用这些方法,并在Fragment中观察这些LiveData
UiState.kt
sealed class UiState<T> {
data class Progress<T>(val isLoading: Boolean) : UiState<T>()
data class Success<T>(val successInfo: T) : UiState<T>()
data class Failure<T>(val throwable: Throwable) : UiState<T>()
data class Alert<T>(val alert: String) : UiState<T>()
companion object {
fun <T> loading(isLoading: Boolean): UiState<T> = Progress(isLoading)
fun <T> success(successInfo: T): UiState<T>? = Success(successInfo)
fun <T> failure(throwable: Throwable): UiState<T> = Failure(throwable)
fun <T> alert(alert: String): UiState<T> = Alert(alert)
}
}
Event.kt
open class Event<out T>(private val content: T) {
private var hasBeenHandled = false
fun getContentIfNotHandled() = if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
fun peekContent() = content
}
BaseViewModel.kt
fun <T> onSuccessHttpResponse(state: MutableLiveData<Event<UiState<T>>>) = Consumer<Response<T>> {
state.value = Event(loading(true))
if (it.isSuccessful) {
state.value = Event(loading(false))
state.value = Event(success(it.body()!!)!!)
} else {
val error = Gson().fromJson(it.errorBody()?.charStream(), ApiError::class.java)
when (it.code()) {
Constants.ACCESS_TOKEN_REFRESH_STATUS_CODE -> state.value = Event(alert("Renew Access Token please"))
Constants.CUSTOM_STATUS_CODE -> state.value = Event(alert(error.message!!))
else -> state.value = Event(alert("Something went wrong"))
}
state.value = Event(loading(false))
}
}
fun <T> onErrorHttpResponse(state: MutableLiveData<Event<UiState<T>>>) = Consumer<Throwable> {
state.value = Event(loading(false))
state.value = Event(UiState.failure(it))
}
fun <T> inputNotFoundError(state: MutableLiveData<Event<UiState<T>>>) {
state.value = Event(loading(false))
state.value = Event(alert("Please Filled all Info"))
}
LoginViewModel.kt
val tutorLoginState: MutableLiveData<Event<UiState<TutorLoginResponse>>> = MutableLiveData()
fun tutorLogin(loginInfo: LoginInfo) {
if (loginInfo.isAssigned()) {
callLoginTutorApi(loginInfo)
} else {
inputNotFoundError(tutorLoginState)
}
}
private fun callLoginTutorApi(loginInfo: LoginInfo) {
compositeDisposable += userLoginService.tutorLogin(loginInfo)
.performOnBackgroundOutputOnMain()
.subscribe({
onSuccessHttpResponse(tutorLoginState)
}, {
onErrorHttpResponse(tutorLoginState)
})
}
LoginFragment.kt
override fun observeLiveData() {
viewModel.tutorLoginState.observe(this, Observer {
it.getContentIfNotHandled()?.let { state ->
when (state) {
is UiState.Progress -> {
if (state.isLoading) {
network_loading_indicator.visible()
} else {
network_loading_indicator.visibilityGone()
}
}
is UiState.Success -> {
val responseData: TutorInfo = state.successInfo.data?.tutorInfo!!
context?.showToast(responseData.tutorName.toString())
}
is UiState.Alert -> context?.showToast(state.alert)
is UiState.Failure -> {
if (state.throwable is IOException) {
context?.showToast("Internet Connection Failed")
} else {
context?.showToast("Json Parsing Error")
}
}
}
}
})
仅Http调用发生。但是对LiveData的更改没有任何反应
答案 0 :(得分:1)
根据先前的对话,您可以像这样在BaseViewModel中处理一次性对象,并创建LiveData值loader
以在基础内部集中处理它,并在每个API调用中重用相同的单个实时事件和message
以显示Toast中的错误或此类错误:
BaseViewModel
abstract class BaseViewModel : ViewModel() {
protected val compositeDisposable = CompositeDisposable()
val loader: MutableLiveData<Boolean> by lazy { SingleLiveEvent<Boolean>() }
val message: MutableLiveData<Message> by lazy { SingleLiveEvent<Message>() }
override fun onCleared() {
compositeDisposable.clear()
super.onCleared()
}
}
在ViewModel中,为响应保留一个LiveData并将其一次性使用。只需在此处切换loader
值,就可以从Fragment / Activity中观察到它,并通过使用doOnSubscribe()
和doOnTerminate()
来切换加载程序的可见性,如下所示(使用RxJava的详细说明)动作操作符可以找到here)。基本上可以总结一下:
doOnSubscribe()
-修改源,以便在从其订阅者订阅时调用给定操作。doOnTerminate()
—在此Observable发出onError或onCompleted信号之前调用指定的操作。 LoginViewModel
private lateinit var disposableResponse: Disposable
val tutorLoginResponse = MutableLiveData<TutorLoginResponse>()
fun login() {
disposableResponse = userLoginService.tutorLogin()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { loader.value = true }
.doOnTerminate { loader.value = false }
.subscribe({
onRetrieveResponseSuccess(it)
}, {
onRetrieveResponseError(it)
})
compositeDisposable.add(disposableResponse)
}
private fun onRetrievePostListSuccess(response: TutorLoginResponse) {
tutorLoginResponse.value = response
}
private fun onRetrievePostListError(error: Throwable) {
message.value = ToastMessage(error.message) //ToastMessage is a simple utility class to show Toast
}
然后从您的视图模型中观察loader
的更新LiveData值,并从“活动/片段”中切换UI中加载程序的可见性,并访问响应如下:
LoginFragment
viewModel.loader.observe(this, Observer {
if(it) showLoader() //showLoader() is a simple method in BaseFragment which invokes .show() on your deafult or custom Lottie animated loader
else hideLoader() //hideLoader() is a similar method for invoking .hide() on your loader
})
viewModel.tutorLoginResponse.observe(this, Observer { response ->
//do whatever with your response that's been returned here
})