Android小知识-剖析OkHttp中的五个拦截器(上篇)

本平台的文章更新会有延迟,大家可以关注微信公众号-顾林海,包括年底前会更新kotlin由浅入深系列教程,目前计划在微信公众号进行首发,如果大家想获取最新教程,请关注微信公众号,谢谢

前面几节介绍了OkHttp的同步和异步请求的整体流程以及Dispatcher分发器的作用,接下来介绍一下OkHttp中的五个拦截器。

RetryAndFollowUpInterceptor拦截器

RetryAndFollowupInterceptor是重试重定向拦截器,它的主要作用是负责失败重连。OkHttp中的重定向功能是默认开启的。

该拦截器方法如下:

    @Override public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        ...
        //标记1
        StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
                createAddress(request.url()), call, eventListener, callStackTrace);
        ...
        while (true) {
            //取消,释放
            if (canceled) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response;
            boolean releaseConnection = true;
            ...
            //标记2
            response = realChain.proceed(request, streamAllocation, null, null);
            releaseConnection = false;
            ...
            Request followUp;
            //标记3
            followUp = followUpRequest(response, streamAllocation.route());
            ...
            //标记4
            if (++followUpCount > MAX_FOLLOW_UPS) {
                streamAllocation.release();
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }
            ...
        }
    }
复制代码

在intercept方法中省略了一些代码,我们只看核心代码:

在标记1处创建了StreamAllocation对象,创建的StreamAllocation对象在RetryAndFollowupInterceptor拦截器中并没有使用到。

StreamAllocation对象会通过标记2处的proceed方法传递给下一个拦截器,直到ConnectInterceptor拦截器,StreamAllocation的主要作用是提供建立HTTP请求所需要的网络组件,ConnectInterceptor从RealInterceptorChain获取前面的Interceptor传过来的StreamAllocation对象,执行StreamAllocation对象的newStream()方法完成所有的连接建立工作,并将这个过程中创建的用于网络IO的RealConnection对象,以及与服务器交互最为关键的HttpCodec等对象传递给后面的CallServerInterceptor拦截器。

在标记3处,会对返回的Response进行检查,通过followUpRequest方法对Response返回的状态码判断,并创建重定向需要发出的Request对象。

followUpRequest代码如下:

   private Request followUpRequest(Response userResponse, Route route) throws IOException {
        if (userResponse == null) throw new IllegalStateException();
        int responseCode = userResponse.code();
        final String method = userResponse.request().method();
        switch (responseCode) {
            case HTTP_PROXY_AUTH://407 代理服务器认证
                ...
            case HTTP_UNAUTHORIZED://401 身份未认证
                ...
            case HTTP_PERM_REDIRECT://308
            case HTTP_TEMP_REDIRECT://307
                //当请求的method不为GET和HEAD时不进行重定向
                ...
            case HTTP_MULT_CHOICE://300 多种选择
            case HTTP_MOVED_PERM://301 永久移除
            case HTTP_MOVED_TEMP://302 临时重定向
            case HTTP_SEE_OTHER://303 其他问题
                ...
            case HTTP_CLIENT_TIMEOUT://408 请求超时
                ...
            case HTTP_UNAVAILABLE://503 服务不可用
                ...
            default:
                return null;
        }
    }
复制代码

followUpRequest方法主要是对返回的状态码进行判断,根据特定的状态码创建重定向需要的Request对象。

回到上面拦截器intercept方法,在标记4判断followUpCount是否大于MAX_FOLLOW_UPS(20),也就是说重定向次数上限为20,当重试次数超过20的时候,会释放StreamAllocation这个对象,这样做是为了防止无限制的重试网络请求,从而造成资源的浪费,关于重定向的次数建议可以按照Chrome遵循21个重定向;Firefox、CURL和WGET遵循20;Safari遵循16;HTTP/1推荐5。

总结RetryAndFollowupInterceptor拦截器:

  1. 创建StreamAllocation对象。

  2. 调用RealInterceptorChain.proceed()进行网络请求。

  3. 根据异常结果或响应结果判断是否进行重新请求。

  4. 调用下一个拦截器,对Response进行处理,返回给上一个拦截器。

BridgeInterceptor拦截器

BridgeInterceptor是桥接和适配拦截器,它的作用是设置内容长度、编码方式以及压缩等等一系列操作,主要是添加头部的作用。

该拦截器方法如下:

    @Override public Response intercept(Interceptor.Chain chain) throws IOException {
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();

        //====================添加头部信息========================
        RequestBody body = userRequest.body();
        ...
        if (userRequest.header("Connection") == null) {
            //标记1
            requestBuilder.header("Connection", "Keep-Alive");
        }
        ...
        //标记2
        Response networkResponse = chain.proceed(requestBuilder.build());
        //标记3
        HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

        Response.Builder responseBuilder = networkResponse.newBuilder()
                .request(userRequest);
        //标记4
        if (transparentGzip
                && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
                && HttpHeaders.hasBody(networkResponse)) {
            //标记5
            GzipSource responseBody = new GzipSource(networkResponse.body().source());
           ...
        }

        return responseBuilder.build();
    }
复制代码

intercept方法主要是给Request添加一些头部信息,在标记1处的Connection设置为Keep-Alive,Keep-Alive的作用是当我们开启一个HTTP连接后,在一定时间内保持它的连接状态。

在标记2处调用了拦截器链的proceed方法向服务器发起请求。

在标记3处通过HttpHeaders的receiveHeaders方法,通过网络请求,服务器返回的Response转化为客户端可以识别的Response,如果HTTP默认支持gzip,那么BridgeInterceptor拦截器将会对这个Response进行解压,最终得到客户端使用的Response。

在标记4处,对transparentGzip标志位进行判断,如果transparentGzip为true就表面Accept-Encoding支持gzip压缩;再判断头部的Content-Encoding是否为gzip,保证服务端返回的响应体内容是经过了gzip压缩的;最后判断HTTP头部是否有Body。当满足这些条件后在标记5处将Response的body转换为GzipSource类型,这样的话client端直接以解压的方式读取数据。

总结BridgeInterceptor拦截器:

  1. 负责将用户构建的一个Request请求转化为能够进行网络访问的请求,通过给头部添加信息。

  2. 将这个符合网络请求的Request进行网络请求。

  3. 将网络请求回来的响应Response转化为用户可用的Response。


838794-506ddad529df4cd4.webp.jpg

搜索微信“顾林海”公众号,定期推送优质文章。

猜你喜欢

转载自juejin.im/post/5bd26291518825287847a316