前言

在Kotlin协程(后简称协程)出来之后,颠覆了我们很多工具类的封装方式,大大简化了我们很多api的调用,并且使异步操作逻辑更清晰了

其中一个很标志性的地方就属网络请求了,以前的网络请求方式声明很麻烦,请求和响应也很麻烦,总结一句话就是啰嗦且易出错

ps:最终的使用方式示例:

使用网络请求的进化过程:

1.从一开始的HttpURLConnection的一把梭,直接一个请求写一串代码(代码太多就不写了)

2.到后续使用三方封装的网络框架,如XUtils,Volly,OkHttp等,这些虽然也简化了操作,但也是很复杂,比如封装了两层的OkHttp可能还需要以下方式声明和调用:

然后在一个或多个回调中处理返回的数据并判断是否是请求成功,解析成响应的数据并操作

3.后来Retrofit出现,解救了苦于网络请求太啰嗦的万千开发们,其使用动态代理加良好的封装,使网络请求声明和请求都变得更简单了

ps:Retrofit如何使用动态代理参考:模仿Retrofit封装一个使用更简单的网络请求框架_lt的博客-CSDN博客_retrofit封装网络请求

pps:进一步简化Retrofit的声明参考:更易于使用的Retrofit(不用写注解)_lt的博客-CSDN博客

声明如下(其会在调用的时候自动解析成一个post请求的Call对象):

使用如下:

可以发现虽然声明简单了很多,但使用还是比较麻烦

这时候就轮到协程登场了

正文

如果是使用Retrofit配合协程简化网络请求参考我以前写的文章第二条(封装网络请求):Kotlin协程在项目中的实际应用_lt的博客-CSDN博客_kotlin协程使用

否则可以看下面的:

接下来就是重头戏:用协程兼容你的老项目和旧的网络请求方式,并且请求方式就像这张图一样

首先我们可以观察一下我们的旧方式的请求,发现可能是有一个统一的回调,将服务端返回的String返回来,可能类似情况1:

也可能使用OkHttp,类似情况2:

也可能类似情况3:

可以观察到,他们都是基于回调,而协程可以将回调转成同步的挂起函数,所以我们将第三种方式(其他情况也类似),改造代码如下:

interface BaseHttp {//网络请求成功后调用该方法fun onHttpSuccess(data: String?)//网络请求失败后调用该方法fun onHttpFailed(message: String?)
}/*** creator: lt  lt.dygzs@qq.com* effect : 使用协程封装基于回调的网络请求* warning:*/
abstract class CoroutineHttp<T : Any> : BaseHttp {private var continuation: CancellableContinuation<Result>? = null//协程对象,如果getX调用慢于回调用private var result: Result? = null//响应结果,如果getX先与回调用//网络请求成功后调用该方法
//2*********************************override fun onHttpSuccess(data: String?) {//保证线程安全handler.post {val continuation = continuationif (continuation == null) {result = Result(data, true)return@post}continuation.resume(Result(data, true))}}//网络请求失败后调用该方法override fun onHttpFailed(message: String?) {handler.post {val continuation = continuationif (continuation == null) {result = Result(message, false)return@post}continuation.resume(Result(message, false))}}/*** 检查协程网络请求的返回值和当前状态是否符合要求* 如果不符合要求,会回调失败的接口,并返回null,不会取消当前协程* [errorListener]msg:服务端返回的消息或异常信息,如果不传使用默认失败策略(即弹toast)*/
//1*********************************suspend fun getOrNull(errorListener: (msg: String?) -> Unit = HttpErrorListener): T? {//保证线程安全val json = withContext(Dispatchers.Main) {val result = suspendCancellableCoroutine<Result> { continuation ->val result = resultif (result != null) {
//4*********************************continuation.resume(result)return@suspendCancellableCoroutine}this@CoroutineHttp.continuation = continuation}
//3*********************************return@withContext if (result.isSuccess) result.data else {errorListener(result.data)null}}return withContext(Dispatchers.Default) {Gson().fromJson(json,this@CoroutineHttp.getSuperClassTypes().first())}}/*** 检查协程网络请求的返回值和当前状态是否符合要求* 如果不符合要求,会回调失败的接口,并取消当前协程* [errorListener]msg:服务端返回的消息或异常信息,如果不传使用默认失败策略(即弹toast)*/suspend fun get(errorListener: (String?) -> Unit = HttpErrorListener): T =getOrNull(errorListener) ?: cancelAndThrow()//获取协程数据,并自动显示和隐藏弹窗,并自动转换成响应类型,如果请求失败或转换失败则返回nullsuspend fun getOrNullWithDialog(errorListener: (String?) -> Unit = HttpErrorListener): T? {val baseActive = coroutineContext[CoroutineElementWithBaseActive]?.baseActivebaseActive?.showWaitDialog()try {return getOrNull(errorListener)} finally {baseActive?.dismissWaitDialog()}}//    获取协程数据,并自动显示和隐藏弹窗,并自动转换成响应类型,如果请求失败或转换失败则取消协程suspend fun getWithDialog(errorListener: (String?) -> Unit = HttpErrorListener): T =getOrNullWithDialog(errorListener) ?: cancelAndThrow()class Result(val data: String?, val isSuccess: Boolean)private object HttpErrorListener : (String?) -> Unit {override fun invoke(p1: String?) {p1.showToast()}}/*** 取消并抛出异常*/suspend fun cancelAndThrow(): Nothing = coroutineContext.job.cancelAndThrow()/*** 获取super的泛型的type列表*/
fun Any.getSuperClassTypes(): Array<Type> =this::class.java.genericSuperclass.asT<ParameterizedType>().actualTypeArguments
}private val handler = Handler(Looper.getMainLooper())

原理大致就是将协程内部的回调给保存下来(挂起),然后在适当的时间调用协程的resume使协程恢复执行

可以看代码标识1处,其他的getxxx方法都会走到这个getOrNull方法中,其先调用suspendCancellableCoroutine方法将协程挂起并拿到内部的回调

然后再看代码标识2处,网络请求回调成功后会走这个onHttpSuccess方法,然后我们判断是否已经挂起了协程(注册了协程的回调):

1.如果挂起了,就调用resume恢复协程的运行,此时代码会执行到标识3处,且将result传了过去,然后后续就执行解析json并判断是否成功等耦合项目的逻辑

2.如果没有挂起,就将数据保存在类中,然后会在调用getxxx方法时检测到有数据并直接恢复协程(标识4处)

然后比如网络请求这样声明:

最后我们就可以使用协程请求网络了(红框的位置相当于getWithDialog方法的成功回调):

也可以在方法后面传入回调处理请求失败的情况(红框位置是成功的回调,黄框位置是失败的回调):

使用协程和gson解析需要导入以下的依赖:

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'implementation 'com.google.code.gson:gson:2.8.7'

扩展

1.CoroutineHttp为什么要声明为抽象类?

因为jvm的泛型擦除,如果你不创建出新的Class,就无法获取他设置的泛型的Type,而使用抽象类,你每new一个匿名内部类都会创建出一个Class,所以可以通过反射获取其声明的泛型的Type

当然open class也可以写出匿名内部类,但是没有了硬性规定容易出现遗漏导致出现bug

2.mainScope是什么

mainScope是一个自定义的协程作用域(CoroutineScope),创建它可以参考这一篇Kotlin-如何创建一个好用的协程作用域_lt的博客-CSDN博客

一般在activity中需要在onCreate中创建,在onDestroy中调用cancel()来取消它所有启动的协程

如果在fragment中就在onCreate中创建,在onDestroyView中调用cancel()

如果有疑问可以评论区留言

end

超简单-用协程简化你的网络请求吧,兼容你的老项目和旧的网络请求方式相关推荐

  1. Kotlin学习——简单运用协程网络下载图片并更新到UI

    kotlin学习 协程Coroutines学习 简单小Demo:通过协程下载一张网络图片并显示出来 文章目录 kotlin学习 前言 一.如何开启一个协程? 二.如何在项目中使用协程 增加对 Kotl ...

  2. python 协程 多线程_python进阶之多线程(简单介绍协程)

    多线程 线程:实现多任务的另一种方式 一个进程中,也经常需要同时做多件事,就需要同时运行多个'子任务',这些子任务,就是线程 线程又被称为轻量级进程(lightweight process),是更小的 ...

  3. 简单总结协程Coroutine及Yield常见用法

    原文地址:http://blog.csdn.net/qq_18995513/article/details/51944602 最近学习协程Coroutine,参考了别人的文章和视频教程,感觉协程用法还 ...

  4. 简单unity协程优化方案

    做三消项目需要做一个提示用户哪个格子可以消除的功能,需要对整个棋盘进行遍历. 原来没用协程的代码 public void GetTip() { GameObject go1, go2;int[] wa ...

  5. python协程异步原理_简单介绍Python的Tornado框架中的协程异步实现原理

    Tornado 4.0 已经发布了很长一段时间了, 新版本广泛的应用了协程(Future)特性. 我们目前已经将 Tornado 升级到最新版本, 而且也大量的使用协程特性. 很长时间没有更新博客, ...

  6. ucontext-人人都可以实现的简单协程库

    1.干货写在前面 协程是一种用户态的轻量级线程.本篇主要研究协程的C/C++的实现. 首先我们可以看看有哪些语言已经具备协程语义: 比较重量级的有C#.erlang.golang* 轻量级有pytho ...

  7. python协程(超详细)

    1.迭代 1.1 迭代的概念 使用for循环遍历取值的过程叫做迭代,比如:使用for循环遍历列表获取值的过程 # Python 中的迭代 for value in [2, 3, 4]:print(va ...

  8. 【Python核心】揭秘Python协程

    首先要明白什么是协程? 协程是实现并发编程的一种方式.一说并发肯定想到了多线程/多进程模型,多线程/多进程正是解决并发问题的经典模型之一 先从一个爬虫实例出发,用清晰的思路并且结合实战来搞懂这个不算特 ...

  9. Kotlin极简教程:第9章 轻量级线程:协程

    原文链接:https://github.com/EasyKotlin 在常用的并发模型中,多进程.多线程.分布式是最普遍的,不过近些年来逐渐有一些语言以first-class或者library的形式提 ...

最新文章

  1. JavaScript计算汉明距离(HammingDistance)
  2. 数据结构 线性存储 -- 栈 讲解
  3. docker安装教程(windows和linux[centos8])
  4. C#一元运算重载的深入理解
  5. get到一个生气后的牵手方式!太可爱了
  6. [C++11]弱引用智能指针weak_ptr初始化和相关的操作函数
  7. 设置vs2008代码区的背景色
  8. 背景图片适应屏幕百分百
  9. Virtualenv 相关
  10. 云图说 | 华为云应用服务网格,让你的应用治理智能化、可视化
  11. windosw启动redis
  12. Ultra-QuickSort(离散化)
  13. 在一台机器设置两个listener(Oracle)
  14. 动态规划系列-连续的子数组和(leetcode523)
  15. R_ggplot2地理信息可视化_史上最全(二)
  16. 华为odjava机试题_手心里的咕咕机,华为手机大小,性价比赶超小米的学习打印机...
  17. [Java] 使用HttpClient实现文件下载
  18. wps怎么删除空白页?你学会了吗?
  19. 你知道什么是大数据的核心吗?
  20. android多点触控的理解

热门文章

  1. NTU 21fall-CE 7454(deep learning for data science)笔记
  2. pytorch笔记: 搭建Skip—gram
  3. oracle in与exists的使用
  4. 判断字符串 正则_(重学前端 - JavaScript(模块一)) 14、引用类型之 RegExp (正则)(详述)...
  5. 正则提取编码解码问题
  6. Python学习前期准备---第九天
  7. 菜鸟、普通、老鸟程序猿如何写奇数判断?--位操作符妙用
  8. 利用solr的 DataImportHandler从mysql数据库建立索引
  9. java代码实现python2中aes加密经历
  10. How to allow/block PING on Linux server – IPTables rules for icmp---reference