okHttp总结(二)

483 阅读4分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

接上篇 okHttp总结(一) 继续总结okHttp的用法

一、拦截器

拦截器的源码分析

okHttp最核心的工作就是在getResponseWithInterceptorChain()中,我们先看源码

@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors
    interceptors += RetryAndFollowUpInterceptor(client)
    interceptors += BridgeInterceptor(client.cookieJar)
    interceptors += CacheInterceptor(client.cache)
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
        interceptors += client.networkInterceptors
    }
    interceptors += CallServerInterceptor(forWebSocket)
        . . .
        try {
            val response = chain.proceed(originalRequest)
            if (isCanceled()) {
                response.closeQuietly()
                throw IOException("Canceled")
            }
            return response
        } catch (e: IOException) {
            . . .
        } finally {
            . . .
        }
}

补充之前 RetryAndFollowUpInterceptor 的源码

  1. RetryAndFollowUpInterceptor

    主要完成两件事情:重试与重定向

    (1) 重试

override fun intercept(chain: Interceptor.Chain): Response {
    . .
    while (true) {
        call.enterNetworkInterceptorExchange(request, newExchangeFinder)
            . . .
            try {
                . . .
                try {
                    . . .
                } catch (e: RouteException) {
                    // 路由器异常,连接未成功,请求还没有发出去
                    if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
                        throw e.firstConnectException.withSuppressed(recoveredFailures)
                    }
                        . . .
                        newExchangeFinder = false
                    continue
                } catch (e: IOException) {
                    // 请求发出去了,但是和服务器通信失败了(Socket流正在读写数据的时候断开连接)
                    if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
                        throw e.withSuppressed(recoveredFailures)
                    }
                        . . .
                        newExchangeFinder = false
                    continue
                }
            } finally {
                call.exitNetworkInterceptorExchange(closeActiveExchange)
            }
    }
}
两个异常都是根据recover方法判断是否能够进行重试,如果返回true,表示允许重试
private fun recover(e: IOException,call: RealCall,userRequest:         Request,requestSendStarted: Boolean): Boolean {
    //在配置okHttpClient设置了不允许重试(默认允许)
    if (!client.retryOnConnectionFailure) return false
    // We can't send the request body again.
    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
    // 判断是不是属于重试异常
    if (!isRecoverable(e, requestSendStarted)) return false
    // No more routes to attempt.
    if (!call.retryAfterFailure()) return false
    // For failure recovery, use the same route selector with a new connection.
    return true
}

(2) 重定向

  如果请求结束后没有发生异常并不代表当前获得的响应就是最终交给用户的,还需要进一步来判断是否需要重定向的判断

  整个需要重定向的判断内容很多,如果此方法返回空,表示不需要再重定向了,直接返回响应,但是如果返回非空,需要重新请求返回的Request

(2) 总结

   重试的前提是出现了RouteException或者IOException,一旦在后续的拦截器中执行过程中出现这两个异常,就会通过recover方法进行判断是否进行连接重试
   重定向发生在重试的判断之后,如果不满足重试条件,还需要进一步调用followUpRequest,根据Response的响应码(如果请求失败,Response都不存在就会抛出异常)
   

2. 自定义拦截器

   实现Interceptor接口,重写 intercept(Interceptor.Chain chain)方法

   调用 Response response = chain.proceed(request); 传递给下一层拦截器获取他的返回结果
   
public class LogInterceptor implements Interceptor {
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        //此三行代码是每个自定义拦截器中必须的
        Request request = chain.request();
        Response response = chain.proceed(request);
        return response;
    }
}
  1. 总结
  • 创建一个数组

  • 添加ApplicationInterceptor应用拦截器(请求开始之前最早被调用)

  • 创建一个RetryAndFollowUpInterceptor拦截器(用于请求的重定向操作以及请求失败后像路由错误、IO异常等的失败的重试)

  • 创建一个BridgeIntercepto拦截器并添加(用于添加一些必要的请求头信息、gzip处理等)

  • 创建一个CacheInterceptor拦截器并添加(用于是对缓存进行判断是否存在和有效,然后读取缓存,更新缓存等)

  • 创建一个ConnectInterceptor拦截器并添加(若前面缓存无效,则创建与服务端建立Socket连接、发起与服务端TCP连接、服务端建立TCP连接、TLS握手等逻辑处理)

  • 添加NetworkInterceptor网络拦截器(组装完成请求之后,真正发起请求之前被调用)

  • 创建一个CallServerInterceptor拦截器并添加(用于向服务器发起真正的网络请求)

  • 通过RealInterceptorChain#proceed(Request)来执行整个拦截器数组的第一个拦截器,然后每一个拦截器处理完毕后再次调用此方法进行下一拦截器的执行

二、 okHttp访问https

1. 证书

  • okhttp信任所有证书(不建议使用)

  • okhttp使用自签证书(建议)

    将证书(一般是cer结尾的文件)放到工程的assets里面
    
    将证书的流数据传入生成SSLSocketFactory
    
    对okhttp进行设置
    

    1. 证书 加密 (可以定义okHttp拦截器 )

  • 先定义一个拦截器的实现

  • okHttp加入该拦截器

  • 加密解密

  • 注意:

      和接口无关的新加的数据放在请求头里
      该close的要close,不然会内存泄漏。
      新旧RequestResponse要区分好,新的要替换旧的去传递或返回。
      要对response.code()做处理,只有在和后台约定好的返回码下才走
    

    二、 用到的设计模式

1. 责任链模式

   主要体现就是拦截器的使用

2. 建造者模式

   主要用处是将对象的创建与表示相分离,用Builder组装各项配置

3. 单例模式

   首先,创建OkHttpClient对象的时候,就推荐使用单例模式,防止创建多个OkHttpClient对象,损耗资源