我有一个用例,我在一个Completable中初始化一些全局变量,在链的下一步(使用andThen
运算符)我使用这些变量。
以下示例详细解释了我的用例
假设你有一个班级User
class User {
String name;
}
我有一个像这样的Observable,
private User mUser; // this is a global variable
public Observable<String> stringObservable() {
return Completable.fromAction(() -> {
mUser = new User();
mUser.name = "Name";
}).andThen(Observable.just(mUser.name));
}
首先,我在我的Completable.fromAction
中进行了一些初始化,我希望andThen
运算符仅在完成Completable.fromAction
之后启动。
这意味着我希望在mUser
运算符启动时初始化andThen
。
以下是我对此可观察
的订阅 stringObservable()
.subscribe(s -> Log.d(TAG, "success: " + s),
throwable -> Log.e(TAG, "error: " + throwable.getMessage()));
但是当我运行此代码时,我收到错误
Attempt to read from field 'java.lang.String User.name' on a null object reference
表示mUser
为空,andThen
在执行Completable.fromAction
中的代码之前启动。这里发生了什么事?
根据andThen
返回一个Observable,它将订阅此Completable,一旦完成,就会订阅{@code next} ObservableSource。此Completable中的错误事件将传播到下游订阅者,并将导致跳过Observable的订阅。
答案 0 :(得分:28)
问题不在andThen
,而在Observable.just(mUser.name)
内的语句为andThen
。 just
运算符将尝试立即创建observable,尽管它仅在Completable.fromAction
之后发出。
问题在于,在尝试使用just创建Observable
时,mUser
为空。
解决方案:您需要推迟创建String Observable直到订阅发生,直到andThen
的上游开始发射。
而不是andThen(Observable.just(mUser.name));
使用
andThen(Observable.defer(() -> Observable.just(mUser.name)));
或者
andThen(Observable.fromCallable(() -> mUser.name));
答案 1 :(得分:2)
我不认为@Sarath Kn的答案是100%正确的。是的,just
会在调用后立即创建可观察到的对象,但andThen
仍在意外时间调用just
。
我们可以将andThen
与flatMap
进行比较以获得更好的理解。这是一个完全可运行的测试:
package com.example;
import org.junit.Test;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.observers.TestObserver;
import io.reactivex.schedulers.Schedulers;
public class ExampleTest {
@Test
public void createsIntermediateObservable_AfterSubscribing() {
Observable<String> coldObservable = getObservableSource()
.flatMap(integer -> getIntermediateObservable())
.subscribeOn(Schedulers.trampoline())
.observeOn(Schedulers.trampoline());
System.out.println("Cold obs created... subscribing");
TestObserver<String> testObserver = coldObservable.test();
testObserver.awaitTerminalEvent();
/*
Resulting logs:
Creating observable source
Cold obs created... subscribing
Emitting 1,2,3
Creating intermediate observable
Creating intermediate observable
Creating intermediate observable
Emitting complete notification
IMPORTANT: see that intermediate observables are created AFTER subscribing
*/
}
@Test
public void createsIntermediateObservable_BeforeSubscribing() {
Observable<String> coldObservable = getCompletableSource()
.andThen(getIntermediateObservable())
.subscribeOn(Schedulers.trampoline())
.observeOn(Schedulers.trampoline());
System.out.println("Cold obs created... subscribing");
TestObserver<String> testObserver = coldObservable.test();
testObserver.awaitTerminalEvent();
/*
Resulting logs:
Creating completable source
Creating intermediate observable
Cold obs created... subscribing
Emitting complete notification
IMPORTANT: see that intermediate observable is created BEFORE subscribing =(
*/
}
private Observable<Integer> getObservableSource() {
System.out.println("Creating observable source");
return Observable.create(emitter -> {
System.out.println("Emitting 1,2,3");
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
System.out.println("Emitting complete notification");
emitter.onComplete();
});
}
private Observable<String> getIntermediateObservable() {
System.out.println("Creating intermediate observable");
return Observable.just("A");
}
private Completable getCompletableSource() {
System.out.println("Creating completable source");
return Completable.create(emitter -> {
System.out.println("Emitting complete notification");
emitter.onComplete();
});
}
}
您可以看到,当我们使用flatmap
时,just
在订阅之后称为 ,这很有意义。如果中间可观察对象取决于向flatmap
发送给 的项目,则系统当然无法在订阅之前创建中间可观察对象。它尚无任何值。您可以想象,如果flatmap
在订阅前叫just
,这将行不通:
.flatMap(integer -> getIntermediateObservable(integer))
奇怪的是,andThen
能够在订阅前创建其内部可观察到的(即称为just
)。它可以做到 是有道理的。 andThen
唯一会收到的是完整的通知,因此没有理由不尽早创建中间可观察对象。唯一的问题是,这不是预期的行为。
@Sarath Kn的解决方案是正确的,但原因有误。如果使用defer
,我们可以看到一切正常:
@Test
public void usingDefer_CreatesIntermediateObservable_AfterSubscribing() {
Observable<String> coldObservable = getCompletableSource()
.andThen(Observable.defer(this::getIntermediateObservable))
.subscribeOn(Schedulers.trampoline())
.observeOn(Schedulers.trampoline());
System.out.println("Cold obs created... subscribing");
TestObserver<String> testObserver = coldObservable.test();
testObserver.awaitTerminalEvent();
/*
Resulting logs:
Creating completable source
Cold obs created... subscribing
Emitting complete notification
Creating intermediate observable
IMPORTANT: see that intermediate observable is created AFTER subscribing =) YEAY!!
*/
}