Android 架构组件改造和RxJava 2错误处理
我目前正试图在架构组件中实现新的ViewModels,使用来自Reformation和Okhttp的API请求,一切都正常,但我不知道如何将错误响应从Reformation传递到Android 架构组件改造和RxJava 2错误处理,android,retrofit2,rx-java2,android-architecture-components,Android,Retrofit2,Rx Java2,Android Architecture Components,我目前正试图在架构组件中实现新的ViewModels,使用来自Reformation和Okhttp的API请求,一切都正常,但我不知道如何将错误响应从Reformation传递到LiveDataReactiveStreams.fromPublisher,然后在片段中向上游传递给观察者。这就是我到目前为止所做的: public class ShowListViewModel extends AndroidViewModel { private final ClientAdapter client
LiveDataReactiveStreams.fromPublisher
,然后在片段中向上游传递给观察者。这就是我到目前为止所做的:
public class ShowListViewModel extends AndroidViewModel {
private final ClientAdapter clientAdapter;
private LiveData<List<Show>> shows;
public ShowListViewModel(Application application) {
super(application);
clientAdapter = new ClientAdapter(getApplication().getApplicationContext());
loadShows();
}
public LiveData<List<Show>> getShows() {
if (shows == null) {
shows = new MutableLiveData<>();
}
return shows;
}
void loadShows() {
shows = LiveDataReactiveStreams.fromPublisher(Observable.fromIterable(ShowsUtil.loadsIds())
.subscribeOn(Schedulers.io())
.flatMap(clientAdapter::getShowWithNextEpisode)
.observeOn(Schedulers.computation())
.toSortedList(new ShowsUtil.ShowComparator())
.observeOn(AndroidSchedulers.mainThread())
.toFlowable());
}
一切都按预期工作,但目前如果我们处于脱机状态,则改型会引发runtimeException并崩溃。我认为问题在于:
LiveDataReactiveStreams.fromPublisher(Observable.fromIterable(ShowsUtil.loadsIds())
.subscribeOn(Schedulers.io())
.flatMap(clientAdapter::getShowWithNextEpisode)
.observeOn(Schedulers.computation())
.toSortedList(new ShowsUtil.ShowComparator())
.observeOn(AndroidSchedulers.mainThread())
.toFlowable());
}
通常我们会使用rxjava2 subscribe并捕获那里的改型错误,但当使用它时,会为我们订阅flowable。那么我们如何将这个错误传递到这里:
model.getShows().observe(这显示了->{//processerror in fragment})
我使用了一个解决方案,该解决方案使用了LiveDataReactiveStreams.fromPublisher
以及一个基于以下类的通用包装类结果
这个想法类似于
无论使用LiveData,都可以使用此模式,其主要思想是能够在不结束流的情况下处理错误。因此,它的工作原理与使用onNext
发出错误的情况类似,实际含义来自数据,无论结果是否存在
如果这将是您的项目主干的一部分,您可以创建一些额外的框架,使所有这些都透明。例如,您可以创建自己的类,该类类似于资源
感知流,它有一个观察
方法,模仿您调用RxJava的观察
的方式,但“展开”数据并调用正确的回调:
fun subscribe(
onNext: (T) -> Unit,
onError: ((Throwable) -> Unit)? = null
) {
val handleOnNext: (Resource<T>) -> Unit = { resource: Resource<T> ->
when (resource.status) {
Status.SUCCESS -> resource.data?.let(onNext)
Status.ERROR -> resource.error?.let { onError?.invoke(it) ?: throw it }
}
}
publishSubject
.subscribeOn(subscribeOn)
.observeOn(observeOn)
.run { subscribe(handleOnNext) }
.addTo(compositeDisposable)
}
fun订阅(
onNext:(T)->单位,
onError:((可丢弃)->单位)?=null
) {
val handleOnNext:(资源)->Unit={Resource:Resource->
何时(资源状态){
Status.SUCCESS->resource.data?.let(onNext)
Status.ERROR->resource.ERROR?.let{onError?.invoke(it)?:throw it}
}
}
出版科目
.subscribeOn(subscribeOn)
.observeOn(observeOn)
.run{subscribe(handleonext)}
.addTo(可合成)
}
与通过LiveData对象仅公开节目列表不同,您需要将节目和错误包装到一个可以保存错误的类中
根据您的示例,您可以执行以下操作:
LiveDataReactiveStreams.fromPublisher(Observable.fromIterable(ShowsUtil.loadsIds())
.subscribeOn(Schedulers.io())
.flatMap(clientAdapter::getShowWithNextEpisode)
.observeOn(Schedulers.computation())
.toSortedList(new ShowsUtil.ShowComparator())
.observeOn(AndroidSchedulers.mainThread())
.map(Result::success)
.onErrorReturn(Result::error)
.toFlowable());
其中Result是保存错误或结果的包装器类
final class Result<T> {
private final T result;
private final Throwable error;
private Result(@Nullable T result, @Nullable Throwable error) {
this.result = result;
this.error = error;
}
@NonNull
public static <T> Result<T> success(@NonNull T result) {
return new Result(result, null);
}
@NonNull
public static <T> Result<T> error(@NonNull Throwable error) {
return new Result(null, error);
}
@Nullable
public T getResult() {
return result;
}
@Nullable
public Throwable getError() {
return error;
}
}
最终课程结果{
私人最终测试结果;
私人最终可丢弃错误;
私有结果(@Nullable T Result,@Nullable Throwable error){
this.result=结果;
this.error=错误;
}
@非空
公共静态结果成功(@NonNull T Result){
返回新结果(结果,空);
}
@非空
公共静态结果错误(@NonNull Throwable error){
返回新结果(空,错误);
}
@可空
公共T getResult(){
返回结果;
}
@可空
公共可丢弃的getError(){
返回误差;
}
}
在我的例子中,我将LiveDataReactiveStreams.java作为“MyLiveDataReactiveStreams.java”复制到我的“util”包中。修改后的版本将运行时异常替换为GreenRobot EventBus post。然后在我的应用程序中,我可以订阅该事件并适当地处理错误。对于这个解决方案,我必须在3个位置添加'@SuppressLint(“RestrictedApi”)'。我不确定谷歌是否允许play store应用程序使用这一功能。本回购协议包含一个完整的示例:
以下是相关代码:
// add GreenRobot to your app/build.gradle
def greenrobot_version = '3.2.0'
def timberkt_version = '1.5.1'
implementation "org.greenrobot:eventbus:$greenrobot_version"
implementation "com.github.ajalt:timberkt:$timberkt_version"
//--------------------------------------------------
// next put this in your Activity or Fragment to handle the error
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onRxErrorEvent(rxError_event: RxErrorEvent) {
// do your custom error handling here
Toast.makeText(activity, rxError_event.errorDescription, Toast.LENGTH_LONG).show()
}
//--------------------------------------------------
// create a new class in 'util' to post the error
import com.github.ajalt.timberkt.Timber
import org.greenrobot.eventbus.EventBus
class RxErrorEvent(val description: String) {
private val _tag = "LEE: <" + RxErrorEvent::class.java.simpleName + ">"
lateinit var errorDescription: String
init {
errorDescription = description
}
fun post() {
Timber.tag(_tag).e("post $errorDescription")
EventBus.getDefault().post(this)
}
}
//--------------------------------------------------
// and use MyLiveDataReactiveStreams (below) instead of LiveDataReactiveStreams
/*
* This is a modified version of androidx.lifecycle.LiveDataReactiveStreams
* The original LiveDataReactiveStreams object can't handle RxJava error conditions.
* Now errors are emitted as RxErrorEvent objects using the GreenRobot EventBus.
*/
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import com.github.ajalt.timberkt.Timber;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.util.concurrent.atomic.AtomicReference;
/**
* Adapts {@link LiveData} input and output to the ReactiveStreams spec.
*/
@SuppressWarnings("WeakerAccess")
public final class MyLiveDataReactiveStreams {
private final static String _tag = "LEE: <" + MyLiveDataReactiveStreams.class.getSimpleName() + ">";
private MyLiveDataReactiveStreams() {
}
/**
* Adapts the given {@link LiveData} stream to a ReactiveStreams {@link Publisher}.
*
* <p>
* By using a good publisher implementation such as RxJava 2.x Flowables, most consumers will
* be able to let the library deal with backpressure using operators and not need to worry about
* ever manually calling {@link Subscription#request}.
*
* <p>
* On subscription to the publisher, the observer will attach to the given {@link LiveData}.
* Once {@link Subscription#request} is called on the subscription object, an observer will be
* connected to the data stream. Calling request(Long.MAX_VALUE) is equivalent to creating an
* unbounded stream with no backpressure. If request with a finite count reaches 0, the observer
* will buffer the latest item and emit it to the subscriber when data is again requested. Any
* other items emitted during the time there was no backpressure requested will be dropped.
*/
@NonNull
public static <T> Publisher<T> toPublisher(
@NonNull LifecycleOwner lifecycle, @NonNull LiveData<T> liveData) {
return new MyLiveDataReactiveStreams.LiveDataPublisher<>(lifecycle, liveData);
}
private static final class LiveDataPublisher<T> implements Publisher<T> {
final LifecycleOwner mLifecycle;
final LiveData<T> mLiveData;
LiveDataPublisher(LifecycleOwner lifecycle, LiveData<T> liveData) {
this.mLifecycle = lifecycle;
this.mLiveData = liveData;
}
@Override
public void subscribe(Subscriber<? super T> subscriber) {
subscriber.onSubscribe(new MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription<T>(subscriber, mLifecycle, mLiveData));
}
static final class LiveDataSubscription<T> implements Subscription, Observer<T> {
final Subscriber<? super T> mSubscriber;
final LifecycleOwner mLifecycle;
final LiveData<T> mLiveData;
volatile boolean mCanceled;
// used on main thread only
boolean mObserving;
long mRequested;
// used on main thread only
@Nullable
T mLatest;
LiveDataSubscription(final Subscriber<? super T> subscriber,
final LifecycleOwner lifecycle, final LiveData<T> liveData) {
this.mSubscriber = subscriber;
this.mLifecycle = lifecycle;
this.mLiveData = liveData;
}
@Override
public void onChanged(@Nullable T t) {
if (mCanceled) {
return;
}
if (mRequested > 0) {
mLatest = null;
mSubscriber.onNext(t);
if (mRequested != Long.MAX_VALUE) {
mRequested--;
}
} else {
mLatest = t;
}
}
@SuppressLint("RestrictedApi")
@Override
public void request(final long n) {
if (mCanceled) {
return;
}
ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
if (mCanceled) {
return;
}
if (n <= 0L) {
mCanceled = true;
if (mObserving) {
mLiveData.removeObserver(MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription.this);
mObserving = false;
}
mLatest = null;
mSubscriber.onError(
new IllegalArgumentException("Non-positive request"));
return;
}
// Prevent overflowage.
mRequested = mRequested + n >= mRequested
? mRequested + n : Long.MAX_VALUE;
if (!mObserving) {
mObserving = true;
mLiveData.observe(mLifecycle, MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription.this);
} else if (mLatest != null) {
onChanged(mLatest);
mLatest = null;
}
}
});
}
@SuppressLint("RestrictedApi")
@Override
public void cancel() {
if (mCanceled) {
return;
}
mCanceled = true;
ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
if (mObserving) {
mLiveData.removeObserver(MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription.this);
mObserving = false;
}
mLatest = null;
}
});
}
}
}
/**
* Creates an observable {@link LiveData} stream from a ReactiveStreams {@link Publisher}}.
*
* <p>
* When the LiveData becomes active, it subscribes to the emissions from the Publisher.
*
* <p>
* When the LiveData becomes inactive, the subscription is cleared.
* LiveData holds the last value emitted by the Publisher when the LiveData was active.
* <p>
* Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
* added, it will automatically notify with the last value held in LiveData,
* which might not be the last value emitted by the Publisher.
* <p>
* Note that LiveData does NOT handle errors and it expects that errors are treated as states
* in the data that's held. In case of an error being emitted by the publisher, an error will
* be propagated to the main thread and the app will crash.
*
* @param <T> The type of data hold by this instance.
*/
@NonNull
public static <T> LiveData<T> fromPublisher(@NonNull Publisher<T> publisher) {
return new MyLiveDataReactiveStreams.PublisherLiveData<>(publisher);
}
/**
* Defines a {@link LiveData} object that wraps a {@link Publisher}.
*
* <p>
* When the LiveData becomes active, it subscribes to the emissions from the Publisher.
*
* <p>
* When the LiveData becomes inactive, the subscription is cleared.
* LiveData holds the last value emitted by the Publisher when the LiveData was active.
* <p>
* Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
* added, it will automatically notify with the last value held in LiveData,
* which might not be the last value emitted by the Publisher.
*
* <p>
* Note that LiveData does NOT handle errors and it expects that errors are treated as states
* in the data that's held. In case of an error being emitted by the publisher, an error will
* be propagated to the main thread and the app will crash.
*
* @param <T> The type of data hold by this instance.
*/
private static class PublisherLiveData<T> extends LiveData<T> {
private final Publisher<T> mPublisher;
final AtomicReference<MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber> mSubscriber;
PublisherLiveData(@NonNull Publisher<T> publisher) {
mPublisher = publisher;
mSubscriber = new AtomicReference<>();
}
@Override
protected void onActive() {
super.onActive();
MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber s = new MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber();
mSubscriber.set(s);
mPublisher.subscribe(s);
}
@Override
protected void onInactive() {
super.onInactive();
MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber s = mSubscriber.getAndSet(null);
if (s != null) {
s.cancelSubscription();
}
}
final class LiveDataSubscriber extends AtomicReference<Subscription>
implements Subscriber<T> {
@Override
public void onSubscribe(Subscription s) {
if (compareAndSet(null, s)) {
s.request(Long.MAX_VALUE);
} else {
s.cancel();
}
}
@Override
public void onNext(T item) {
postValue(item);
}
@SuppressLint("RestrictedApi")
@Override
public void onError(final Throwable ex) {
mSubscriber.compareAndSet(this, null);
ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
//NOTE: Errors are be handled upstream
Timber.tag(_tag).e("LiveData does not handle errors. Errors from publishers are handled upstream via EventBus. error: " + ex);
RxErrorEvent rx_event = new RxErrorEvent(ex.toString());
rx_event.post();
}
});
}
@Override
public void onComplete() {
mSubscriber.compareAndSet(this, null);
}
public void cancelSubscription() {
Subscription s = get();
if (s != null) {
s.cancel();
}
}
}
}
}
//将GreenRobot添加到您的应用程序/build.gradle
def greenrobot_版本='3.2.0'
def timberkt_版本='1.5.1'
实现“org.greenrobot:eventbus:$greenrobot\u版本”
实现“com.github.ajalt:timberkt:$timberkt\u版本”
//--------------------------------------------------
//接下来,将其放入活动或片段中以处理错误
覆盖有趣的onStart(){
super.onStart()
EventBus.getDefault()寄存器(此)
}
覆盖桌面上的乐趣(){
super.onStop()
EventBus.getDefault().unregister(此);
}
@订阅(threadMode=threadMode.MAIN)
有趣的RXerorEvent(RXeror_事件:RXerorEvent){
//请在此处执行自定义错误处理
Toast.makeText(活动,rxError\u event.errorDescription,Toast.LENGTH\u LONG.show())
}
//--------------------------------------------------
//在“util”中创建新类以发布错误
导入com.github.ajalt.timberkt.Timber
导入org.greenrobot.eventbus.eventbus
类RxErrorEvent(val说明:字符串){
private val_tag=“LEE:”
lateinit变量errorDescription:字符串
初始化{
errorDescription=描述
}
趣味帖({
Timber.tag(_tag.e)(“post$errorDescription”)
EventBus.getDefault().post(此)
}
}
//--------------------------------------------------
//并使用MyLiveDataReactiveStreams(如下)代替LiveDataReactiveStreams
/*
*这是androidx.lifecycle.LiveDataReactiveStreams的修改版本
*原始LiveDataReactiveStreams对象无法处理RxJava错误条件。
*现在,使用GreenRobot事件总线将错误作为RXerorEvent对象发出。
*/
导入android.annotation.SuppressLint;
导入androidx.annotation.NonNull;
导入androidx.annotation.Nullable;
导入androidx.arch.core.executor.ArchTaskExecutor;
导入androidx.lifecycle.LifecycleOwner;
导入androidx.lifecycle.LiveData;
导入androidx.lifecycle.Observer;
导入com.github.ajalt.timberkt.Timber;
导入org.reactivestreams.Publisher;
导入org.reactivestreams.Subscriber;
导入org.reactivestreams.Subscription;
导入java.util.concurrent.AtomicReference;
/**
*将{@link LiveData}输入和输出适配到ReactiveStreams规范。
*/
@抑制警告(“弱化访问”)
公共最终类MyLiveDataReactiveStreams{
普里夫
// add GreenRobot to your app/build.gradle
def greenrobot_version = '3.2.0'
def timberkt_version = '1.5.1'
implementation "org.greenrobot:eventbus:$greenrobot_version"
implementation "com.github.ajalt:timberkt:$timberkt_version"
//--------------------------------------------------
// next put this in your Activity or Fragment to handle the error
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onRxErrorEvent(rxError_event: RxErrorEvent) {
// do your custom error handling here
Toast.makeText(activity, rxError_event.errorDescription, Toast.LENGTH_LONG).show()
}
//--------------------------------------------------
// create a new class in 'util' to post the error
import com.github.ajalt.timberkt.Timber
import org.greenrobot.eventbus.EventBus
class RxErrorEvent(val description: String) {
private val _tag = "LEE: <" + RxErrorEvent::class.java.simpleName + ">"
lateinit var errorDescription: String
init {
errorDescription = description
}
fun post() {
Timber.tag(_tag).e("post $errorDescription")
EventBus.getDefault().post(this)
}
}
//--------------------------------------------------
// and use MyLiveDataReactiveStreams (below) instead of LiveDataReactiveStreams
/*
* This is a modified version of androidx.lifecycle.LiveDataReactiveStreams
* The original LiveDataReactiveStreams object can't handle RxJava error conditions.
* Now errors are emitted as RxErrorEvent objects using the GreenRobot EventBus.
*/
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import com.github.ajalt.timberkt.Timber;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.util.concurrent.atomic.AtomicReference;
/**
* Adapts {@link LiveData} input and output to the ReactiveStreams spec.
*/
@SuppressWarnings("WeakerAccess")
public final class MyLiveDataReactiveStreams {
private final static String _tag = "LEE: <" + MyLiveDataReactiveStreams.class.getSimpleName() + ">";
private MyLiveDataReactiveStreams() {
}
/**
* Adapts the given {@link LiveData} stream to a ReactiveStreams {@link Publisher}.
*
* <p>
* By using a good publisher implementation such as RxJava 2.x Flowables, most consumers will
* be able to let the library deal with backpressure using operators and not need to worry about
* ever manually calling {@link Subscription#request}.
*
* <p>
* On subscription to the publisher, the observer will attach to the given {@link LiveData}.
* Once {@link Subscription#request} is called on the subscription object, an observer will be
* connected to the data stream. Calling request(Long.MAX_VALUE) is equivalent to creating an
* unbounded stream with no backpressure. If request with a finite count reaches 0, the observer
* will buffer the latest item and emit it to the subscriber when data is again requested. Any
* other items emitted during the time there was no backpressure requested will be dropped.
*/
@NonNull
public static <T> Publisher<T> toPublisher(
@NonNull LifecycleOwner lifecycle, @NonNull LiveData<T> liveData) {
return new MyLiveDataReactiveStreams.LiveDataPublisher<>(lifecycle, liveData);
}
private static final class LiveDataPublisher<T> implements Publisher<T> {
final LifecycleOwner mLifecycle;
final LiveData<T> mLiveData;
LiveDataPublisher(LifecycleOwner lifecycle, LiveData<T> liveData) {
this.mLifecycle = lifecycle;
this.mLiveData = liveData;
}
@Override
public void subscribe(Subscriber<? super T> subscriber) {
subscriber.onSubscribe(new MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription<T>(subscriber, mLifecycle, mLiveData));
}
static final class LiveDataSubscription<T> implements Subscription, Observer<T> {
final Subscriber<? super T> mSubscriber;
final LifecycleOwner mLifecycle;
final LiveData<T> mLiveData;
volatile boolean mCanceled;
// used on main thread only
boolean mObserving;
long mRequested;
// used on main thread only
@Nullable
T mLatest;
LiveDataSubscription(final Subscriber<? super T> subscriber,
final LifecycleOwner lifecycle, final LiveData<T> liveData) {
this.mSubscriber = subscriber;
this.mLifecycle = lifecycle;
this.mLiveData = liveData;
}
@Override
public void onChanged(@Nullable T t) {
if (mCanceled) {
return;
}
if (mRequested > 0) {
mLatest = null;
mSubscriber.onNext(t);
if (mRequested != Long.MAX_VALUE) {
mRequested--;
}
} else {
mLatest = t;
}
}
@SuppressLint("RestrictedApi")
@Override
public void request(final long n) {
if (mCanceled) {
return;
}
ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
if (mCanceled) {
return;
}
if (n <= 0L) {
mCanceled = true;
if (mObserving) {
mLiveData.removeObserver(MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription.this);
mObserving = false;
}
mLatest = null;
mSubscriber.onError(
new IllegalArgumentException("Non-positive request"));
return;
}
// Prevent overflowage.
mRequested = mRequested + n >= mRequested
? mRequested + n : Long.MAX_VALUE;
if (!mObserving) {
mObserving = true;
mLiveData.observe(mLifecycle, MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription.this);
} else if (mLatest != null) {
onChanged(mLatest);
mLatest = null;
}
}
});
}
@SuppressLint("RestrictedApi")
@Override
public void cancel() {
if (mCanceled) {
return;
}
mCanceled = true;
ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
if (mObserving) {
mLiveData.removeObserver(MyLiveDataReactiveStreams.LiveDataPublisher.LiveDataSubscription.this);
mObserving = false;
}
mLatest = null;
}
});
}
}
}
/**
* Creates an observable {@link LiveData} stream from a ReactiveStreams {@link Publisher}}.
*
* <p>
* When the LiveData becomes active, it subscribes to the emissions from the Publisher.
*
* <p>
* When the LiveData becomes inactive, the subscription is cleared.
* LiveData holds the last value emitted by the Publisher when the LiveData was active.
* <p>
* Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
* added, it will automatically notify with the last value held in LiveData,
* which might not be the last value emitted by the Publisher.
* <p>
* Note that LiveData does NOT handle errors and it expects that errors are treated as states
* in the data that's held. In case of an error being emitted by the publisher, an error will
* be propagated to the main thread and the app will crash.
*
* @param <T> The type of data hold by this instance.
*/
@NonNull
public static <T> LiveData<T> fromPublisher(@NonNull Publisher<T> publisher) {
return new MyLiveDataReactiveStreams.PublisherLiveData<>(publisher);
}
/**
* Defines a {@link LiveData} object that wraps a {@link Publisher}.
*
* <p>
* When the LiveData becomes active, it subscribes to the emissions from the Publisher.
*
* <p>
* When the LiveData becomes inactive, the subscription is cleared.
* LiveData holds the last value emitted by the Publisher when the LiveData was active.
* <p>
* Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
* added, it will automatically notify with the last value held in LiveData,
* which might not be the last value emitted by the Publisher.
*
* <p>
* Note that LiveData does NOT handle errors and it expects that errors are treated as states
* in the data that's held. In case of an error being emitted by the publisher, an error will
* be propagated to the main thread and the app will crash.
*
* @param <T> The type of data hold by this instance.
*/
private static class PublisherLiveData<T> extends LiveData<T> {
private final Publisher<T> mPublisher;
final AtomicReference<MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber> mSubscriber;
PublisherLiveData(@NonNull Publisher<T> publisher) {
mPublisher = publisher;
mSubscriber = new AtomicReference<>();
}
@Override
protected void onActive() {
super.onActive();
MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber s = new MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber();
mSubscriber.set(s);
mPublisher.subscribe(s);
}
@Override
protected void onInactive() {
super.onInactive();
MyLiveDataReactiveStreams.PublisherLiveData.LiveDataSubscriber s = mSubscriber.getAndSet(null);
if (s != null) {
s.cancelSubscription();
}
}
final class LiveDataSubscriber extends AtomicReference<Subscription>
implements Subscriber<T> {
@Override
public void onSubscribe(Subscription s) {
if (compareAndSet(null, s)) {
s.request(Long.MAX_VALUE);
} else {
s.cancel();
}
}
@Override
public void onNext(T item) {
postValue(item);
}
@SuppressLint("RestrictedApi")
@Override
public void onError(final Throwable ex) {
mSubscriber.compareAndSet(this, null);
ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
@Override
public void run() {
//NOTE: Errors are be handled upstream
Timber.tag(_tag).e("LiveData does not handle errors. Errors from publishers are handled upstream via EventBus. error: " + ex);
RxErrorEvent rx_event = new RxErrorEvent(ex.toString());
rx_event.post();
}
});
}
@Override
public void onComplete() {
mSubscriber.compareAndSet(this, null);
}
public void cancelSubscription() {
Subscription s = get();
if (s != null) {
s.cancel();
}
}
}
}
}