当前位置:网站首页>Kotlin协程利用CoroutineContext实现网络请求失败后重试逻辑
Kotlin协程利用CoroutineContext实现网络请求失败后重试逻辑
2022-07-05 13:38:00 【大鱼Ss】
在Android开发中有一个典型场景:网络请求失败后重试:一般的逻辑是弹出一个Dialog提醒用户“网络请求失败”,并提供重试的按钮。
如果当前页面只有一个网络请求,那么逻辑就很简单了:只需要再调用一下发起这个网络请求的方法就可以了。而当一个页面有多个网络请求时,我常用的办法为失败回调加状态,根据不同的状态调用不同的方法。但是这个方法不免有些繁琐,也有点不安全。首先,你要额外的增加状态,并将它传来传去。有些情况下,你甚至还需要重新初始化网络请求参数。更要命的是:你还要管理这个状态,一旦管理不善,就会导致调用了不该调用的方法,引入严重的BUG。
直到有一天我看到CoroutineExceptionHandler,灵光突现——可以使用协程上下文来保存将来可能需要重试的网络请求和Request数据,这样就能解决上面的问题了。
由于我所开发的大多数项目都是采用ViewModel实现网络请求逻辑和UI层的解耦,而网络请求基本上是采用Coroutine+Retrofit的方式实现的,基本上都是使用viewModelScope。
viewModelScope.launch() {
request()
}
viewModelScope本质上是一个ViewModel的扩展函数,利用它可以便捷的在ViewModel创建协程,具体的代码就不展开了。默认情况下,它的CoroutineContext由Job和CoroutineDispatcher组成。而协程的上下文本质上就是一个实现了key-value访问的方式的链表结构。我们可以通过继承AbstractCoroutineContextElement的方式实现自定义的CoroutineContext上下文:
class RetryCallback(val callback: () -> Unit) : AbstractCoroutineContextElement(RetryCallback) {
companion object Key : CoroutineContext.Key<RetryCallback>
}
紧接着,当网络请求发生异常时借助CoroutineExceptionHandler获取到我们需要重新执行的操作:
val coroutineExceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
val callback = coroutineContext[RetryCallback]
?.callback
}
紧接着,要将coroutineExceptionHandler添加到发起网络请求的协程上下文里:
viewModelScope.launch(exceptionHandler
+ RetryCallback { request() }) {
request()
}
此时,只要在发起网络请求的页面里获取到callback,并在点击重试按钮的时候调用它,就能实现重试的逻辑。
进一步对它进行封装并增加失败后自动重试逻辑,创建供ViewModel使用的接口,用来处理网络请求错误的后续逻辑:
interface ViewModelFailed {
/**
* @param throwable:异常信息
* @param callback:需要重试的函数
* */
fun requestFailed(throwable: Throwable, callback: () -> Unit)
}
为它创建扩展函数,用来创建CoroutineExceptionHandler和RetryCallback上下文实例:
/**
* @param autoReTry:是否自动重试
* @param callback:需要重试的函数
* */
fun ViewModelFailed.initRetry(autoReTry: Boolean = false, callback: () -> Unit) =
CoroutineExceptionHandler { coroutineContext, throwable ->
val retryCallBack = {
coroutineContext[RetryCallback]
?.callback?.invoke()
}
if (autoReTry) {
//自动开始重试逻辑
onRetry()
retryCallBack.invoke()
} else {
//不自动开始重试,后续操作交给用户决定
requestFailed(throwable) {
retryCallBack
}
}
} + RetryCallback(callback)
ViewModel需要实现ViewModelFailed接口,并在发起网络请求的协程中调用initRetry方法添加异常处理上下文:
class MainViewViewModel : ViewModel(), ViewModelFailed {
val liveData: MutableLiveData<BaseData> = MutableLiveData()
/**
* @param num:用来演示Request请求数据
* @param repeat:失败后自动重试的次数
* */
fun request(num: Int, repeat: Int = 0) {
liveData.value = BaseData.loading()
viewModelScope.launch(initRetry(repeat > 0) {
request(num,repeat - 1)
}) {
liveData.value = BaseData.success(simulateHttp(num))
}
}
private suspend fun simulateHttp(num: Int) = withContext(Dispatchers.IO) {
//模拟网络请求
...
}
override fun requestFailed(throwable: Throwable, callback: () -> Unit) {
//处理失败逻辑
dialog()
//重试
callback.invoke()
}
override fun onRetry() {
}
}
总结
写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的可扫描下方二维码免费获取。
还有免费的高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**
最后,文中资料太大没法放上来。需要的朋友们如果需要可扫描下方二维码免费获取。
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。
作者:白瑞德
链接:https://juejin.cn/post/7080826259785121822
边栏推荐
- 华为推送服务内容,阅读笔记
- MySQL --- 数据库查询 - 排序查询、分页查询
- 一网打尽异步神器CompletableFuture
- 4年工作经验,多线程间的5种通信方式都说不出来,你敢信?
- Don't know these four caching modes, dare you say you understand caching?
- Shuttle INKWELL & ink components
- 【公开课预告】:视频质量评价基础与实践
- 这18个网站能让你的页面背景炫酷起来
- redis6数据类型及操作总结
- leetcode 10. Regular Expression Matching 正则表达式匹配 (困难)
猜你喜欢
Godson 2nd generation burn PMON and reload system
Solve the problem of invalid uni app configuration page and tabbar
Shandong University Summer Training - 20220620
Talk about seven ways to realize asynchronous programming
Don't know these four caching modes, dare you say you understand caching?
Binder communication process and servicemanager creation process
go 数组与切片
The real king of caching, Google guava is just a brother
“百度杯”CTF比赛 九月场,Web:Upload
嵌入式软件架构设计-消息交互
随机推荐
With 4 years of working experience, you can't tell five ways of communication between multithreads. Dare you believe it?
【 script secret pour l'utilisation de MySQL 】 un jeu en ligne sur l'heure et le type de date de MySQL et les fonctions d'exploitation connexes (3)
What about data leakage? " Watson k'7 moves to eliminate security threats
Idea set method annotation and class annotation
asp. Net read TXT file
go 数组与切片
53. 最大子数组和:给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
【每日一题】1200. 最小绝对差
Fragmented knowledge management tool memos
多人合作项目查看每个人写了多少行代码
嵌入式软件架构设计-消息交互
Godson 2nd generation burn PMON and reload system
Android本地Sqlite数据库的备份和还原
【Hot100】34. Find the first and last positions of elements in a sorted array
MySQL --- 数据库查询 - 排序查询、分页查询
The development of speech recognition app with uni app is simple and fast.
Laravel framework operation error: no application encryption key has been specified
Multi person cooperation project to see how many lines of code each person has written
Solve the problem of "unable to open source file" xx.h "in the custom header file on vs from the source
Huawei push service content, read notes