githubbrowsersample - android architecture components example
¿Cómo manejar estados de error con LiveData? (5)
Ajustar los datos que devuelve de LiveData con algún tipo de mensaje de error
public class DataWrapper<T>T{
private T data;
private ErrorObject error; //or A message String, Or whatever
}
// Ahora en tu LifecycleRegistryOwner
Class
LiveData<DataWrapper<SomeObjectClass>> result = modelView.getResult();
result.observe(this, newData ->{
if(newData.error != null){ //Can also have a Status Enum
//Handle Error
}
else{
//Handle data
}
});
Simplemente capture una Exception
lugar o tírela. use el Objeto de error para pasar estos Datos a la IU.
MutableLiveData<DataWrapper<SomObject>> liveData = new...;
//On Exception catching:
liveData.set(new DataWrapper(null, new ErrorObject(e));
El nuevo LiveData
se puede usar como reemplazo de los observables de RxJava en algunos escenarios. Sin embargo, a diferencia de Observable
, LiveData
no tiene devolución de llamada por errores.
Mi pregunta es: ¿cómo debo manejar los errores en LiveData
, por ejemplo, cuando está respaldado por algún recurso de red que no se puede recuperar debido a una IOException
?
En mi aplicación, tuve que traducir RxJava Observables a LiveData. Mientras hacía eso, por supuesto tuve que mantener el estado de error. Así es como lo hice (Kotlin)
class LiveDataResult<T>(val data: T?, val error: Throwable?)
class LiveObservableData<T>(private val observable: Observable<T>) : LiveData<LiveDataResult<T>>() {
private var disposable = CompositeDisposable()
override fun onActive() {
super.onActive()
disposable.add(observable.subscribe({
postValue(LiveDataResult(it, null))
}, {
postValue(LiveDataResult(null, it))
}))
}
override fun onInactive() {
super.onInactive()
disposable.clear()
}
}
En una de las aplicaciones de muestra de Google para los componentes de arquitectura de Android envuelven el objeto emitido LiveData en una clase que puede contener un estado, datos y mensaje para el objeto emitido.
Con este enfoque, puede usar el estado para determinar si hubo un error.
He creado una aplicación de búsqueda de películas here en la que he usado diferentes objetos LiveData
, uno para la respuesta exitosa de la red y otra para los fracasados:
private val resultListObservable = MutableLiveData<List<String>>()
private val resultListErrorObservable = MutableLiveData<HttpException>()
fun findAddress(address: String) {
mainModel.fetchAddress(address)!!.subscribeOn(schedulersWrapper.io()).observeOn(schedulersWrapper.main()).subscribeWith(object : DisposableSingleObserver<List<MainModel.ResultEntity>?>() {
override fun onSuccess(t: List<MainModel.ResultEntity>) {
entityList = t
resultListObservable.postValue(fetchItemTextFrom(t))
}
override fun onError(e: Throwable) {
resultListErrorObservable.postValue(e as HttpException)
}
})
}
Otro enfoque es usar MediatorLiveData
que tomará las fuentes de LiveData
de diferente tipo. Esto te dará separación de cada evento:
Por ejemplo:
open class BaseViewModel : ViewModel() {
private val errorLiveData: MutableLiveData<Throwable> = MutableLiveData()
private val loadingStateLiveData: MutableLiveData<Int> = MutableLiveData()
lateinit var errorObserver: Observer<Throwable>
lateinit var loadingObserver: Observer<Int>
fun <T> fromPublisher(publisher: Publisher<T>): MediatorLiveData<T> {
val mainLiveData = MediatorLiveData<T>()
mainLiveData.addSource(errorLiveData, errorObserver)
mainLiveData.addSource(loadingStateLiveData, loadingObserver)
publisher.subscribe(object : Subscriber<T> {
override fun onSubscribe(s: Subscription) {
s.request(java.lang.Long.MAX_VALUE)
loadingStateLiveData.postValue(LoadingState.LOADING)
}
override fun onNext(t: T) {
mainLiveData.postValue(t)
}
override fun onError(t: Throwable) {
errorLiveData.postValue(t)
}
override fun onComplete() {
loadingStateLiveData.postValue(LoadingState.NOT_LOADING)
}
})
return mainLiveData
}
}
En este ejemplo, la carga y el error de LiveData
comenzarán a observarse una vez que MediatorLiveData
tenga observadores activos.