The new LiveData
can be used as a replacement for RxJava's observables in some scenarios. However, unlike Observable
, LiveData
has no callback for errors.
My question is: How should I handle errors in LiveData
, e.g. when it's backed by some network resource that can fail to be retrieved due to an IOException
?
This function is deprecated. This extension method is not required when using Kotlin 1.4.
The RxJava's approach to choose a thread during the subscription, not in time of the sending is much more appropriate. Actually the only advantage of LiveData over RxJava we have noticed is they automatically subscribe and unsubscribe on Activity life cycle events (or over android components implementing life cycle).
MutableLiveData is commonly used since it provides the postValue() , setValue() methods publicly, something that LiveData class doesn't provide. LiveData/MutableLiveData is commonly used in updating data in a RecyclerView from a collection type(List, ArrayList etc).
In one of Google's sample apps for Android Architecture Components they wrap the LiveData emitted object in a class that can contain a status, data, and message for the emitted object.
https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/vo/Resource.kt
With this approach you can use the status to determine if there was an error.
You can extend from MutableLiveData
and create a holder Model to wrap your data.
This is your Wrapper Model
public class StateData<T> { @NonNull private DataStatus status; @Nullable private T data; @Nullable private Throwable error; public StateData() { this.status = DataStatus.CREATED; this.data = null; this.error = null; } public StateData<T> loading() { this.status = DataStatus.LOADING; this.data = null; this.error = null; return this; } public StateData<T> success(@NonNull T data) { this.status = DataStatus.SUCCESS; this.data = data; this.error = null; return this; } public StateData<T> error(@NonNull Throwable error) { this.status = DataStatus.ERROR; this.data = null; this.error = error; return this; } public StateData<T> complete() { this.status = DataStatus.COMPLETE; return this; } @NonNull public DataStatus getStatus() { return status; } @Nullable public T getData() { return data; } @Nullable public Throwable getError() { return error; } public enum DataStatus { CREATED, SUCCESS, ERROR, LOADING, COMPLETE } }
This is your extended LiveData Object
public class StateLiveData<T> extends MutableLiveData<StateData<T>> { /** * Use this to put the Data on a LOADING Status */ public void postLoading() { postValue(new StateData<T>().loading()); } /** * Use this to put the Data on a ERROR DataStatus * @param throwable the error to be handled */ public void postError(Throwable throwable) { postValue(new StateData<T>().error(throwable)); } /** * Use this to put the Data on a SUCCESS DataStatus * @param data */ public void postSuccess(T data) { postValue(new StateData<T>().success(data)); } /** * Use this to put the Data on a COMPLETE DataStatus */ public void postComplete() { postValue(new StateData<T>().complete()); } }
And this is how you use it
StateLiveData<List<Book>> bookListLiveData; bookListLiveData.postLoading(); bookListLiveData.postSuccess(books); bookListLiveData.postError(e);
And how it can be observed:
private void observeBooks() { viewModel.getBookList().observe(this, this::handleBooks); } private void handleBooks(@NonNull StateData<List<Book>> books) { switch (books.getStatus()) { case SUCCESS: List<Book> bookList = books.getData(); //TODO: Do something with your book data break; case ERROR: Throwable e = books.getError(); //TODO: Do something with your error break; case LOADING: //TODO: Do Loading stuff break; case COMPLETE: //TODO: Do complete stuff if necessary break; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With