【问题标题】:Retrofit and RxJava, avoiding callback hell改造和 RxJava,避免回调地狱
【发布时间】:2016-03-27 06:59:51
【问题描述】:
所以目前,我使用日期参数(使用 Retrofit)发出异步网络请求,并且如果该请求返回的响应代码不是 200(或者如果它的 429、400,或者如果响应正文为空,最容易确定的),我在 1 天前使用日期参数发出新请求。同样,如果此请求返回的响应代码不是 200,我会再发出一个日期比前一个早 1 天的请求,如果前两个请求失败,则总共有 3 个可能的请求。
我目前正在通过一堆回调来实现这一点,并调用一个新方法设置来在每次尝试的第 -1 天执行请求。
我知道我可以通过 Rx 和 Rx 功能中内置的 Retrofits 实现更简洁的解决方案。这将如何实现?
【问题讨论】:
标签:
android
retrofit
rx-java
rx-android
【解决方案1】:
首先你需要在你的依赖中添加 RxAndroid 和 RxJava 适配器
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'io.reactivex:rxandroid:1.0.1'
然后您需要将调用适配器注册到您的改造声明中
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(myBaseurl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
然后您可以将您的服务接口返回类型从 Call 更改为 Observable
public interface MyAPIService {
@POST("user")
Call<User> getUser();
@POST("user")
Observable<Response<User>> getUserWithRxJava();
@POST("user_friends")
Observable<Response<List<User>>> getUserFriends();
}
这是一个链接调用的例子
myService.getUserWithRxJava()
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<Response<User>, Observable<Response<List<User>>>>() {
@Override
public Observable<List<Home>> call(Response<User> responseUser) {
// You can also use responseUser.code to get the response code
// but isSuccess() function will return true if the code
// is in the range [200..300)
if (responseUser.isSuccess()) {
return myService.getUserFriends();
} else {
// You can also use Observable.empty() if you want to ignore unsuccessful response
return Observable.error(myThrowable);
}
}
})
.subscribe(new Subscriber<Response<List<User>>>() {
@Override
public void onCompleted() {
// TODO Completed
}
@Override
public void onError(Throwable e) {
// TODO Handle the error
}
@Override
public void onNext(Response<List<User>> friendListResponse) {
// TODO do something with the data
// To get the serialized data you can use friendListResponse.body();
}
});
【解决方案2】:
非 200 响应应返回为 RetrofitError 对象,其中包含带有状态代码的 Response 对象。
你可以这样做:
observable
.retryWhen(new RetryStrategy())
.subscribe(...);
而RetryStrategy 可能看起来像这样(注意,我使用的是retrolambda,所以无论您在哪里看到->,只需替换为一个新的匿名内部类):
public class RetryStrategy implements Func1<Observable<? extends Throwable>, Observable<?>> {
public RetryStrategy() {}
@Override
public Observable<?> call(Observable<? extends Throwable> attempts) {
return attempts.flatMap((throwable) -> {
if (throwable instanceof RetrofitError) {
RetrofitError error = (RetrofitError) throwable;
if (error.getKind() == RetrofitError.Kind.HTTP) {
if (error.getResponse().getStatus() == 401) {
// This is where you attempt to recover
return someRecoveryObservable???;
}
}
}
// Bubble any other errors back up, e.g. connection loss.
return Observable.error(throwable);
});
}
}
您还可以在此处通过添加对重试计数的支持来实现Exponential Backoff,并在恢复阶段使用Observable.timer(long, TimeUnit)(并根据当前的尝试次数计算时间)。这种方法有时会极大地改善不连贯的连接问题,尤其是在后台运行的“即发即弃”任务中。