You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit+RxJava同步数据遇SocketTimeoutException致App崩溃求助

问题分析

从崩溃日志和你的代码来看,应用崩溃的本质是RxJava的UndeliverableException,触发点是网络请求抛出的SocketTimeoutException: SSL handshake timed out。具体原因是:当zip操作符中的某个Single请求出错时,整个流的错误没有被上层订阅者正确捕获处理,导致RxJava无法传递这个错误,最终抛出未交付异常引发崩溃。

再结合代码细节看:

  • syncData()返回的Single<Boolean>如果在订阅时没添加onError回调,任何子请求的错误都会成为未捕获错误
  • HttpRequestDataManager里用Single.create包裹已有Single的订阅,虽然调用了subscriber.onError(it),但如果上层订阅不处理这个错误,依然会引发问题
  • SSL握手超时本身是网络层面的问题,需要先优化OkHttp的超时配置来减少这类情况的发生

解决方案

1. 为syncData()的订阅添加错误处理(最关键)

确保所有流中的错误都被捕获,这是阻止崩溃的核心。比如你调用syncData()的地方要这样写:

syncData()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { isSuccess -> 
            // 处理同步成功逻辑
            getProgressTextView().text = getString(R.string.sync_success)
        },
        { error -> 
            // 处理所有错误,包括SocketTimeoutException
            getProgressTextView().text = getString(R.string.sync_failed)
            error.message?.let { toast(it) }
            
            // 针对超时做针对性提示
            if (error is SocketTimeoutException) {
                toast(getString(R.string.network_timeout_prompt))
            }
        }
    )

2. 优化HttpRequestDataManager代码,避免冗余的Single.create

原代码用Single.create包裹已有Single的订阅完全没必要,直接用RxJava操作符转换更安全,还能减少错误传递的隐患:

object HttpRequestDataManager : AnkoLogger {
    inline fun <reified T : io.realm.RealmModel> getAndSave(single: Single<List<T>>): Single<Boolean> {
        return single
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSuccess { list ->
                executeTransaction {
                    list.saveAll()
                }
                debug { "success sync, with ${list.size} results" }
            }
            .map { true } // 成功时返回true
            .onErrorReturn { error ->
                warn { error.message }
                // 可选策略1:出错时返回false,让zip继续处理其他请求
                false
                // 可选策略2:让错误传递到上层处理,注释上面的false,改用下面的代码
                // return@onErrorReturn Single.error(error)
            }
    }
}

两种策略按需选择:

  • 返回false:zip会继续处理其他请求,最终整体返回false
  • 传递错误:直接中断流,由上层onError统一处理

3. 为OkHttp设置合理超时时间,减少SSL握手超时

在构建Retrofit的OkHttpClient时,添加超时配置:

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(JwtInterceptor(context))
    .connectTimeout(15, TimeUnit.SECONDS) // 连接超时
    .readTimeout(20, TimeUnit.SECONDS) // 读取超时
    .writeTimeout(20, TimeUnit.SECONDS) // 写入超时
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .client(okHttpClient)
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build()

根据你的实际网络环境调整超时时间,避免过短导致频繁超时。

4. 添加RxJava全局错误兜底处理器(可选但推荐)

为了防止其他未捕获的错误导致崩溃,可以在Application的onCreate中添加全局错误处理:

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        RxJavaPlugins.setErrorHandler { error ->
            if (error is UndeliverableException) {
                val cause = error.cause
                if (cause is SocketTimeoutException) {
                    // 记录日志或做静默处理,避免崩溃
                    Log.w("RxGlobalError", "SSL handshake timeout caught: ${cause.message}")
                    return@setErrorHandler
                }
            }
            // 其他错误记录日志或上报
            Log.e("RxGlobalError", "Undeliverable error occurred", error)
        }
    }
}

这样即使有未被捕获的错误,也不会直接导致应用崩溃。


总结

核心问题是错误未被正确捕获,只要确保每个RxJava流的订阅都有onError回调,同时优化网络超时配置,就能解决这个崩溃问题。另外,简化RxJava流的写法可以减少错误传递的隐患。

内容的提问来源于stack exchange,提问作者Flofloaud1034

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:32:28