@

前言

Retrofit 从 2.6.0 版本开始, 内置了对 Kotlin Coroutines 的支持. 我们统一处理异常及响应状态码, 使用DSL 让代码更加漂亮整洁

先导包:

//协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3' // Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.2.0"

一、准备工作

1.BaseResp

统一的返回实体, 包含 code, msg, data; 这种服务器响应结构比较常见

data class BaseResp<T>(
// 响应状态码
var code: Int = -1,
// 响应信息
var msg: String = "",
// 数据实体
var data: T? = null
)

2.Api

这里是一个验证码请求接口, 使用的 Json 提交参数

interface Api {
@POST("api/getCode")
suspend fun getCode(@Body body: RequestBody): BaseResp<JsonObject?>
}

3.ApiManager

使用 枚举单例模式; 包括 初始化 Retrofit, OkHttpClient, 添加请求Token, 请求日志打印.

/**
* Retrofit 管理类;
*/
enum class ApiManager {
INSTANCE;
private val retrofit: Retrofit
val mApi: Api
private val mMediaTypeJson: MediaType? init {
retrofit = Retrofit.Builder()
.baseUrl("https://... 服务器地址")
.client(initOkhttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build()
mApi = retrofit.create(Api::class.java)
mMediaTypeJson = "application/json; charset=utf-8".toMediaTypeOrNull()
} private fun initOkhttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.retryOnConnectionFailure(true) //重试
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addNetworkInterceptor(initTokenInterceptor())
.addInterceptor(initLogInterceptor())
.build()
} private fun initTokenInterceptor(): Interceptor = object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
// 为请求添加 Token
val token: String = SpUtils.getToken()
val originalRequest = chain.request()
return if (token.isNullOrEmpty()) {
chain.proceed(originalRequest)
}else {
token = "Bearer $token"
val updateRequest = originalRequest.newBuilder().header("token", token).build()
chain.proceed(updateRequest)
}
}
} /*
* 日志拦截器
* */
private fun initLogInterceptor(): HttpLoggingInterceptor {
if(!BuildConfig.DEBUG){ //生产环境不打日志
return HttpLoggingInterceptor()
} val interceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Log.i("Retrofit_network", message)
}
})
interceptor.level = HttpLoggingInterceptor.Level.BODY
return interceptor
} // 请求数据转 Json 形式;
open fun getJsonBody(data: Any): RequestBody {
val strEntity = mGson.toJson(data)
return strEntity.toRequestBody(mMediaTypeJson)
}
}

二、开始使用

1.简单使用

以下代码在 Activity 中;

lifecycleScope.launch {
val param = ApiManager.INSTANCE.getJsonBody(mapOf("phone" to "13333333333", "codeType" to "1"))
val data = ApiManager.INSTANCE.mApi.getCode(param).data
binding.tvTitle.text = data.toString()
}

是不是非常简单, 协程内除了准备参数 就只剩两行代码.

上面代码并没有处理异常, 及请求失败的情况. 下面我们就开始封装请求

2.DSL

class RetrofitDSL<T> {
internal lateinit var api: (suspend () -> BaseResp<T?>)
private set
internal var onSuccess: ((BaseResp<T?>) -> Unit)? = null
private set
internal var onFailed: ((msg: String?, code: Int) -> Unit)? = null
private set
internal var onComplete: (() -> Unit)? = null
private set /**
* 获取数据
* @param block (T) -> Unit
*/
fun api(block: suspend () -> BaseResp<T?>) {
this.api = block
} /**
* 获取数据成功
* @param block (T) -> Unit
*/
fun onSuccess(block: (BaseResp<T?>) -> Unit) {
this.onSuccess = block
} /**
* 获取数据失败
* @param block (msg: String, errorCode: Int) -> Unit
*/
fun onFailed(block: (msg: String?, code: Int) -> Unit) {
this.onFailed = block
} /**
* 访问完成
* @param block () -> Unit
*/
fun onComplete(block: () -> Unit) {
this.onComplete = block
}
}

看到 onSuccess, onFailed 是不是有熟悉的感觉!

3.扩展函数

我们需要创建一个 .kt 文件; 为 协程作用域 添加扩展函数

fun <T> CoroutineScope.retrofit(
dsl: RetrofitDSL<T>.() -> Unit
) {
launch {
val retrofitDsl = RetrofitDSL<T>()
retrofitDsl.dsl()
try {
val result = retrofitDsl.api()
when(val code = result.code){
200 -> retrofitDsl.onSuccess?.invoke(result)
... // 其他响应码
else -> retrofitDsl.onFailed?.invoke(result.msg, code)
}
} catch (e: Exception) {
retrofitDsl.onFailed?.invoke(e.message, -10)
}
... // 其他异常类型
finally {
retrofitDsl.onComplete?.invoke()
}
}
}

这里 只写了 Exception; 需要判断多种异常的话, 往 catch 后面加就行了. 同样, 响应码也随便加, 想怎么处理就怎么处理.

4.请求发起

以下代码在 ViewModel 中;

// 注意: <JsonObject> 与 Api 中配置的请求响应结果类型一致 BaseResp<JsonObject?>
viewModelScope.retrofit<JsonObject> {
api { ApiManager.INSTANCE.mApi.getCode(param) }
onSuccess { // it = BaseResp<JsonObject?>
_number.set(it.data?.getAsJsonPrimitive("code")?.asString)
}
onFailed { msg, _ ->
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
}
onComplete {
Toast.makeText(getApplication(), "完事了", Toast.LENGTH_SHORT).show()
}
}

onSuccess, onFailed, onComplete 都可以不写, 想写谁就写谁;

// 不关心请求结果的话; 一个 api 就够了
viewModelScope.retrofit<JsonObject> {
api { ApiManager.INSTANCE.mApi.getCode(param) }
}

总结

好吧, 没有总结

Kotlin Coroutine(协程): 四、+ Retrofit的更多相关文章

  1. Kotlin Coroutine(协程): 一、样例

    @ 目录 前言 一.直接上例子 1.延时任务. 2.异步任务 3.并行任务: 4.定时任务: 总结 前言 你还在用 Hanlder + Message? 或者 AsyncTask? 你还在用 Rxja ...

  2. Kotlin Coroutine(协程): 二、初识协程

    @ 目录 前言 一.初识协程 1.runBlocking: 阻塞协程 2.launch: 创建协程 3.Job 4.coroutineScope 5.协程取消 6.协程超时 7.async 并行任务 ...

  3. Kotlin Coroutine(协程): 三、了解协程

    @ 目录 前言 一.协程上下文 1.调度器 2.给协程起名 3.局部变量 二.启动模式 CoroutineStart 三.异常处理 1.异常测试 2.CoroutineExceptionHandler ...

  4. python并发编程之gevent协程(四)

    协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...

  5. Coroutine(协程)模式与线程

    概念 协程(Coroutine)这个概念最早是Melvin Conway在1963年提出的,是并发运算中的概念,指两个子过程通过相互协作完成某个任务,用它可以实现协作式多任务,协程(coroutine ...

  6. Android中的Coroutine协程原理详解

    前言 协程是一个并发方案.也是一种思想. 传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高.但是面对计算密集型的任务不如多线程并行运算效率高. 不同的语言对于协程都有不同的实 ...

  7. coroutine协程

    如果你接触过lua这种小巧的脚本语言,你就会经常接触到一个叫做协程的神奇概念.大多数脚本语言都有对协程不同程度的支持.但是大多编译语言,如C/C++,根本就不知道这样的东西存在.当然也很多人研究如何在 ...

  8. [Unity-22] Coroutine协程浅析

    1.概念解释 协程并非一个独立的线程.在Unity中.全部的语句都是在一个线程中运行的,也就是说.Unity是单线程的(详细的能够參见http://blog.csdn.net/alexander_xf ...

  9. Coroutine 协程

    https://en.wikipedia.org/wiki/Coroutine Coroutines are computer program components that generalize s ...

随机推荐

  1. Day029 JDK8中新日期和时间API (四)

    JDK8中新日期和时间API 其他的一些API ZoneId:该类中包含了所有的时区信息,一个时区的ID,如 Europe/Paris ZonedDateTime:一个在ISO-8601日历系统时区的 ...

  2. 【源码解析】阿里在线诊断工具greys源码

    撸起袖子加油干!开门见山! 一.源码下载 下载代码: git clone https://github.com/oldmanpushcart/greys-anatomy.git 二.源码分析 2.1 ...

  3. 出现异常org.yaml.snakeyaml.parser.ParserException: while parsing a block mapping

    这是因为yaml的配置文件格式出错导致的异常 原代码,仔细看数据源的配置没有和type的路径一致,而是下一级的目录,所以导致出错 使用shift+tab快捷键向左移动改变下就好了 最后成功运行

  4. 机器学习算法之K近邻算法

    0x00 概述   K近邻算法是机器学习中非常重要的分类算法.可利用K近邻基于不同的特征提取方式来检测异常操作,比如使用K近邻检测Rootkit,使用K近邻检测webshell等. 0x01 原理   ...

  5. Redis限制一键登录次数

    一.产生背景 之前的随笔提到过项目中写了一键登录功能.上线后除了有时候网络波动会导致登陆失败,其他情况一直稳如老狗 しかし,邮件看到有人恶意刷一键登录,这年头闲的人可真闲啊, 只能思考如何搞一搞 二. ...

  6. 中国摄像头CMOS需求潜力旺盛

    中国摄像头CMOS需求潜力旺盛 CMOS是Complementary Metal Oxide Semiconductor(互补金属氧化物半导体)的缩写.它是指制造大规模集成电路芯片用的一种技术或用这种 ...

  7. 『动善时』JMeter基础 — 39、JMeter中如果(If)控制器详解

    目录 1.什么是逻辑控制器 2.如果控制器介绍 3.如果控制器的使用 (1)测试计划内包含的元件 (2)如果控制器界面内容 (3)HTTP请求界面内容 (4)运行结果 4.如果控制器中表达式的写法 ( ...

  8. Redis系列(四):地理信息

    除了Bitmaps和HyperLogLog,Redis还提供了关于地理空间的相关操作,主要是经纬度.Redis提供的地理信息操作是有误差的,最大误差可能会达到5%.Redis提供的Geo的操作如下: ...

  9. fail2ban防护ssh免于暴力破解

    一.背景 开放到公网的主机无时无刻不在遭受攻击,其中ssh暴力破解频率最高,会有无数机器不分日夜地搜索公网上的猎物,然后进行弱密码尝试 如果你的公网机器恰巧设的弱密码,估计刚装上系统,没过几小时别人就 ...

  10. Open C

    Open C UF  公共类型UF_ABORT   进度中断UF_ASSEMUF_ATTRUF_BOUNDUF_BREPUF_CAMUF_CFIUF_CGMUF_CLEARUF_CLONE      ...