在我们使用Retrofit 1.9时,我的同事创建了以下课程
public class SomeApiCallAction {
private Subscription subscription;
private NoInternetConnectionInterface noInternetConnectionInterface;
public interface NoInternetConnectionInterface {
PublishSubject noInternetConnection(Throwable throwable);
}
public void execute(Subscriber subscriber, NoInternetConnectionInterface noInternetConnectionInterface) {
this.noInternetConnectionInterface = noInternetConnectionInterface;
this.subscription = retrofit.someService().someApiCall()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber)
.retryWhen(retryFunction);
}
public void cancel() {
if (this.subscription != null) {
this.subscription.unsubscribe();
}
}
private Func1, Observable>> retryFunction = new Func1, Observable>>() {
@Override
public Observable> call(Observable extends Throwable> observable) {
return observable.flatMap(new Func1>() {
@Override
public Observable> call(final Throwable throwable) {
if (noInternetConnectionInterface!= null && (throwable instanceof IOException || throwable instanceof SocketTimeoutException)) {
return noInternetConnectionInterface.noInternetConnection(throwable);
}else{
return Observable.error(throwable);
}
}
});
}
}
SomeApiCallAction只是一个简单的类,它包含了内部的改进api调用,唯一特别的是它的重试功能.重试函数将检查throwable是否是IOException或SocketTimeoutException,如果是,它将调用接口,以便我们可以向用户提供重试对话框,询问他们是否要重试该操作.我们的用法类似于以下代码段
public class SomeActivity implement NoInternetConnectionInterface {
@OnClick(R.id.button)
public void do(View v) {
new SomeApiCallAction().execute(
new Subscriber(),
this
)
}
@Override
public PublishSubject noInternetConnection(final Throwable throwable) {
Log.i("Dev", Thread.currentThread() " Error!");
final PublishSubject subject = PublishSubject.create();
runOnUiThread(new Runnable() {
@Override
public void run() {
NoInternetDialogFragment dialog = NoInternetDialogFragment.newInstance();
dialog.setNoInternetDialogFragmentListener(new NoInternetDialogFragmentListener{
@Override
public void onUserChoice(boolean retry, NoInternetDialogFragment dialog) {
Log.i("Dev", Thread.currentThread() " Button Click!");
if (retry) {
subject.onNext(1);
} else {
subject.onError(throwable);
}
dialog.dismiss();
}
});
dialog.show(getSupportFragmentManager(), NoInternetDialogFragment.TAG);
}
});
return subject;
}
}
当我们使用Retrofit 1.9.0时,这个实现工作得非常完美.我们通过打开飞行模式进行测试,然后按下按钮执行api呼叫.
>第一次执行失败,我在重试功能中得到了UnknownHostException.
>所以,我调用界面(Activity)来显示重试对话框
>我仍然在飞行模式下按重试按钮重复执行
>正如预期的那样,用户按下重试按钮后发生的每次执行都失败了,我总是在重试功能中得到UnknownHostException.
>如果我一直按下重试按钮,重试对话框将永远显示,直到我关闭飞行模式.
但在我们更新我们的依赖关系之后
'com.squareup.retrofit2:retrofit:2.0.2'
'com.squareup.retrofit2:adapter-rxjava:2.0.2'
我们再试一次,但这次行为改变了,
>第一次执行失败,我在重试功能中获得了与之前相同的UnknownHostException.
>所以,我调用界面(Activity)来显示重试对话框
>我仍然在飞行模式下按重试按钮重复执行
>但是这一次,在重试函数中,我得到了NetworkOnMainThreadException而不是像它那样接收UnknowHostException.
>因此条件不匹配,接口没有被调用,结果只有1个重试对话框呈现给用户.
以下是上面代码的日志
Thread[android_0,5,main] Error!
Thread[main,5,main] Button Click!
你知道这会导致什么吗?任何建议,评论都会非常感激.
注意:以下是我们一直在使用并可能相关的其他依赖项.但是它们最近没有更新,从本项目开始就使用这些版本.
'com.jakewharton:butterknife:8.0.1'
'io.reactivex:rxandroid:1.1.0'
'io.reactivex:rxjava:1.1.0'
'com.google.dagger:dagger-compiler:2.0'
'com.google.dagger:dagger:2.0'
'javax.annotation:jsr250-api:1.0'
更多信息
我只是将我的代码重置回到我们使用Retrofit 1.9的时候,我发现打印日志不同
Thread[Retrofit-Idle,5,main] Error!
Thread[main,5,main] Button Click!
不确定这是否与问题有关,但很明显,在1.9.0中,我将不同线程中的接口调用为2.0.0
最终编辑
在阅读了@JohnWowUs的答案并按照他提供的链接后,我发现在Retrofit 2中,网络调用默认是同步的
要解决我的问题,有两种方法可以解决此问题
1.)按照@JohnWowUs的建议,通过为retryFunction指定线程
this.subscription = retrofit.someService().someApiCall()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber)
.retryWhen(retryFunction, Schedulers.io());
2.)创建改造对象时,在创建RxJavaCallAdapterFactory时指定线程
retrofit = new Retrofit.Builder()
.baseUrl(AppConfig.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(getGson()))
.addCallAdapterFactory(
RxJavaCallAdapterFactory.createWithScheduler(
Schedulers.from(threadExecutor)
)
)
.build();
解决方法:
我认为问题在于,当您重新订阅时,由于在retryWhen中使用默认的trampoline调度程序而在主线程上订阅. Retrofit 1.9为您处理了调度,因此使用subscribeOn毫无意义.问题讨论是here.在Retrofit 2中我相信这已经改变了所以你应该尝试类似的东西
this.subscription = retrofit.someService().someApiCall()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber)
.retryWhen(retryFunction, Schedulers.io());