Android RxJava主题在不正确的调度程序上发出

Android RxJava主题在不正确的调度程序上发出,android,multithreading,rx-java,scheduler,subject,Android,Multithreading,Rx Java,Scheduler,Subject,我有以下课程,我是单身人士: public class SessionStore { Subject<Session, Session> subject; public SessionStore() { subject = new SerializedSubject<>(BehaviorSubject.create(new Session()); } public void set(Session session) {

我有以下课程,我是单身人士:

public class SessionStore {
    Subject<Session, Session> subject;

    public SessionStore() {
       subject = new SerializedSubject<>(BehaviorSubject.create(new Session());
    }

    public void set(Session session) {
        subject.onNext(session);
    }

    public Observable<UserSession> observe() {
        return subject.distinctUntilChanged();
    }
}
公共类会话存储{
学科;
公共会话存储(){
subject=newserializedsubject(BehaviorSubject.create(newsession());
}
公共无效集(会话){
主题:下一届(会议);
}
公众观察{
返回subject.distinctUntilChanged();
}
}
在活动中,我观察会话并对每次更改执行网络操作:

private Subscription init() {
    return sessionStore
            .observe()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .flatMap(new Func1<Session, Observable<Object>>() {
                @Override
                public Observable<Object> call(Session session) {
                    return retrofitService.getAThing();
                }
            })
            .subscribe(...);
}
private Subscription init(){
返回会话存储
.观察
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(新函数1(){
@凌驾
公共可观察呼叫(会话){
return.service.getAThing();
}
})
.认购(……);
}
当我订阅会话存储时,主题会立即在
io()
上发出,因为它是
行为主题
,订阅者在
mainThread()上执行

当我在订阅时调用
sessionStore.set(new AnotherSession())
时,问题就出现了。我认为这应该执行
io()
调度程序上
init()
中定义的流。然而,相反的情况是,流在
subject.onNext()的同一线程上执行调用了
。当我在
flatMap()中执行网络操作时,导致了
NetworkOnMainThreadException

我对主题的理解是错误的吗?我是不是这样误用了它们?请问正确的解决方法是什么


我还尝试用
observeble.fromEmitter()
方法中的
observeable.fromEmitter()
替换整个主题方法,但令人惊讶的是,输出结果完全相同。

当你调用操作符时,它会影响整个下游。如果你调用:

.observeOn(AndroidSchedulers.mainThread())
在错误的位置,流的其余部分在指定的线程上执行

我建议您始终添加:

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
在溪流的尽头:

private Subscription init() {
    return sessionStore
            .observe()
            .flatMap(new Func1<Session, Observable<Object>>() {
                @Override
                public Observable<Object> call(Session session) {
                    return retrofitService.getAThing();
                }
            })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(...);
}
private Subscription init(){
返回会话存储
.观察
.flatMap(新函数1(){
@凌驾
公共可观察呼叫(会话){
return.service.getAThing();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.认购(……);
}

我想你忘记了你的
主题也是一个观察者,所以为了让
onNext
在io线程上运行,请尝试

public class SessionStore {
    Subject<Session, Session> subject;

    public UserSessionStore() {
       subject = new SerializedSubject<>(BehaviorSubject.create(new Session())).observeOn(Schedulers.io());
    }

    public void set(Session session) {
        subject.onNext(session);
    }

    public Observable<UserSession> observe() {
        return subject.distinctUntilChanged();
    }
}
公共类会话存储{
学科;
公共用户会话存储(){
subject=newserializedSubject(BehaviorSubject.create(newsession()).observeOn(Schedulers.io());
}
公共无效集(会话){
主题:下一届(会议);
}
公众观察{
返回subject.distinctUntilChanged();
}
}

请看一下书中的以下部分“

默认情况下,对主题调用onNext()会直接传播到所有观察者的onNext()回调方法。毫不奇怪,这些方法共享相同的名称。在某种程度上,对主题调用onNext()会间接调用每个订阅服务器上的onNext()

让我们回顾一下: 如果从线程1对主题调用onNext,它将从线程1向订阅者调用onNext。onSubscribe将被释放

所以首先要做的是: 订阅将在哪个线程上进行:

retrofitService.getAThing()
我只是猜测一下,然后说它是调用线程。这将是observeOn中描述的线程,这是Android UI循环

根据调度程序的指定,observeOn下的每个值都将从线程a转移到线程b。observeOn应该在订阅之前出现在UI循环上。订阅中接收到的每个值都将出现在UI循环上,这不会阻止UI线程或以异常结束

请看一下示例代码和输出:

class SessionStore {
    private Subject<String, String> subject;

    public SessionStore() {
        subject = BehaviorSubject.create("wurst").toSerialized();
    }

    public void set(String session) {
        subject.onNext(session);
    }

    public Observable<String> observe() {
        return subject
                .asObservable()
                .doOnNext(s -> System.out.println("Receiving value on Thread:: " + Thread.currentThread()))
                .distinctUntilChanged();
    }
}

@Test
public void name() throws Exception {
    // init
    SessionStore sessionStore = new SessionStore();

    TestSubscriber testSubscriber = new TestSubscriber();
    Subscription subscribe = sessionStore
            .observe()
            .flatMap(s -> {
                return Observable.fromCallable(() -> {
                    System.out.println("flatMap Thread:: " + Thread.currentThread());
                    return s;
                }).subscribeOn(Schedulers.io());
            })
            .doOnNext(s -> System.out.println("After flatMap Thread:: " + Thread.currentThread()))
            .observeOn(Schedulers.newThread()) // imagine AndroidScheduler here
            .subscribe(testSubscriber); // Do UI-Stuff in subscribe

    new Thread(() -> {
        System.out.println("set on Thread:: " + Thread.currentThread());
        sessionStore.set("123");
    }).start();

    new Thread(() -> {
        System.out.println("set on Thread:: " + Thread.currentThread());
        sessionStore.set("345");
    }).start();

    boolean b = testSubscriber.awaitValueCount(3, 3_000, TimeUnit.MILLISECONDS);

    Assert.assertTrue(b);
}

谢谢。尽管我确实在流的末尾指定了调度程序,但问题仍然存在。
reformationservice.getAThing()
没有指定任何调度程序?没有。我还简化了示例以提高可读性。我也尝试了这个示例。但是通过在
distinctUntilChanged()之后添加
io()
。我相信你的意思是
subscribeOn()
而不是
observeOn()
并且
序列化子对象的构造函数接受Subject而不可观察,因此您不能按编写的方式执行。是的,这是我的一个输入错误。
observeOn
应该在
序列化子对象上,但它仍然应该是
observeOn
。谢谢,我还没有考虑为它定义调度程序在flatMap内部创建的observeOn。我认为如果在flatMap之后定义调度程序,它将应用于整个流。这取决于您试图实现什么。您可以在flatMap之前使用observeOn。链中的每个值都会从observeOn的点从一个线程X移动到另一个线程。但是,您通过在flatMap中为可观察对象指定Subscribe,您实现了并发性。因此onSubscribe/onObserve之间存在差异。onObserve可以在一个可观察对象中放置多次,以便将排放移动到另一个线程。对于Subscribe,一个可观察对象应该只有一个Subscribe,因为Subscribe是最重要的将使用upper。考虑到这一点,我不明白的是,当我从
flatMap()
内部移除
subscribeOn()
并将其放在前面一步时,流的行为为何会有所不同。就像
sessionStore.observe().subscribeOn().flatMap()…
在flatMap中,您正在创建一个新的obser
Receiving value on Thread:: Thread[main,5,main]
flatMap Thread:: Thread[RxIoScheduler-2,5,main]
After flatMap Thread:: Thread[RxIoScheduler-2,5,main]
set on Thread:: Thread[Thread-1,5,main]
set on Thread:: Thread[Thread-0,5,main]
Receiving value on Thread:: Thread[Thread-1,5,main]
flatMap Thread:: Thread[RxIoScheduler-2,5,main]
After flatMap Thread:: Thread[RxIoScheduler-2,5,main]
Receiving value on Thread:: Thread[Thread-1,5,main]
flatMap Thread:: Thread[RxIoScheduler-2,5,main]
After flatMap Thread:: Thread[RxIoScheduler-2,5,main]