优雅使用Retrofit,在协程时代遨游安卓网络请求(二)上

简介: 优雅使用Retrofit,在协程时代遨游安卓网络请求(二)

前言:由于框架本身也在不断地迭代,因此文章中的部分代码可能存在更新或者过时,如果你想阅读源码或者查看代码的在项目中的实际使用方法,可以查看笔者目前在维护的compose项目:Spacecraft: 《Spacecraft - 我的安卓技术实践平台》-查看代码请进入develop分支 (gitee.com)


关于上一篇的一些补全


  在上一篇文章中,笔者提出的NetworkResult缺乏了部分关键代码,首先我们重新回顾这部分代码。


sealed class NetworkResult<T> {
    /**
     * 网络请求成功
     */
    class Success<T>(private val response: Response<T>) : NetworkResult<T>(){}
    /**
     * 网络请求失败
     */
    sealed class Failure<T> : NetworkResult<T>() {
        /**
         * 服务器内部错误
         */
        data class ServerError<T>(private val response: Response<T>) : Failure<T>() {}
        /**
         * 网络请求出现异常
         */
        data class Exception<T> constructor(
            val exception: Throwable
        ) : Failure<T>() {}
    }
}

  可以看到,Response转成NetworkResult的过程,实际上就是用NetworkResultResponse一部分或者全部包裹起来的过程,当然我们并不希望将原始的Response暴露给调用者,我们希望做一些接口上的屏蔽,把对Response的取值交给接口属性,接下来定义一套接口。


private interface ResponseGetter {
    //http状态码
    val code: Int
    //响应头
    val headers: Headers
    //请求Url(部分场景用于判断)
    val url: String
}

注:可以扩充你希望得到的属性

  接下来,让Result子类实现这个接口,都用lazy来委任取值(避免反复调用取值函数降低性能),需要注意的是Success和ServerError分别拥有一个单独的属性:responseBody和ResponseErrorMessage。

  responseBody是接口的返回值对应的泛型类型,responseErrorMessage则是服务器内部错误的信息,一般是一个HTML(具体看后台框架)


/**
 * 网络请求成功
 */
class Success<T>(private val response: Response<T>) : NetworkResult<T>(), ResponseGetter {
    val responseBody by lazy { response.body()!! }
    override val code by lazy { response.code() }
    override val headers: Headers by lazy { response.headers() }
    override val url by lazy { response.raw().request.url.toString() }
}
/**
 * HTTP协议错误
 */
data class ServerError<T>(val response: Response<T>) : Failure<T>(), ResponseGetter {
    val responseErrorMessage: String by lazy { response.errorBody()?.string().orEmpty() }
    override val code by lazy { response.code() }
    override val headers: Headers by lazy { response.headers() }
    override val url by lazy { response.raw().request.url.toString() }
}

&nmsp; 因为Exception并没有Response,所以我们不需要实现接口方法,独立给他增加一个异常信息。所谓的“异常信息”并不是指异常本身的错误堆栈,这些堆栈是给程序员阅读的,用户并不知道是什么含义,所以我们需要针对特定的异常去翻译一套用户能够识别的错误信息,例如将网络中断异常翻译成“Network Error”。至于如何实现我们下文讲解。


/**
 * 网络请求出现异常
 */
data class Exception<T>(val exception: Throwable) : Failure<T>() {
    //分析异常类型,返回自然语言错误信息
    val exceptionMessage:String by lazy {
        //TODO 下文讲解
    }
}

好了,上一篇的坑已经补完了,我们接下来继续讲解实际开发中会遇到的问题。


实际开发中会遇到的问题


  这些问题的解决过程肯定离不开okhttp(毕竟框架底层就是okhttp),因此笔者希望你对okhttp有一定的理解,特别是拦截器的层面。


公共参数


  几乎所有的项目都会遇到添加公参的问题,这里以POST请求为例,讲解一下如何添加公参:众所周知,POST请求的请求体是放在body中的,因此我们只需要以下几步:

  1. 新增一个okhttp拦截器
  2. 通过chain获取request,通过request获取到原始的body,将body中的字节流转成字符串或者其他格式(例如JSON,这个具体看你们公司项目)
  3. 对转换后的对象进行添加公参操作,例如JSON就是添加一些字段
  4. 将对象转回request,通过chain传递到下一个拦截器中

  依然是废话不多说,看代码!


/**
 * 公参基类,重写方法来增加公参
 */
abstract class BaseCommonParamsInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originRequest = chain.request()
        //1.body转成json,如果body为空则构建空json,只填充公参
        val jsonBody = (originRequest.body?.toJson() ?: JSONObject()).apply {
            //2.添加公参
            addCommonParams()
        }
        //3.构建新的requestBody,传递给下一个拦截器
        return chain.proceed(originRequest.newBuilder().apply {
            method(
                originRequest.method,
                jsonBody.toString().toRequestBody(originRequest.body?.contentType())
            )
        }.build())
    }
    //公参
    protected abstract fun getCommonParams(): Map<String, String>
    //给JSON添加参数
    private fun JSONObject.addCommonParams() {
        getCommonParams().forEach { entry ->
            put(entry.key, entry.value)
        }
    }
}
//将RequestBody中的字节流转成字符串
fun RequestBody.string(): String {
    val buffer = Buffer()
    writeTo(buffer)
    return buffer.readUtf8()
}
fun RequestBody.toJson(): JSONObject {
    return string().toJSONObject()
}

  originRequest会被转成Json对象,然后对json对象进行添加公参,逻辑非常简单。需要注意的是RequestBody.string()这个扩展方法,通过它转成字符串后,你就可以根据你们公司的请求格式来转成其他实体类了,笔者演示的是转成json。

  接下来重写,然后添加到okhttp的构造方法链中就好了。


class MyCommonParamsInterceptor : BaseCommonParamsInterceptor() {
    override fun getCommonParams(): Map<String, String> = CommHttpParams.getInstance().urlParamsMap
}

  实际上大部分的定制逻辑(接近99%),都可以通过添加拦截器来实现,okhttp的拦截器真的是神器。

image.png



相关文章
|
缓存 JSON API
XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装
XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装
669 0
XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装
|
数据安全/隐私保护 Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(二)下
优雅使用Retrofit,在协程时代遨游安卓网络请求(二)
123 0
|
Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(一)下
优雅使用Retrofit,在协程时代遨游安卓网络请求(一)
75 0
|
自然语言处理 程序员 Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)上
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)
108 0
|
API Android开发 Kotlin
优雅使用Retrofit,在协程时代遨游安卓网络请求(一)上
优雅使用Retrofit,在协程时代遨游安卓网络请求(一)
90 0
|
API Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)下
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)
63 0
|
JSON 缓存 前端开发
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约2
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约
209 0
|
缓存 JSON 网络协议
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约1
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约
271 0
|
网络协议 Java
OkHttp架构—异步请求enqueue(不完整篇)
我分为了四个部分,橙色第一部分实例化一个OkHttoClient类对象就可以了。 所有的逻辑大部分在拦截器Interceptors中,但进入拦截器之前还要靠分发器来调配请求任务。 分发器Dispatcher:内部维护队列和线程池,完成请求调配。 拦截器Interceptors:完成整个请求。
340 0
|
网络协议 安全 Python
【Tornado】协程队列和异步DNS解析器在Tornado项目里的实战表现已经运用详解
【Tornado】协程队列和异步DNS解析器在Tornado项目里的实战表现已经运用详解
160 2