美文网首页
okhttp之旅(三)--拦截器Interceptor概述

okhttp之旅(三)--拦截器Interceptor概述

作者: brock | 来源:发表于2019-01-29 20:09 被阅读197次
    Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(retryAndFollowUpInterceptor);
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        interceptors.add(new CacheInterceptor(client.internalCache()));
        interceptors.add(new ConnectInterceptor(client));
        if (!forWebSocket) {
            interceptors.addAll(client.networkInterceptors());
        }
        interceptors.add(new CallServerInterceptor(forWebSocket));

        Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
                originalRequest, this, eventListener, client.connectTimeoutMillis(),
                client.readTimeoutMillis(), client.writeTimeoutMillis());

        return chain.proceed(originalRequest);
    }

从上述代码可以看出拦截器调用的先后顺序依次是
client.interceptors()-->RetryAndFollowUpInterceptor-->BridgeInterceptor-->CacheInterceptor-->ConnectInterceptor-->client.networkInterceptors()-->CallServerInterceptor

1 Interceptor接口

所有的拦截器(包括我们自定义的)都实现了Interceptor接口

public interface Interceptor {
    Response intercept(Chain chain) throws IOException;

    interface Chain {
        Request request();

        Response proceed(Request request) throws IOException;

        /**
         * 返回请求将在其上执行的连接。
         * 这只适用于网络拦截器的chains; 对于应用程序拦截器,这总是空的。
         */
        @Nullable
        Connection connection();

        Call call();

        int connectTimeoutMillis();

        Chain withConnectTimeout(int timeout, TimeUnit unit);

        int readTimeoutMillis();

        Chain withReadTimeout(int timeout, TimeUnit unit);

        int writeTimeoutMillis();

        Chain withWriteTimeout(int timeout, TimeUnit unit);
    }

2 Okhttp内置的拦截器

  • RetryAndFollowUpInterceptor:负责失败重试以及重定向。
  • BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。
  • CacheInterceptor:负责读取缓存以及更新缓存。
  • ConnectInterceptor:负责与服务器建立连接。
  • CallServerInterceptor:负责从服务器读取响应的数据。

2.1 RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor负责失败重试以及重定向。
RetryAndFollowUpInterceptor的作用就是处理了一些连接异常以及重定向。

  • 整个方法的流程
  • 构建一个StreamAllocation对象,StreamAllocation相当于是个管理类,维护了Connections、Streams和Calls之间的管理,该类初始化一个Socket连接对象,获取输入/输出流对象。
  • 继续执行下一个Interceptor,即BridgeInterceptor
  • 抛出异常,则检测连接是否还可以继续,以下情况不会重试:
    客户端配置出错不再重试
    出错后,request body不能再次发送
    发生以下Exception也无法恢复连接
    ProtocolException:协议异常
    InterruptedIOException:中断异常
    SSLHandshakeException:SSL握手异常
    SSLPeerUnverifiedException:SSL握手未授权异常
    没有更多线路可以选择。
  • 根据响应码处理请求,返回Request不为空时则进行重定向处理,重定向的次数不能超过20次。
  • 由followUpRequest()方法完成
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Call call = realChain.call();
        EventListener eventListener = realChain.eventListener();
        //1\. 构建一个StreamAllocation对象,StreamAllocation相当于是个管理类,维护了
        //Connections、Streams和Calls之间的管理,该类初始化一个Socket连接对象,获取输入/输出流对象。
        StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
                createAddress(request.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;
        //重定向次数
        int followUpCount = 0;
        Response priorResponse = null;
        while (true) {
            if (canceled) {
                //删除连接上的call请求
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response;
            boolean releaseConnection = true;
            try {
                //2\. 继续执行下一个Interceptor,即BridgeInterceptor
                response = realChain.proceed(request, streamAllocation, null, null);
                releaseConnection = false;
            } catch (RouteException e) {
                //抛出异常以指示通过单个路由连接的问题。
                // The attempt to connect via a route failed. The request will not have been sent.
                //尝试通过路由进行连接失败。 该请求不会被发送
                if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
                    throw e.getLastConnectException();
                }
                releaseConnection = false;
                continue;
            } catch (IOException e) {
                // An attempt to communicate with a server failed. The request may have been sent.
                //尝试与服务器通信失败。 该请求可能已发送
                boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
                if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
                releaseConnection = false;
                continue;
            } finally {
                // We're throwing an unchecked exception. Release any resources.
                // 检测到其他未知异常,则释放连接和资源
                if (releaseConnection) {
                    streamAllocation.streamFailed(null);
                    streamAllocation.release();
                }
            }

            // Attach the prior response if it exists. Such responses never have a body.
            //构建响应体,这个响应体的body为空。
            if (priorResponse != null) {
                response = response.newBuilder()
                        .priorResponse(priorResponse.newBuilder()
                                .body(null)
                                .build())
                        .build();
            }

            Request followUp;
            try {
                // 根据响应码处理请求,返回Request不为空时则进行重定向处理
                followUp = followUpRequest(response, streamAllocation.route());
            } catch (IOException e) {
                streamAllocation.release();
                throw e;
            }

            if (followUp == null) {
                if (!forWebSocket) {
                    streamAllocation.release();
                }
                return response;
            }

            closeQuietly(response.body());

            //重定向的次数不能超过20次
            if (++followUpCount > MAX_FOLLOW_UPS) {
                streamAllocation.release();
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }

            if (followUp.body() instanceof UnrepeatableRequestBody) {
                streamAllocation.release();
                throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
            }

            if (!sameConnection(response, followUp.url())) {
                streamAllocation.release();
                streamAllocation = new StreamAllocation(client.connectionPool(),
                        createAddress(followUp.url()), call, eventListener, callStackTrace);
                this.streamAllocation = streamAllocation;
            } else if (streamAllocation.codec() != null) {
                throw new IllegalStateException("Closing the body of " + response
                        + " didn't close its backing stream. Bad interceptor?");
            }

            request = followUp;
            priorResponse = response;
        }
    }

  • StreamAllocation这个类的作用
  • 这个类协调了三个实体类的关系:
  • Connections:连接到远程服务器的物理套接字,这个套接字连接可能比较慢,所以它有一套取消机制。
  • Streams:定义了逻辑上的HTTP请求/响应对,每个连接都定义了它们可以携带的最大并发流,HTTP/1.x每次只可以携带一个,HTTP/2每次可以携带多个。
  • Calls:定义了流的逻辑序列,这个序列通常是一个初始请求以及它的重定向请求,对于同一个连接,我们通常将所有流都放在一个调用中,以此来统一它们的行为。

followUpRequest()

相关文章

网友评论

      本文标题:okhttp之旅(三)--拦截器Interceptor概述

      本文链接:https://www.haomeiwen.com/subject/yukfsqtx.html