RxHttp ,比Retrofit 更优雅的协程体验 ,面试复盘

 .await()

if (response.code == 200) {
//拿到data字段(Student)刷新UI
} else {
//拿到msg字段给出错误提示
}


试想一下,一个项目少说也有30+个这样的接口,如果每个接口读取这么判断,就显得不够优雅,也可以说是灾难,相信也没有人会这么干。而且对于UI来说,只需要data字段即可,错误提示啥的我管不着。

那有没有什么办法,能直接拿到data字段,并且对code做出统一判断呢?有的,直接上代码

val student = RxHttp.get("/service/…")
.toResponse() //调用此方法,直接拿到data字段,也就是Student对象
.await()
//直接开始更新UI


可以看到,这里调用了`toResponse()`方法,就直接拿到了data字段,也就是Student对象。

此时,相信很多人会有疑问,

*   业务code哪里判断的?
    
*   业务code非200时,msg字段怎么拿到?
    

为此,先来回答第一个问题,业务code哪里判断的?

其实`toResponse()`方法并不是RxHttp内部提供的,而是用户通过自定义解析器,并用`@Parser`注解标注,最后由注解处理器`rxhttp-compiler`自动生成的,听不懂?没关系,直接看代码

@Parser(name = “Response”)
open class ResponseParser : AbstractParser {

//以下两个构造方法是必须的
protected constructor() : super()
constructor(type: Type) : super(type)

@Throws(IOException::class)
override fun onParse(response: okhttp3.Response): T {
    val type: Type = ParameterizedTypeImpl[Response::class.java, mType] //获取泛型类型
    val data: Response<T> = convert(response, type)   //获取Response对象
    val t = data.data                             //获取data字段
    if (data.code != 200 || t == null) { //code不等于200,说明数据不正确,抛出异常
        throw ParseException(data.code.toString(), data.msg, response)
    }
    return t
}

}


上面代码只需要关注两点即可,

第一点,我们在类开头使用了`@Parser`注解,并为解析器取名为`Response`,所以就有了`toResponse()`方法(命名方式为:to + Parser注解里设置的名字);

第二点,我们在`if`语句里,对code做了判断,非200或者data为空时,就抛出异常,并带上了code及msg字段,所以我们在异常回调的地方就能拿到这两个字段

接着回答第二个问题,code非200时,如何拿到msg字段?直接上代码,看一个使用协程发送请求的完整案例

//当前环境在Fragment中
fun getStudent() {
//rxLifeScope在rxLife-coroutine库中,需要单独依赖
rxLifeScope.launch({ //通过launch方法开启一个协程
val student = RxHttp.get("/service/…")
.toResponse()
.await()
}, {
//异常回调,这里的it为Throwable类型
val code = it.code
val msg = it.msg
})
}


**注:RxLifeScope 是 [RxLife-Coroutine]( )库中的类,本文后续会详细介绍**

上面的代码,在异常回调中便可拿到code及msg字段,需要注意的是,`it.code`及`it.msg`是我为Throwable类扩展的两个属性,代码如下:

val Throwable.code: Int
get() {
val errorCode = when (this) {
is HttpStatusCodeException -> this.statusCode //Http状态码异常
is ParseException -> this.errorCode //业务code异常
else -> “-1”
}
return try {
errorCode.toInt()
} catch (e: Exception) {
-1
}
}

val Throwable.msg: String
get() {
return if (this is UnknownHostException) { //网络异常
“当前无网络,请检查你的网络设置”
} else if (
this is SocketTimeoutException //okhttp全局设置超时
|| this is TimeoutException //rxjava中的timeout方法超时
|| this is TimeoutCancellationException //协程超时
) {
“连接超时,请稍后再试”
} else if (this is ConnectException) {
“网络不给力,请稍候重试!”
} else if (this is HttpStatusCodeException) { //请求失败异常
“Http状态码异常”
} else if (this is JsonSyntaxException) { //请求成功,但Json语法异常,导致解析失败
“数据解析失败,请检查数据是否正确”
} else if (this is ParseException) { // ParseException异常表明请求成功,但是数据不正确
this.message ?: errorCode //msg为空,显示code
} else {
“请求失败,请稍后再试”
}
}


到这,业务code统一判断就介绍完毕,上面的代码,大部分人都可以简单修改后,直接用到自己的项目上,如`ResponseParser`解析器,只需要改下`if`语句的判断条件即可

2.4、操作符介绍
---------

### map 转换符号

`map`操作符很好理解,RxJava及协程的Flow都有该操作符,功能都是一样,用于转换对象,如下:

val student = RxHttp.postForm("/service/…")
.toStr()
.map { it.length } //String转Int
.tryAwait() //这里返回 Student? 对象,即有可能为空


### timeout 超时

OkHttp提供了全局的读、写及连接超时,有时我们也需要为某个请求设置不同的超时时长,此时就可以用到RxHttp的`timeout(Long)`方法,如下:

val student = RxHttp.postForm("/service/…")
.toResponse()
.timeout(3000) //超时时长为3s
.await()


### retry 失败重试

OkHttp为我们提供了全局的失败重试机制,然而,这远远不能满足我们的需求,比如,我就部分接口需要失败重试,而不是全局的;我需要根据某些条件来判断是否需要重试;亦或者我需要周期性重试,即间隔几秒后重试等等

如我们需要在网络出现异常时,重试2次,每次间隔1秒,代码如下:

val student = RxHttp.postForm("/service/…")
.toResponse()
.retry(2, 1000) { //重试2次,每次间隔1s
it is ConnectException //如果是网络异常就重试
}
.await()


`retry()`方法共有3个参数,分别是重试次数、重试周期、重试条件,都有默认值,3个参数可以随意搭配,如下:

/**

  • 失败重试,该方法仅在使用协程时才有效
  • @param times 重试次数, 默认Int.MAX_VALUE 代表不断重试
  • @param period 重试周期, 默认为0, 单位: milliseconds
  • @param test 重试条件, 默认为true,只要出现异常就重试
    */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值