Android 架构组件改造和RxJava 2错误处理

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

我目前正试图在架构组件中实现新的ViewModels,使用来自Reformation和Okhttp的API请求,一切都正常,但我不知道如何将错误响应从Reformation传递到
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();
                }
            }
        }
    }
}