Retrofit原理分析

1.OkHttp源码

首先来一张okhttp源码的完整流程图

 1.1.RealCall.getResponseWithInterceptorChain方法解析

首先看一个典型的同步请求过程

public String get(String url) throws IOException { 
    //新建OKHttpClient客户端 
    OkHttpClient client = new OkHttpClient(); 
    //新建一个Request对象 
    Request request = new Request.Builder().url(url).build(); 
    //Response为OKHttp中的响应 
    Response response = client.newCall(request).execute(); 
    if (response.isSuccessful()) { 
        return response.body().string(); 
    }else{ 
        throw new IOException("Unexpected code " + response); 
    } 
} 
/** 
* Prepares the {@code request} to be executed at some point in the futur e.
* 准备将要被执行的request 
*/ 
@Override 
public Call newCall(Request request) {
    return new RealCall(this, request);
}

上面的client.newCall(request)返回的是RealCall,RealCall才是真正的请求执行来看 下RealCall的构造方法和execute方法

    protected RealCall(OkHttpClient client, Request originalRequest) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(clien t);
    }

    @Override
    public Response execute() throws IOException {
        synchronized (this) {
            //检查这个 call是否已经被执行了,每个 call 只能被执行一次,如果想要一个完全一 样的 call,可以利用 all#clone 方法进行克隆  
            if (executed)
                throw new IllegalStateException("Already Executed");
            executed = true;
        }
        try {
            //不过度关注  
            client.dispatcher.executed(this);
            // 真正发出网络请求,解析返回结果的地方‐‐需要关注的地方  
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } finally {
            //不过度关注,返回执行状态  
            client.dispatcher.finished(this);
        }
    }
真正发出网络请求,解析返回结果的,还是 getResponseWithInterceptorChain:
    //拦截器的责任链。
    private Response getResponseWithInterceptorChain() throws
            IOException {
        // Build a full stack of interceptors.
        List<Interceptor>
                interceptors = new ArrayList<>();
        //在配置 OkHttpClient 时设置的 interceptors;
        interceptors.addAll(client.interceptors());
        //负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
        interceptors.add(retryAndFollowUpInterceptor);

        //负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友 好的响应的 BridgeInterceptor
        //其实也就是给request和response添加header
        interceptors.add(new
                BridgeInterceptor(client.cookieJar()));
        //负责读取缓存直接返回、更新或者写入缓存的CacheInterceptor 
        interceptors.add(new CacheInterceptor(client.internalCache()));
        //负责和服务器建立连接的ConnectInterceptor,比较复杂 
        interceptors.add(new ConnectInterceptor(client));
        if
        (!retryAndFollowUpInterceptor.isForWebSocket()) {
            //配置 OkHttpClient 时设置的
            networkInterceptors interceptors.addAll(client.networkInterceptors());
        }
        //负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor,也 不深入研究和okio相关
        interceptors.add(new CallServerInterceptor(
                retryAndFollowUpInterceptor.isForWebSocket()));
        Interceptor.Chain chain = new
                RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
        //开始链式调用
        return chain.proceed(originalRequest);
    }
在chain.proceed(originalRequest);中开启链式调用
public Response proceed(Request request, StreamAllocation streamAllocatio n, HttpCodec httpCodec, 2Connection connection) throws IOException {
        if (index >= interceptors.size()) throw new AssertionError();
        calls++;
        //***************略***************
        // Call the next interceptor in the chain.、
        //这里是责任链模式调用
        //实例化下一个拦截器对应的RealIterceptorChain对象  RealInterceptorChain
        next = new RealInterceptorChain(10interceptors, streamAllocation, httpCodec,
                connection, index + 1, reque st);
        //得到当前的拦截器
        Interceptor interceptor =
                interceptors.get(index);
        //调用当前拦截器的intercept()方法,并将下一个拦截器的RealIterceptorChain对 象传递下去 Response
        response = interceptor.intercept(next);
        //***************略***************
        return response;
    }
    // 看看interceptor.intercept(next)干了啥,又调用了chain.proceed地柜迭代啊
    //核心代码,调用下一个拦截器
    response =((RealInterceptorChain)chain).proceed(request, streamAllocat ion,
            null,null);

1.2自定义一个仿okhttp的不纯的责任链demo 上面拦截器责任链模式我们可以自己写个demo加深理解,就以我们平常的请假申请流程为 例,员工提交申请(request)--主管审批--经理审批--总监审批到此流程完成给你 resposne。

    public interface ModelOkHttpInterceptor {
        //处理实际的任务 
        public void intercept(Chain chain);

        interface Chain {
            //构建责任链 
            void proceed(String user);
        }
    }

    public class RealChain implements ModelOkHttpInterceptor.Chain {
        private List<ModelOkHttpInterceptor> mInterceptors;
        private int mIndex;
        private String mUser;

        public RealChain(List<ModelOkHttpInterceptor> interceptors, int index, String user) {
            mInterceptors = interceptors;
            mIndex = index;
            mUser = user;
        }

        @Override
        public void proceed(String user) {
            mUser = user;
            if (mIndex > mInterceptors.size()) {
                System.out.print("错误,没有人能够审批了");
            }
            RealChain chain = new RealChain(mInterceptors, mIndex + 1, mUser);
            ModelOkHttpInterceptor interceptor = mInterceptors.get(mIndex);
            interceptor.intercept(chain);
        }

        public String getUser() {
            return mUser;
        }
    }

    public class StaffInterceptor implements ModelOkHttpInterceptor {
        @Override
        public void intercept(Chain chain) {
            String name = ((RealChain) chain).getUser();
            System.out.println(name + "提交加班申请");
            //员工提交申请,电子流责任链转到主管那里 
            chain.proceed("主管");
        }
    }

    public class SupervisorInterceptor implements ModelOkHttpInterceptor {
        @Override
        public void intercept(Chain chain) {
            String name = ((RealChain) chain).getUser();
            System.out.println(name + "审批通过");
            //主管神品通过申请,电子流责任链转到部门经理那里 
            chain.proceed("经理");
        }
    }

    public class ManagerInterceptor implements ModelOkHttpInterceptor {
        @Override
        public void intercept(Chain chain) {
            String name = ((RealChain) chain).getUser();
            System.out.println(name + "审批通过");
            //部门经理提交申请,电子流责任链转到总监那里 
            chain.proceed("总监");
        }
    }

    public class ChiefInterceptor implements ModelOkHttpInterceptor {
        @Override
        public void intercept(Chain chain) {
            String name = ((RealChain) chain).getUser();
            //一般总监审批通过流程就结束了,这里就相当于request结束有response返回 
            System.out.println(name + "审批通过");
        }
    }

    public class MainTest {
        public static void main(String[] args) {
            List<ModelOkHttpInterceptor> interceptorList = new ArrayList<>();
            interceptorList.add(new StaffInterceptor());
            interceptorList.add(new SupervisorInterceptor());
            interceptorList.add(new ManagerInterceptor());
            interceptorList.add(new ChiefInterceptor());
            //构建加班申请责任链 
            ModelOkHttpInterceptor.Chain realChain = new RealChain(interceptorList, 0, "老三");
            //员工开始提交申请 
            realChain.proceed("老三");
        }
    }

2.几个主要的拦截器解析

2.1RetryAndFollowUpInterceptor:负责失败重试以及重定向拦截器 这个拦截器负责:当一个请求由于某种原因获取响应数据失败了,就回去尝试重连恢复,并 且重新封装request(followUpRequest)最大重试次数20次。

/**
 * This interceptor recovers from failures and follows redirects as neces sary.
 * 负责失败重试以及重定向的
 * It may throw an {@link IOException} if the call was canceled.
 */
public final class RetryAndFollowUpInterceptor implements Interceptor {
    /**
     * How many redirects and auth challenges should we attempt? Chrome follo ws 21 redirects; Firefox,
     * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.10 * 重试的最大尝试次数,超过将抛出异常
     */
    private static final int MAX_FOLLOW_UPS = 20;
    //OkHttpClient成员
    private final OkHttpClient client;
    //这个类暂时先放过
    private StreamAllocation streamAllocation;
    //这个也先放过
    private boolean forWebSocket;
    //请求被取消
    private volatile boolean canceled;

    public RetryAndFollowUpInterceptor(OkHttpClient client) {
        this.client = client;
    }

    /**
     * 同步的立即取消socket连接请求
     */
    public void cancel() {
        canceled = true;
        StreamAllocation streamAllocation = this.streamAllocation;
        if (streamAllocation != null) streamAllocation.cancel();
    }
    //*******************省略了一些set方法**********************

    /**
     * 主要爱分析这个方法
     *
     * @param chain
     * @return
     * @throws IOException
     */
    @Override
    public Response intercept(Chain chain) throws IOException {
        //拿到拦截链中的请求
        Request request = chain.request();
        //连接池,获取一个正确的连接
        streamAllocation = new StreamAllocation(
                client.connectionPool(), createAddress(request.url()));
        //重定向次数初始化为0
        int followUpCount = 0;
        Response priorResponse = null;
        //开始循环
        while (true) {
            //请求被取消直接抛异常
            if (canceled) {
                //连接被释放
                streamAllocation.release();
                throw new IOException("Canceled");
            }
            Response response = null;
            boolean releaseConnection = true;
            try {
                //核心代码,调用下一个拦截器
                response = ((RealInterceptorChain) chain).proceed(request, streamAlloca tion, 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(), true, request)) throw e.getLa
                stConnectException();
                releaseConnection = false;
                //重试啊
                continue;
            } catch (IOException e) {
                // An attempt to communicate with a server failed. The request may have been sent.
                //和服务器交互失败,请求也不会被发送
                if (!recover(e, false, 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.
            //如果上一次重试获取的response存在,将其boby置为空(没看明白干嘛)
            if (priorResponse != null) {
                response = response.newBuilder()
                        .priorResponse(priorResponse.newBuilder()
                                .body(null)
                                .build())
                        .build();
            }
            //新的重试request封装:添加请求头,添加连接超时
            Request followUp = followUpRequest(response);
            //followUp为空(看下面followUpRequest这个方法的注释如果follow‐up不必要或者 不可用就返回空)
            //重试流程结束,在这里跳出。
            if (followUp == null) {
                if (!forWebSocket) {
                    streamAllocation.release();
                }
                return response;
            }

            closeQuietly(response.body());
            //超过最大重试次数,释放连接,抛出异常
            if (++followUpCount > MAX_FOLLOW_UPS) {
                streamAllocation.release();
                throw new ProtocolException("Too many follow‐up requests: " + followUpC ount);
            }
            //异常处理
            if (followUp.body() instanceof UnrepeatableRequestBody) {
                throw new HttpRetryException("Cannot retry streamed HTTP body", respons e.code());
            }
            if (!sameConnection(response, followUp.url())) {
                streamAllocation.release();
                streamAllocation = new StreamAllocation(
                        client.connectionPool(), createAddress(followUp.url()));
            } else if (streamAllocation.codec() != null) {
                throw new IllegalStateException("Closing the body of " + response
                        + " didn't close its backing stream. Bad interceptor?");
            }
            //request重新赋值
            request = followUp;
            priorResponse = response;
        }
    }

    //*******************省略一些方法**********************

    /**
     * Figures out the HTTP request to make in response to receiving {@code userResponse}. This will
     * either add authentication headers, follow redirects or handle a clien t request timeout. If a
     * follow‐up is either unnecessary or not applicable, this returns null.
     * 对重试请求做了下封装:添加header,连接timeout
     */
    private Request followUpRequest(Response userResponse) throws IOExcepti on {
        if (userResponse == null) throw new IllegalStateException();
        Connection connection = streamAllocation.connection();
        Route route = connection != null
                ? connection.route()
                : null;
        int responseCode = userResponse.code();

        final String method = userResponse.request().method();
        switch (responseCode) {
            case HTTP_PROXY_AUTH:
                Proxy selectedProxy = route != null
                        ? route.proxy()
                        : client.proxy();
                if (selectedProxy.type() != Proxy.Type.HTTP) {
                    throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
                }
                return client.proxyAuthenticator().authenticate(route, userResponse);

            case HTTP_UNAUTHORIZED:
                return client.authenticator().authenticate(route, userResponse);

            case HTTP_PERM_REDIRECT:
            case HTTP_TEMP_REDIRECT:
                // "If the 307 or 308 status code is received in response to a request other than GET
                // or HEAD, the user agent MUST NOT automatically redirect the request"
                if (!method.equals("GET") && !method.equals("HEAD")) {
                    return null;
                }
                // fall‐through
            case HTTP_MULT_CHOICE:
            case HTTP_MOVED_PERM:
            case HTTP_MOVED_TEMP:
            case HTTP_SEE_OTHER:
                // Does the client allow redirects?
                if (!client.followRedirects()) return null;
                String location = userResponse.header("Location");
                if (location == null) return null;
                HttpUrl url = userResponse.request().url().resolve(location);

                // Don't follow redirects to unsupported protocols.
                if (url == null) return null;

                // If configured, don't follow redirects between SSL and non‐SSL.
                boolean sameScheme = url.scheme().equals(userResponse.request().url().s cheme());
                if (!sameScheme && !client.followSslRedirects()) return null;

                // Redirects don't include a request body.
                Request.Builder requestBuilder = userResponse.request().newBuilder();
                if (HttpMethod.permitsRequestBody(method)) {
                    193 if (HttpMethod.redirectsToGet(method)) {
                        requestBuilder.method("GET", null);
                    } else {
                        requestBuilder.method(method, null);
                    }
                    198 requestBuilder.removeHeader("Transfer‐Encoding");
                    requestBuilder.removeHeader("Content‐Length");
                    requestBuilder.removeHeader("Content‐Type");
                }

                // When redirecting across hosts, drop all authentication headers. This 204 // is potentially annoying to the application layer since they have no
                // way to retain them.
                if (!sameConnection(userResponse, url)) {
                    requestBuilder.removeHeader("Authorization");
                }

                return requestBuilder.url(url).build();

            case HTTP_CLIENT_TIMEOUT:

                // 408's are rare in practice, but some servers like HAProxy use this r esponse code. The
                // spec says that we may repeat the request without modifications. Mode rn browsers also
                // repeat the request (even non‐idempotent ones.) 216 if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
                return null;
        }

        return userResponse.request();

        default:
        return null;
    }
}

/**
 * Returns true if an HTTP request for {@code followUp} can reuse the co nnection used by this
 * engine.
 * 判断request请求是否可以复用
 */
private boolean sameConnection(Response response, HttpUrl followUp) {
    HttpUrl url = response.request().url();
    return url.host().equals(followUp.host())
            && url.port() == followUp.port()
            && url.scheme().equals(followUp.scheme());
}
2.2BridgeInterceptor:
BridgeInterceptor的注释意思是根据用户请求构建网络请求,使得它继续去访问网络,最
后根据网络响应构建用户响应, 其实就是卫request添加请求头,为response添加响应
头。
/**
     * Bridges from application code to network code. First it builds a netwo rk request from a user  * request. Then it proceeds to call the network. Finally it builds a use r response from the network  * response.
     * 上面意思是根据用户请求构建网络请求,使得它继续去访问网络,最后根据网络响应构 建用户响应。  * 其实就是卫request添加请求头,为response添加响应头。
     */
    public final class BridgeInterceptor implements Interceptor {
        private final CookieJar cookieJar;

        public BridgeInterceptor(CookieJar cookieJar) {
            this.cookieJar = cookieJar;
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request userRequest = chain.request();
            Request.Builder requestBuilder = userRequest.newBuilder();
            RequestBody body = userRequest.body();
            0 if (body != null) {
                MediaType contentType = body.contentType();  //添加contenttype  
                if (contentType != null) {
                    requestBuilder.header("Content‐Type", contentType.toString());
                }
                long contentLength = body.contentLength();
                if (contentLength != ‐){
                    //如果body有值,请求头设置body的内容长度,并且移除传输编码 
                    requestBuilder.header("Content‐Length", Long.toString(contentLength));
                    requestBuilder.removeHeader("Transfer‐Encoding");
                } else{
                    // 否则,设置传输编码(这里是分块传输编码),移除内容长度值  
                    requestBuilder.header("Transfer‐Encoding", "chunked");
                    requestBuilder.removeHeader("Content‐Length");
                }
            }  //添加Host  
            if (userRequest.header("Host") == null) {
                requestBuilder.header("Host", hostHeader(userRequest.url(), false));
            }  //添加Connection  
            if (userRequest.header("Connection") == null) {
                requestBuilder.header("Connection", "Keep‐Alive");
            }  // If we add an "Accept‐Encoding: gzip" header field we're responsible for also decompressing  
            // the transfer stream.  
            boolean transparentGzip = false;
            if (userRequest.header("Accept‐Encoding") == null) {
                transparentGzip = true;
                requestBuilder.header("Accept‐Encoding", "gzip");
            }
            List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
            if (!cookies.isEmpty()) {
                requestBuilder.header("Cookie", cookieHeader(cookies));
            }
            if (userRequest.header("User‐Agent") == null) {
                requestBuilder.header("User‐Agent", Version.userAgent());
            }  //开始给resposne添加header  
            Response networkResponse = chain.proceed(requestBuilder.build());
            HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkRespons e.headers());
            Response.Builder responseBuilder = networkResponse.newBuilder().request(userRequest);
            if (transparentGzip && "gzip".equalsIgnoreCase(networkResponse.header("Content‐Encoding")) && HttpHeaders.hasBody(networkResponse)) {
                GzipSource responseBody = new GzipSource(networkResponse.body().source());
                Headers strippedHeaders = networkResponse.headers().newBuilder().removeAll("Content‐Encoding").removeAll("Content‐Length").build();
                responseBuilder.headers(strippedHeaders);
                responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
            }
            return responseBuilder.build();
        }

        /**
         * Returns a 'Cookie' HTTP request header with all cookies, like {@cod e a=b; c=d}.
         */
        private String cookieHeader(List<Cookie> cookies) {
            StringBuilder cookieHeader = new StringBuilder();
            for (int i = 0, size = cookies.size(); i < size; i++) {
                if (i > 0) {
                    cookieHeader.append("; ");
                }
                Cookie cookie = cookies.get(i);
                cookieHeader.append(cookie.name()).append('=').append(cookie.value());
            }
            return cookieHeader.toString();
        }
    }
2.3ConnectInterceptor
ConnectInterceptor的intercept内部代码很少,关内容主要是咋在
streamAllocation.newStream这里比较复杂。newStream内部findHealthyConnection
是找到一个可用连接的意思重点就在这,通过找到的可用连接newCodec()返回了
HttpCodec的实现类Http1Codec对象。
    public final class ConnectInterceptor implements Interceptor {
        public final OkHttpClient client;

        public ConnectInterceptor(OkHttpClient client) {
            this.client = client;
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            RealInterceptorChain realChain = (RealInterceptorChain) chain;
            Request request = realChain.request();
            StreamAllocation streamAllocation = realChain.streamAllocation();  // We need the network to satisfy this request. Possibly for validating a conditional GET.  boolean doExtensiveHealthChecks = !request.method().equals("GET");  HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHea lthChecks);
            RealConnection connection = streamAllocation.connection();
            return realChain.proceed(request, streamAllocation, httpCodec, connecti on);
        }
    }

    //主要看看findHealthyConnection()的实现
    public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealth Checks) {
        int connectTimeout = client.connectTimeoutMillis();
        int readTimeout = client.readTimeoutMillis();
        int writeTimeout = client.writeTimeoutMillis();
        boolean connectionRetryEnabled = client.retryOnConnectionFailure();
        try {  //findHealthyConnection这个方法是重点  
            RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
            HttpCodec resultCodec;
            if (resultConnection.httpConnection != null) {
                resultCodec = new HttpCodec(client, this, resultConnection.httpConnec tion);
            } else {
                resultConnection.socket().setSoTimeout(readTimeout);
                resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
                resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
                resultCodec = new HttpCodec(client, this, resultConnection.source, resultConnection.sink);
            } synchronized (connectionPool) {
                codec = resultCodec;
                return resultCodec;
            }
        } catch (IOException e) {
            throw new RouteException(e);
        }
    }
流程:先检测链接是否可用: a 可用的话直接返回,结束流程。
b 不可用,先去连接池中查找:
连接池找到:结束流程。
未找到:提供address,再次去连接池中查找。如果找到了直接结束流程,如果没找到:生
成一个新的连接,并且将这个新的链接加入到连接池,然后返回这个新链接。
    private RealConnection findHealthyConnection(int connectTimeout, int read Timeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks) throws IOException {
        while (true) {
            RealConnection candidate = findConnection(connectTimeout, readTimeout, w riteTimeout, pingIntervalMillis, connectionRetryEnabled);  // If this is a brand new connection, we can skip the extensive health c hecks.  synchronized (connectionPool) {  if (candidate.successCount == ) {  return candidate;  }  }  // Do a (potentially slow) check to confirm that the pooled connection is still good. If it  // isn't, take it out of the pool and start again.  if (!candidate.isHealthy(doExtensiveHealthChecks)) {//判断连接是否可用  noNewStreams();//连接不可用,移除  continue;//不可用,就一直持续  }  return candidate;  }  }  private RealConnection findConnection(int connectTimeout, int readTimeou t, int writeTimeout,  int pingIntervalMillis, boolean connectionRetryEnabled) throws IOExcept ion {  boolean foundPooledConnection = false;  RealConnection result = null;  Route selectedRoute = null;
            Connection releasedConnection;
            Socket toClose;
            //异常情况,直接抛出  
            synchronized (connectionPool) {
                if (released) throw new IllegalStateException("released");
                if (codec != null) throw new IllegalStateException("codec != null");
                if (canceled)
                    throw new IOException("Canceled");
                // Attempt to use an already‐allocated connection. We need to be carefu l here because our  
                // already‐allocated connection may have been restricted from creating new streams.  
                releasedConnection = this.connection;
                toClose = releaseIfNoNewStreams();
                if (this.connection != null) { //  
                    // 经过releaseIfNoNewStreams,connection不为null,则连接是可用的  
                    // We had an already‐allocated connection and it's good.  
                    result = this.connection;
                    releasedConnection = null;
                }
                if (!reportedAcquired) {
                    // If the connection was never reported acquired, don't report it as re leased!  
                    releasedConnection = null;
                }
                //无可用连接,去连接池connectionPool中获取  
                if (result == null) {
                    // Attempt to get a connection from the pool.  
                    Internal.instance.get(connectionPool, address, this, null);
                    if (connection != null) {
                        foundPooledConnection = true;
                        result = connection;
                    } else {
                        selectedRoute = route;
                    }
                }
            }
            closeQuietly(toClose);
            if (releasedConnection != null) {
                eventListener.connectionReleased(call, releasedConnection);
            }
            if (foundPooledConnection) {
                eventListener.connectionAcquired(call, result);
            }
            if (result != null) {
                //上面通过去连接池中找,如果result不为null,说明找到了 可用连接  
                // If we found an already‐allocated or pooled connection, we're done.  
                return result;
            }  // If we need a route selection, make one. This is a blocking operatio n. 
            // 如果在连接池中也没找到可用连接, 就需要一个路由信息,这是一个阻塞操作  
            boolean newRouteSelection = false;
            if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
                newRouteSelection = true;
                routeSelection = routeSelector.next();
            }
            synchronized (connectionPool) {
                if (canceled) throw new IOException("Canceled");
                if (newRouteSelection) {
                    // Now that we have a set of IP addresses, make another attempt at gett ing a connection from  
                    // the pool. This could match due to connection coalescing.  
                    // 提供address,再次从连接池中获取连接  
                    List<Route> routes = routeSelection.getAll();
                    for (int i = ,size = routes.size();
                    i<size ;
                    i++){
                        Route route = routes.get(i);
                        Internal.instance.get(connectionPool, address, this, route);
                        if (connection != null) {
                            foundPooledConnection = true;
                            result = connection;
                            this.route = route;
                            break;
                        }
                    }
                }
            }
            //提供路路由信息,然后进行查找可用链接,还是没有找到可用链接,就需要生成一个 新的连接  
            if (!foundPooledConnection) {
                if (selectedRoute == null) {
                    selectedRoute = routeSelection.next();
                }   // Create a connection and assign it to this allocation immediately. Th is makes it possible  
                // for an asynchronous cancel() to interrupt the handshake we're about to do.  
                route = selectedRoute;
                refusedStreamCount =; result = new RealConnection(connectionPool, selectedRoute);
                acquire(result, false);
            }
        }   // If we found a pooled connection on the nd time around, we're done.  
        // 如果连接是从连接池中找到的,直接拿出来使用  
        if (foundPooledConnection) {
            eventListener.connectionAcquired(call, result);
            return result;
        }   // Do TCP + TLS handshakes. This is a blocking operation.  
        result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalM illis, connectionRetryEnabled, call, eventListener);
        routeDatabase().connected(result.route());
        Socket socket = null;
        synchronized (connectionPool) {
            reportedAcquired = true;
            // Pool the connection.  
            // 将新生成的连接放入连接池中  
            Internal.instance.put(connectionPool, result);
            // If another multiplexed connection to the same address was created co ncurrently, then  
            // release this connection and acquire that one.
            //如果是一个http连接,http连接应具有多路复用特性,  
            if (result.isMultiplexed()) {
                socket = Internal.instance.deduplicate(connectionPool, address, this);
                result = connection;
            }
        }
        closeQuietly(socket);
        eventListener.connectionAcquired(call, result);
        return result;
    }

2.4CacheInterceptor

CacheInterceptor核心还是调用下一个拦截器,从网络获取响应数据,流程:
1.首先根据requst来判断cache中是否存在缓存,如有存在取出这个response
2.根据request获取缓存策略:网络,缓存,网络+缓存
3.一些异常情况判断:
(1)既无网络请求访问,也没有缓存,直接返回504
(2)没有网络请求访问,但是有缓存直接返回缓存
4.调用下一个拦截器,从网络获取响应数据
5.如果本地存在cacheResponse,网络response和cacheResponse作比较,判断是否更新
cacheResponse
6.没有cacheResponse,直接写入网络response到缓存
    public final class CacheInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {  //获取到缓存‐‐‐‐第一步  
            Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;
            long now = System.currentTimeMillis();
            //CacheStrategy缓存策略类,管理request和response的缓存‐‐‐‐第二步  
            CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
            Request networkRequest = strategy.networkRequest;
            Response cacheResponse = strategy.cacheResponse;
            if (cache != null) {
                cache.trackResponse(strategy);
            }
            //存在缓存但是不适用,关闭  
            if (cacheCandidate != null && cacheResponse == null) {
                closeQuietly(cacheCandidate.body()); // The cache candidate wasn't appl icable. Close it.  
            }
            // If we're forbidden from using the network and the cache is insuffici ent, fail.  
            // 如果禁止访问网络,并且无缓存,返回‐‐‐‐第三步  
            if (networkRequest == null && cacheResponse == null) {
                return new Response.Builder().request(chain.request()).protocol(Protocol.HTTP__).code().message("Unsatisfiable Request (only‐if‐cached)").body(EMPTY_BODY).sentRequestAtMillis(‐L).receivedResponseAtMillis(System.currentTimeMillis()).build();
            }
            // If we don't need the network, we're done.  
            // 没有网络请求,直接返回缓存‐‐还是第三步 
            if (networkRequest == null) {
                return cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();
            }
            Response networkResponse = null;
            try {
                //网络请求拦截器‐‐‐‐第四步  
                networkResponse = chain.proceed(networkRequest);
            } finally {
                // If we're crashing on I/O or otherwise, don't leak the cache body.  
                // 如果因为io或者其他原因崩溃,不泄露缓存并关闭 
                if (networkResponse == null && cacheCandidate != null) {
                    closeQuietly(cacheCandidate.body());
                }
            }
            // If we have a cache response too, then we're doing a conditional get.  
            // 如果已经存在缓存response,并且正在做条件get请求  
            if (cacheResponse != null) {
                if (validate(cacheResponse, networkResponse)) {
                    Response response = cacheResponse.newBuilder().headers(combine(cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(cacheResponse)).networkResponse(stripBody(networkResponse)).build();
                    networkResponse.body().close();
                    // Update the cache after combining headers but before stripping the  
                    // Content‐Encoding header (as performed by initContentStream()).  
                    // 更新缓存‐‐‐‐第五步  
                    cache.trackConditionalCacheHit();
                    cache.update(cacheResponse, response);
                    return response;
                } else {
                    closeQuietly(cacheResponse.body());
                }
            }
            Response response = networkResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).networkResponse(stripBody(networkResponse)).build();
            if (HttpHeaders.hasBody(response)) {
                CacheRequest cacheRequest = maybeCache(response, networkResponse.reques t(), cache);
                //写入缓存‐‐‐‐第六步  
                response = cacheWritingResponse(cacheRequest, response);
            } return response;
        }
    }

2.5CallServerInterceptor:发送和接收数据

    public final class CallServerInterceptor implements Interceptor {
        private final boolean forWebSocket;

        public CallServerInterceptor(boolean forWebSocket) {
            this.forWebSocket = forWebSocket;
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
            StreamAllocation streamAllocation = ((RealInterceptorChain) chain).stre amAllocation();
            Request request = chain.request();
            long sentRequestMillis = System.currentTimeMillis();
            //添加请求头
            httpCodec.writeRequestHeaders(request);
            if (HttpMethod.permitsRequestBody(request.method()) && request.body() ! = null){
                Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
                BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
                request.body().writeTo(bufferedRequestBody);
                bufferedRequestBody.close();
            }
            httpCodec.finishRequest();
            //发起请求,得到response
            Response response = httpCodec.readResponseHeaders().request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
            if (!forWebSocket || response.code() != 101) {
                response = response.newBuilder().body(httpCodec.openResponseBody(response)).build();
            }
            if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) {
                streamAllocation.noNewStreams();
            }  //异常情况处理
            int code = response.code();
            if ((code == 204 || code == 205) && response.body().contentLength() >) {
                throw new ProtocolException("HTTP " + code + " had non‐zero Content‐Length: " + response.body().con tentLength());
            } return response;
        }
    }

3.请求过程

3.1同步请求流程
1.1有提到过,不说了
3.2异步请求流程
典型流程
    private final OkHttpClient client = new OkHttpClient();

    public void run() throws Exception {
        Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
                Headers responseHeaders = response.headers();
                for (int i = ,size = responseHeaders.size();
                i<size ;
                i++){
                    System.out.println(responseHeaders.name(i) + ": " + responseHeaders.val ue(i));
                } System.out.println(response.body().string());
            }
        });
    }

和同步不同的地方主要是enqueue,看这个方法:

    //异步任务使用
    @Override
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }  //调用了上面我们没有详细说的Dispatcher类中的enqueue(Call )方法.接着继续看‐‐分 析
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    //正 在准备中的异步请求队列
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    //运行中的异步请求
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

    //同 步请求  /**  **分析  **/
    synchronized void enqueue(AsyncCall call) {
        //如果中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到 runningAsyncCalls中执行,同时利用线程池
        // 执行call;否者将call加入到readyAsyncCa lls中
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            runningAsyncCalls.add(call);
            //call加入到线程池中执行了。现在再看AsynCall的代码,它是RealCall中的内部类‐‐ 分析
            executorService().execute(call);
        } else {
            readyAsyncCalls.add(call);
        }
    }  //异步请求  /**  **分析  **/

    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

        private AsyncCall(Callback responseCallback) {
            super("OkHttp %s", redactedUrl());
            this.responseCallback = responseCallback;
        }

        String host() {
            return originalRequest.url().host();
        }

        Request request() {
            return originalRequest;
        }

        RealCall get() {
            return RealCall.this;
        }

        @Override
        protected void execute() {
            boolean signalledCallback = false;
            try {
                //破案了,异步请求也是调用getResponseWithInterceptorChain处理来获得respons e,
                // 异步任务同样也是会经过一系列的拦截器
                Response response = getResponseWithInterceptorChain();
                if (retryAndFollowUpInterceptor.isCanceled()) {
                    signalledCallback = true;
                    responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
                } else {
                    signalledCallback = true;
                    responseCallback.onResponse(RealCall.this, response);
                }
            } catch (
                    IOException e) {
                if (signalledCallback) {
                    // Do not signal the callback twice!
                    Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
                } else {
                    responseCallback.onFailure(RealCall.this, e);
                }
            } finally {
                client.dispatcher().finished(this);
            }
        }
    }

4.自定义一个拦截器(LogInterceptor) 就以最常见的LoggingInterceptor为例子

    class LoggingInterceptor implements Interceptor {
        @Override
        public Response intercept(Interceptor.Chain chain) throws IOEx ception {
            Request request = chain.request();
            //.请求前‐‐打印请求信息
            long t = System.nanoTime();
            logger.info(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()));
            //.网络请求
            Response response = chain.proceed(request);
            //.网络响应后‐‐打印响应信息
            long t = System.nanoTime();
            logger.info(String.format("Received response for %s in %.fms%n%s", response.request().url(), (t ‐ t)
                    / ed, response.headers()))
            ;
            return response;
        }
    }

4.1添加 Application Interceptor,request是最初的request,response 就是最终的响 应,得到日志相对较少

    OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();
    Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User‐Agent", "OkHttp Example").build();
    Response response = client.newCall(request).execute();  response.body().

    close();

    响应数据:
    INFO:
    Sending request
    http://www.publicobject.com/helloworld.txt on null
    User‐Agent: OkHttp Example
            
    INFO: Received response for https://publicobject.com/helloworld.txt in  .ms
    Server: nginx/.. (Ubuntu)
    Content‐Type: text/plain
    Content‐Length:   Connection: keep‐alive

4.2添加 NetworkInterceptor,可以得到的是更多的信息,重定向的信息也会得到。

    OkHttpClient client = new OkHttpClient.Builder().addNetworkInterceptor(new LoggingInterceptor()).build();
    Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User‐Agent", "OkHttp Example").build();
    Response response = client.newCall(request).execute();
    response.body().

    close();

    INFO:
    Sending request
    http://www.publicobject.com/helloworld.txt on Conn ection{www.publicobject.com:, proxy=DIRECT hostAddress=... ciph erSuite=none protocol=http/.}
    User‐Agent:
    OkHttp Example
    Host:
    www.publicobject.com Connection:Keep‐
    Alive Accept‐Encoding:gzip
    INFO:
    Received response for http://www.publicobject.com/helloworld.txt i n .ms  Server: nginx/.. (Ubuntu)  Content‐Type: text/html
    Content‐Length:Connection:keep‐
    alive Location:https://publicobject.com/helloworld.txt
    INFO:
    Sending request
    https://publicobject.com/helloworld.txt on Connect ion{publicobject.com:, proxy=DIRECT hostAddress=... cipherSuit e=TLS_ECDHE_RSA_WITH_AES__CBC_SHA protocol=http/.}
    User‐Agent:
    OkHttp Example
    Host:
    publicobject.com Connection:Keep‐
    Alive Accept‐Encoding:gzip
    INFO: Received response for https://publicobject.com/helloworld.txt in  .ms  Server: nginx/.. (Ubuntu)  Content‐Type: text/plain  Content‐Length:   Connection: keep‐alive

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值