Retrofit Serviceから作成されたObservable<<List<Foo>> getFoo()
があり、.getFoo()
メソッドを呼び出した後、それを複数のサブスクライバーと共有する必要があります。ただし、.share()
メソッドを呼び出すと、ネットワークコールが再実行されます。再生オペレーターも機能しません。潜在的な解決策は.cache()
かもしれないことは知っていますが、この振る舞いの原因はわかりません。
_// Create an instance of our GitHub API interface.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
// Create a call instance for looking up Retrofit contributors.
Observable<List<Contributor>> testObservable = retrofit
.create(GitHub.class)
.contributors("square", "retrofit")
.share();
Subscription subscription1 = testObservable
.subscribe(new Subscriber<List<Contributor>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(List<Contributor> contributors) {
System.out.println(contributors);
}
});
Subscription subscription2 = testObservable
.subscribe(new Subscriber<List<Contributor>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(List<Contributor> contributors) {
System.out.println(contributors + " -> 2");
}
});
subscription1.unsubscribe();
subscription2.unsubscribe();
_
上記のコードは、前述の動作を再現できます。これをデバッグして、受信したリストが別のMemoryAddressに属していることを確認できます。
ConnectableObservablesを潜在的なソリューションとして検討しましたが、これには元のobservableを持ち歩き、新しいサブスクライバーを追加するたびに.connect()
を呼び出す必要があります。
.share()
のこの種の動作は、Retrofit 1.9まで正常に機能していました。 Retrofit 2-ベータ版の動作を停止しました。数時間前にリリースされたRetrofit 2リリースバージョンではまだテストしていません。
編集:2017年1月2日
将来の読者のために、私は記事を書きました here 事件についてもっと説明します!
.share()
によって返されたConnectedObservable
を通常のObservable
に(暗黙的に)キャストしているようです。ホットとコールドのオブザーバブルの違いを読んでください。
試して
_ConnectedObservable<List<Contributor>> testObservable = retrofit
.create(GitHub.class)
.contributors("square", "retrofit")
.share();
Subscription subscription1 = testObservable
.subscribe(new Subscriber<List<Contributor>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(List<Contributor> contributors) {
System.out.println(contributors);
}
});
Subscription subscription2 = testObservable
.subscribe(new Subscriber<List<Contributor>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(List<Contributor> contributors) {
System.out.println(contributors + " -> 2");
}
});
testObservable.connect();
subscription1.unsubscribe();
subscription2.unsubscribe();
_
編集:新しいサブスクリプションが必要になるたびにconnect()
を呼び出す必要はありません。オブザーバブルを起動するためにのみ必要です。 replay()
を使用して、後続のすべてのサブスクライバーがすべてのアイテムを生成することを確認できると思います
_ConnectedObservable<List<Contributor>> testObservable = retrofit
.create(GitHub.class)
.contributors("square", "retrofit")
.share()
.replay()
_
RxJava開発者DávidKarnokに戻って確認した後、ここで何が起こっているかについて完全な説明を提案したいと思います。
share()
はpublish().refCount()
として定義されます。 e。ソースObservable
は最初にpublish()
によってConnectableObservable
に変換されますが、connect()
を「手動で」呼び出す代わりに、その部分はrefCount()
。特に、refCount
は、最初のサブスクリプションを受け取ると、ConnectableObservable
でconnect()
を呼び出します。その後、少なくとも1人のサブスクライバーがいる限り、サブスクライブされたままになります。そして、最後に、サブスクライバーの数が0に下がると、サブスクライバーは上方にサブスクリプションを解除します。 coldObservables
を使用すると、Retrofitによって返されるものと同様に、実行中の計算が停止します。
これらのサイクルのいずれか後に別のサブスクライバーが登場すると、refCount
が再びconnect
を呼び出し、ソースObservableへの新しいサブスクリプションをトリガーします。この場合、別のネットワーク要求がトリガーされます。
現在、これは通常、Retrofit 1(および実際には this commit )より前のバージョンでは明らかになりませんでした。これらの古いバージョンのRetrofitはデフォルトですべてのネットワーク要求を別のスレッドに移動したためです。これは通常、最初のリクエスト/ Observable
がまだ実行中にすべてのsubscribe()
呼び出しが発生するため、新しいSubscriber
sが単にrefCount
したがって、追加のリクエストをトリガーしません/ Observables
。
ただし、Retrofitの新しいバージョンでは、デフォルトで作業を別のスレッドに移動しません。たとえば、subscribeOn(Schedulers.io())
を呼び出して明示的に移動する必要があります。そうしないと、すべてが現在のスレッドに留まります。つまり、2番目のsubscribe()
は、最初のObservable
がonCompleted
を呼び出した後にのみ発生するため、 Subscribers
はサブスクライブを解除し、すべてがシャットダウンされます。さて、最初の段落で見たように、2番目のsubscribe()
が呼び出されると、share()
には選択肢がありませんが、ソースObservableに別のSubscription
を引き起こし、別のネットワーク要求。
ですから、Retrofit 1から慣れた動作に戻るには、subscribeOn(Schedulers.io())
を追加するだけです。
これにより、ほとんどの場合、ネットワーク要求のみが実行されます。ただし、原則として、ネットワーク要求が非常に高速である場合、および/またはsubscribe()
呼び出しがかなりの遅延で発生する場合にのみ、複数の要求を取得できます(Retrofit 1を使用すると常に取得できます)。再度、2番目のsubscribe()
が発生すると、最初の要求は終了します。
したがって、Dávidはcache()
(ただし、あなたが言及した欠点があります)またはreplay().autoConnect()
を使用することをお勧めします。これらの リリースノート によると、autoConnect
はrefCount
の前半だけのように機能します。より正確には、
refCount()と動作が似ていますが、サブスクライバが失われても切断されない点が異なります。
つまり、最初のsubscribe()
が発生したときにのみリクエストがトリガーされますが、その後のSubscriber
sは、サブスクライバーが0であるかどうかに関係なく、発行されたすべてのアイテムを受け取ります。