RxAndroid + Retrofit callTimeout 不会触发 onError

Posted

技术标签:

【中文标题】RxAndroid + Retrofit callTimeout 不会触发 onError【英文标题】:RxAndroid + Retrofit callTimeout doesn't trigger onError 【发布时间】:2020-08-14 12:59:27 【问题描述】:

我正在使用 Rxandroid + Retrofit 来发出 http 请求。代码如下:

Interceptor headerInterceptor = getHeaderInterceptor();
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .callTimeout(5, TimeUnit.SECONDS)
                .addInterceptor(headerInterceptor)
                .addInterceptor(httpLoggingInterceptor)
                .build();

        Gson gson = new GsonBuilder()
                .setLenient()
                .create();

        sRetrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(client)
                .build();

像这样使用它:

ApiProvider.provideApi(MyApi.class)
                .submit(id, mRequest)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        response -> 
                            Log.w("tag", "success");
                        ,
                        throwable -> 
                            Log.w("tag", "error");
                        
                );

我将connectTimeout / readTimeout / writeTimeout设置为60秒,将callTimeout设置为5秒。

我知道这个配置可能不合理但我只想在5秒后得到一个超时异常,并且可以调用Log.w("tag", "error");

但是,我发现我的测试永远不会调用这条线。如果我将connectionTimeout 设置为 1 秒,那么将立即调用此行。

那么如果我想让 callTimeout 触发日志错误行该怎么办呢?

【问题讨论】:

尝试添加.retryOnConnectionFailure(false) 5 秒的通话超时非常少...大约 50 秒或 1 分钟。 @AntonisRadz 尝试了retryOnConnectionFailure(false),不起作用。但还是谢谢你们! @PrajwalW 嗯,我同意 5 秒太短,但如果我将连接超时设置为 1 秒,代码运行良好,我可以调用 onError。谢谢! 正如你提到的,连接超时似乎小于呼叫超时。请尝试一次。设置调用超时值 > 连接超时值。 【参考方案1】:

就我所见,我认为您的问题可能不在于您为callTimeout(5, TimeUnit.SECONDS) 设置的秒数,我认为您的 Rx 流可能已经引发了一些错误,因此流刚刚中断您可以获得任何响应从这里。但是,您将时间秒数重置为 1 秒,然后重新启动应用程序,此时间流不会中断并且您会收到错误消息。 如此简单地再次对其进行重新测试,以确保您的流在输入此订阅之前不会中断。

我已经用一些愚蠢的实现进行了测试:

class MainActivity : AppCompatActivity() 

    val delayInterceptor = object : Interceptor 
        override fun intercept(chain: Interceptor.Chain): okhttp3.Response 
            Thread.sleep(6000L)
            return chain.proceed(chain.request())
        

    
    val client: OkHttpClient = OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .callTimeout(5, TimeUnit.SECONDS)
            .addInterceptor(delayInterceptor)
            .build()

    val retrofit: Retrofit = Retrofit.Builder()
            .baseUrl("https://en.wikipedia.org/")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();

    data class Wiki(
            @SerializedName("type")
            val type: String
    )

    interface WikiService 
        @GET("api/rest_v1/page/random/summary")
        fun getRandomSummary(): Single<Wiki>
    

    @SuppressLint("CheckResult")
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        retrofit.create(WikiService::class.java)
                .getRandomSummary()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    Log.d("tag", "success")
                , 
                    Log.e("tag", "error")
                )
    

【讨论】:

感谢您的回答!请您帮忙确认Log.e("tag", "error " + it),其中it 指的是InterruptedIOException: timeout?好像回调是由 callTimeout 触发的,它应该是这个错误而不是其他错误。如果响应不是json对象而是纯字符串,触发onError的原因可能不止1个,例如SocketTimeoutException(由connectionTimeout引起)或ClassCastException。感谢您的帮助!【参考方案2】:

我终于找到原因了。

我使用的是com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory,根据其自述文件已被弃用:https://github.com/JakeWharton/retrofit2-rxjava2-adapter

This is now DEPRECATED!

Retrofit 2.2 and newer have a first-party call adapter for RxJava 2: https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2

切换到retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory 后,一切都开始运行良好。

顺便说一句,对于任何可能对两者之间的区别感兴趣的人?附上我在下面找到的关键信息:

【讨论】:

以上是关于RxAndroid + Retrofit callTimeout 不会触发 onError的主要内容,如果未能解决你的问题,请参考以下文章

Retrofit2.0+RxJava+RxAndroid——强强联合的网络请求框架

MVP实战心得—封装Retrofit2.0+RxAndroid+RxBus

RxAndroid+RxJava+Gson+retrofit+okhttp初步搭建android网络请求框架

Android Retrofit和Rxandroid的使用

Android Retrofit和Rxandroid的使用

Retrofit2.0+RxJava+RxAndroid——强强联合的网络请求框架