使用BottomNavigationView跳转Fragment报java.io.IOException: Canceled如何解决
问题原因
- 这个OkHttp抛出的
java.io.IOException: Canceled是请求被主动取消的典型报错,结合仅从Fragment2跳转Fragment1时才触发的场景,核心原因是Fragment2退出生命周期时,取消了和页面绑定的所有OkHttp请求,但其中部分请求是和Fragment1共用的、或是跳转时刚触发的Fragment1的请求被Fragment2的销毁逻辑误取消了。 - 结合BottomNavigationView的默认切换逻辑:切换底部导航时,旧Fragment(Fragment2)会先执行
onDestroyView/onStop等销毁生命周期,再创建/显示新Fragment(Fragment1)。如果你在Fragment2的销毁回调里写了全局的OkHttp请求取消逻辑(比如调用OkHttpClient.dispatcher.cancelAll()或者不分请求标签全量取消),就会刚好把刚触发的Fragment1的初始化网络请求一起取消,触发报错。而从Fragment3跳转时,Fragment3的销毁逻辑没有做全局取消、或是取消时机和Fragment1的请求时机没有重叠,就不会复现问题。
解决方案
- 不要使用全局取消请求的API:禁止在Fragment/Activity的销毁逻辑中调用
OkHttpClient.dispatcher.cancelAll(),改为给每个页面的请求添加独立的标签,销毁时仅取消对应标签的请求:
- 不要使用全局取消请求的API:禁止在Fragment/Activity的销毁逻辑中调用
// 给Fragment2的请求添加专属tag val request = Request.Builder() .url("你的接口地址") .tag("Fragment2_Request") .build() // Fragment2销毁时仅取消自己页面的请求 override fun onDestroyView() { super.onDestroyView() okHttpClient.dispatcher.cancelAll { request -> request.tag == "Fragment2_Request" } }
- 调整BottomNavigationView的Fragment切换生命周期:如果你的导航用的是Jetpack Navigation组件,可以给导航图的Fragment配置
android:maxLifecycle="STARTED",切换时仅把旧Fragment回调到onStop不会销毁View,避免销毁逻辑误杀请求。
- 调整BottomNavigationView的Fragment切换生命周期:如果你的导航用的是Jetpack Navigation组件,可以给导航图的Fragment配置
- 增加请求异常捕获:在OkHttp的请求回调处主动捕获
Canceled异常,针对这类用户主动取消的请求不需要做错误提示,仅做本地日志记录即可,避免崩溃:
- 增加请求异常捕获:在OkHttp的请求回调处主动捕获
override fun onFailure(call: Call, e: IOException) { if (e is IOException && e.message == "Canceled") { // 主动取消的请求,不需要处理 Log.d("OkHttp", "请求已取消") return } // 其他异常正常处理 }
内容的提问来源于stack exchange,提问作者wawanopoulos
相关产品推荐
相关产品推荐

