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
相关产品推荐
相关产品推荐

