我可以使用Retrofit + RxJava来聆听无尽的流吗?例如Twitter流。我有这个:
public interface MeetupAPI {
@GET("http://stream.meetup.com/2/rsvps/")
Observable<RSVP> getRSVPs();
}
MeetupAPI api = new Retrofit.Builder()
.baseUrl(MeetupAPI.RSVP_API)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(MeetupAPI.class);
api.getRSVPs()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(rsvp -> Log.d(TAG, "got rsvp"),
error -> Log.d(TAG, "error: " + error),
() -> Log.d(TAG, "onComplete"));
但在解析第一个对象后调用“onComplete”。有没有办法告诉Retrofit保持开放直到另行通知?
答案 0 :(得分:15)
这是我的解决方案:
您可以使用@Streaming批注:
public interface ITwitterAPI {
@GET("/2/rsvps")
@Streaming
Observable<ResponseBody> twitterStream();
}
ITwitterAPI api = new Retrofit.Builder()
.baseUrl("http://stream.meetup.com")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build().create(ITwitterAPI.class);
使用@Streaming
,我们可以从ResponseBody
获得原始输入。
这里我的函数用于将事物换行包裹事件:
public static Observable<String> events(BufferedSource source) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try {
while (!source.exhausted()) {
subscriber.onNext(source.readUtf8Line());
}
subscriber.onCompleted();
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}
}
});
}
结果用法:
api.twitterStream()
.flatMap(responseBody -> events(responseBody.source()))
.subscribe(System.out::println);
关于正常停止的更新
当我们取消订阅时,改装会关闭输入流。但是不可能从输入流本身检测到输入流是否关闭,所以只有这样 - 尝试从流中读取 - 我们使用Socket closed
消息获得异常。
我们可以将此异常解释为结束:
@Override
public void call(Subscriber<? super String> subscriber) {
boolean isCompleted = false;
try {
while (!source.exhausted()) {
subscriber.onNext(source.readUtf8Line());
}
} catch (IOException e) {
if (e.getMessage().equals("Socket closed")) {
isCompleted = true;
subscriber.onCompleted();
} else {
throw new UncheckedIOException(e);
}
}
//if response end we get here
if (!isCompleted) {
subscriber.onCompleted();
}
}
如果连接因响应结束而关闭,我们没有任何例外。这里isCompleted
检查一下。如果我错了,请告诉我。)
答案 1 :(得分:1)
Zella's answer适用于带有rxJava的Retrofit2, 对于rxJava2,我修改了自定义的observable,如下所示:
//imports
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okio.BufferedSource
import java.io.IOException
fun events(source: BufferedSource): Observable<String> {
return Observable.create { emitter ->
var isCompleted = false
try {
while (!source.exhausted()) {
emitter.onNext(source.readUtf8Line()!!)
}
emitter.onComplete()
} catch (e: IOException) {
e.printStackTrace()
if (e.message == "Socket closed") {
isCompleted = true
emitter.onComplete()
} else {
throw IOException(e)
}
}
if (!isCompleted) {
emitter.onComplete()
}
}
}
模块级别build.gradle依赖项的更改:
//retrofit rxJava2 adapter
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.1'
//rx-java
implementation 'io.reactivex.rxjava2:rxjava:2.2.11'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
改装适配器更改:
ITwitterAPI api = new Retrofit.Builder()
.baseUrl("http://stream.meetup.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build().create(ITwitterAPI.class);
并将流API称为
api.twitterStream()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap { responseBody-> events(responseBody.source()) }
.subscribe({ t ->
Log.i(TAG, "onNext t=$t")
}, { e ->
Log.i(TAG, "onError e=$e")
}, {
Log.i(TAG, "onFinish")
})