LiveData
在某些情况下,新功能可以替代RxJava的可观察对象。但是,与不同Observable
,LiveData
它没有回调错误。
我的问题是:我应该如何处理中的错误LiveData
,例如当错误由某些网络资源支持时,由于出现错误而无法检索IOException
?
LiveData
在某些情况下,新功能可以替代RxJava的可观察对象。但是,与不同Observable
,LiveData
它没有回调错误。
我的问题是:我应该如何处理中的错误LiveData
,例如当错误由某些网络资源支持时,由于出现错误而无法检索IOException
?
Answers:
在Google用于Android体系结构组件的示例应用程序之一中,他们将LiveData发出的对象包装在一个类中,该类可以包含发出的对象的状态,数据和消息。
通过这种方法,您可以使用状态来确定是否存在错误。
您可以扩展MutableLiveData
并创建一个持有人模型来包装您的数据。
这是你的包装模型
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
}
}
这是您扩展的LiveData对象
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());
}
}
这就是你的使用方式
StateLiveData<List<Book>> bookListLiveData;
bookListLiveData.postLoading();
bookListLiveData.postSuccess(books);
bookListLiveData.postError(e);
以及如何观察它:
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;
}
}
LiveData
从StateLiveData
类
用某种错误消息包装从LiveData返回的数据
public class DataWrapper<T>T{
private T data;
private ErrorObject error; //or A message String, Or whatever
}
//现在在你的LifecycleRegistryOwner
班上
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
}
});
只是抓住一个Exception
代替或抛出它。使用错误的Object将此数据传递给UI。
MutableLiveData<DataWrapper<SomObject>> liveData = new...;
//On Exception catching:
liveData.set(new DataWrapper(null, new ErrorObject(e));
LiveData
转换为可观察的Observable<LiveData<Model>>
吗?那我们可以在那里处理错误吗?
另一种方法是使用MediatorLiveData
它将获取LiveData
不同类型的源。这将使您分隔每个事件:
例如:
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
}
}
在此示例中,LiveData
一旦MediatorLiveData
有活动的观察者,就会开始观察到装载和错误。
MediatorLiveData
将再次观察到它(在viewModel中仍然存在),问题是何时注册/观察到liveData将将上次发布到liveData的内容交付。如果最后一个发布是错误状态,则还原的活动将无法获取先前发布的数据,因此无法在终止该活动之前恢复UI体验。如何使用来处理os杀死/恢复活动MediatorLiveData
?
SingleLiveData
它将避免向新观察者传递最新结果。这是避免这种情况的一种方法。
data
(如果liveData中的最后发布内容位于state
的发布之后data
)。state
注册到liveData时,我们可以忽略liveData中的,但是如何获取数据以恢复以前的UI体验?如果我们有两个单独的liveData通道,一个通道用于data
一个通道,state
则不会出现此问题,那么如何将它们合并为一个liveData通道?
在我的应用程序中,我不得不将RxJava Observables转换为LiveData。这样做时,我当然必须保持错误状态。这是我的做法(科特琳)
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()
}
}
LiveDataReactiveStreams.fromPublisher()
如文档中所述,不处理错误。Rx错误将在主线程上引发错误并使应用程序崩溃。但是,您也可以将错误包装LiveDataResult
在Rx级别,然后用于LiveDataReactiveStreams.fromPublisher()
将其转换为LiveData。
只是克里斯·库克(Chris Cook)回答的方法的一些实现:
首先,我们需要包含响应数据和异常的对象:
/**
* A generic class that holds a value with its loading status.
*
* @see <a href="https://github.com/android/architecture-components-samples/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/vo/Resource.kt">Sample apps for Android Architecture Components</a>
*/
data class Resource<out T>(val status: Status, val data: T?, val exception: Throwable?) {
enum class Status {
LOADING,
SUCCESS,
ERROR,
}
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(exception: Throwable): Resource<T> {
return Resource(Status.ERROR, null, exception)
}
fun <T> loading(): Resource<T> {
return Resource(Status.LOADING, null, null)
}
}
}
然后是我自己的发明-AsyncExecutor。
这个小班做3件事:
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
class AsyncExecutor {
companion object {
fun <T> run(callback: () -> T): LiveData<Resource<T>> {
val resourceData: MutableLiveData<Resource<T>> = MutableLiveData()
Thread(Runnable {
try {
resourceData.postValue(Resource.loading())
val callResult: T = callback()
resourceData.postValue(Resource.success(callResult))
} catch (e: Throwable) {
resourceData.postValue(Resource.error(e))
}
}).start()
return resourceData
}
}
}
然后,您可以在ViewModel中创建一个LiveData,其中包含回调或异常的结果:
class GalleryViewModel : ViewModel() {
val myData: LiveData<Resource<MyData>>
init {
myData = AsyncExecutor.run {
// here you can do your synchronous operation and just throw any exceptions
return MyData()
}
}
}
然后,您可以在用户界面中获取数据和任何异常:
class GalleryFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
galleryViewModel = ViewModelProviders.of(this).get(GalleryViewModel::class.java)
// ...
// Subscribe to the data:
galleryViewModel.myData.observe(viewLifecycleOwner, Observer {
when {
it.status === Resource.Status.LOADING -> {
println("Data is loading...")
}
it.status === Resource.Status.ERROR -> {
it.exception!!.printStackTrace()
}
it.status === Resource.Status.SUCCESS -> {
println("Data has been received: " + it.data!!.someField)
}
}
})
return root
}
}
我在这里构建了一个电影搜索应用程序,其中我习惯了不同的LiveData
对象,一个用于网络的成功响应,另一个用于不成功的对象:
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)
}
})
}