Okhttp3源码分析

Okhttp3 使用

get请求

1.拿到OkhttpClient对象

 OkHttpClient client = new OkHttpClient();

2.构造Request对象

   Request request = new Request.Builder().url(url).get().build();

3.将request封装为call

 Call call = client.newCall(request);

4.根据需求使用同步或者异步方法

 //同步调用
        try {
            Response response = call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //异步调用,并设置回调
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //请求失败
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //请求成功,注意当前线程是子线程,
            }
        });

Post 请求

1.拿到OkhttpClient对象

 OkHttpClient client = new OkHttpClient();

2.构建FormBody对象

   FormBody formBody = new FormBody.Builder()
                .add("key1", "value1")
                .build();

3.构建Request对象

 Request request = new Builder().url(url).post(formBody).build();

4.根据需求使用同步或者异步方法

 //同步调用
        try {
            Response response = call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //异步调用,并设置回调
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //请求失败
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //请求成功,注意当前线程是子线程,
            }
        });

Okhttp 流程分析

下图为Okhttp工作的大致流程
image

Call&& RealCall

1.首先看下这段代码:Call call = client.newCall(request);

OkhttpClient.java

 @Override
    public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
    }

2.这里创建了一个RealCall的对象,而RealCall是实现了Call的接口。

Call.java

public interface Call extends Cloneable {
  /** 获得原始请求 */
  Request request();

  /** 同步执行请求 */
  Response execute() throws IOException;

  /** 异步执行请求 */
  void enqueue(Callback responseCallback);

  /** 尽可能取消请求。已经完成了的请求不能被取消 */
  void cancel();

  /**
   * 调用了execute()或者enqueue(Callback)后都是true
   */
  boolean isExecuted();

  boolean isCanceled();

  /** 创建一个新的、完全一样的Call对象,即使原对象状态为enqueued或者executed */
  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

回到RealCall ,看看他的构造方法和成员变量:

final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
final EventListener eventListener;

/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;

// Guarded by this.
private boolean executed;

RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  // 事件监听器,默认为null,目前3.8版本也无法设置
  final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

  this.client = client;
  this.originalRequest = originalRequest;
  this.forWebSocket = forWebSocket;
  // 创建一个重试、重定向拦截器
  this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

  // TODO(jwilson): this is unsafe publication and not threadsafe.
  this.eventListener = eventListenerFactory.create(this);
}

RealCall的同步请求方法

RealCall.excute()

    @Override
    public Response execute() throws IOException {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        //捕获调用栈跟踪
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
            //发起请求
            client.dispatcher().executed(this);
            //获取请求结果
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            eventListener.callFailed(this, e);
            throw e;
        } finally {
            client.dispatcher().finished(this);
        }
    }

上面的方法一旦执行就就将executed设置为true,表示该请求已经执行了,你能在执行。
然后就继续调用 client.dispatcher().executed(this); 该方法就是将call添加到Dispather.runningSyncCalls中去,

   synchronized void executed(RealCall call) {
        //一个Deque容器想,和ArrayList差不多(内部也是用数组实现, 区别在于扩容上)
        runningSyncCalls.add(call);
    }

接着调用getResponseWithInterceptorChain()方法进行网络请求,返回Reponse结果。具体后面讲异步在进行详细介绍。
最后调用了client.dispatcher().finished(this); 该方法就是call执行完毕和将call从Dispather.runningSyncCalls中移除出去。见代码

void finished(RealCall call) {
        finished(runningSyncCalls, call, false);
    }

    private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
          //使用对象锁保证线程安全
        synchronized (this) {
            if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
            if (promoteCalls) promoteCalls();
            runningCallsCount = runningCallsCount();
            idleCallback = this.idleCallback;
        }

        if (runningCallsCount == 0 && idleCallback != null) {
            idleCallback.run();
        }
    }

同时,如果promoteCalls为true(此处入参false),还会执行promoteCalls方法,此方法是给异步调用准备的,具体代码后面会谈到;
最后如果runningAsyncCalls、runningSyncCalls这俩正在执行的同步、异步队列之和为0,说明dispatcher处理空闲状态,那么调用idleCallback.run通知外界dispatcher已经空闲了。到此此处同步方法已经初步结束。

RealCall的异步方法enqueue

同execute方法类似,一旦Call.enqueue方法被执行,那么其executed就会被设置为true,如果多次调用就会报错。
然后调用client.dispatcher().enqueue(new AsyncCall(responseCallback));方法开始了异步调用。
源码:

  @Override
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        //捕获调用栈跟踪
        captureCallStackTrace();
        //事件
        eventListener.callStart(this);
        //调用异步方法
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

看下AsyncCall的相关代码

final class AsyncCall extends NamedRunnable {
  private final Callback responseCallback;

  AsyncCall(Callback responseCallback) {
    super("OkHttp %s", redactedUrl());
    this.responseCallback = responseCallback;
  }
  ...
  @Override protected void execute() {
    ...
  }
}

他是继承一个NamedRunnable类,是具有一个name属性的Runnable的抽象类,在执行方法的时候为执行该代码的当前线程设置名字

/**
 * Runnable implementation which always sets its thread name.
 */
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

下面接续返回Dispatcher.enqueue方法:

private int maxRequests = 64;
private int maxRequestsPerHost = 5;

synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
  } else {
    readyAsyncCalls.add(call);
  }
}

maxRequests和maxRequestsPerHost都有默认值,且有setter方法可以设置具体值,两者的setter方法最后会执行promoteCalls方法尝试执行异步任务。
在enqueue方法中,首先会检查「正在运行的异步请求数」以及「call对应的host上的异步请求数」是否达到了阈值。如果还没有达到阈值,那么加入到runningAsyncCalls队列中,同时开始执行请求;否则加入到readyAsyncCalls队列中进行等待.
executorService().execute(call),这是一个单例实现的线程池,该线程值也可以在Dispatcher的构造器中传入。

/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;

public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}

public synchronized ExecutorService executorService() {
if (executorService == null) {
  executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
      new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}

提交到线程池后,AsyncCall.run方法就会被调用,又因为AsyncCall继承了NamedRunnable,所以最后执行的是AsyncCall.execute方法:

        @Override
       protected void execute() {
           boolean signalledCallback = false;
           try {
               //执行请求 (拦截器)
               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 {
                   eventListener.callFailed(RealCall.this, e);
                   responseCallback.onFailure(RealCall.this, e);
               }
           } finally {
               client.dispatcher().finished(this);
           }
       }

首先调用getResponseWithInterceptorChain进行网络请求并获取Response,然后根据请求是否被取消,调用对应的回调方法。最后调用client.dispatcher().finished(this)方法在runningAsyncCalls方法中移除call,并尝试执行其他的异步方法。

我们先看client.dispatcher().finished(this)方法,最后再看getResponseWithInterceptorChain方法的实现:

/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
  finished(runningAsyncCalls, call, true);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
  int runningCallsCount;
  Runnable idleCallback;
  synchronized (this) {
    if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
    if (promoteCalls) promoteCalls();
    runningCallsCount = runningCallsCount();
    idleCallback = this.idleCallback;
  }

  if (runningCallsCount == 0 && idleCallback != null) {
    idleCallback.run();
  }
}

private void promoteCalls() {
  if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
  if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

  for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
    AsyncCall call = i.next();

    if (runningCallsForHost(call) < maxRequestsPerHost) {
      i.remove();
      runningAsyncCalls.add(call);
      executorService().execute(call);
    }

    if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
  }
}

2个finshed方法不用说了,将同步的时候已经介绍了。
promoteCalls方法的代码也很清晰明了,这是一个将等待队列中的任务移到runningAsyncCalls且执行的过程。

getResponseWithInterceptorChain

此处是拦截器,重头戏来了话不多说看下源码:

    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);
    }

首先将以下拦截器依次加入到List中:

OkHttpClient设置的拦截器interceptors()
重试、重定向拦截器RetryAndFollowUpInterceptor
把用户请求转换为服务器请求、把服务器返响应转换为用户响应的BridgeInterceptor
读取缓存直接返回、将响应写入到缓存中的CacheInterceptor
与服务器建立连接的ConnectInterceptor
OkHttpClient设置的网络拦截器networkInterceptors()
真正执行网络请求的CallServerInterceptor
将所有的拦截器保存在interceptors后,创建一个拦截器责任链RealInterceptorChain,并调用其proceed开始处理网络请求。

Okhttp3的五大拦截器的作用的简单介绍

RetryAndFollowUpInterceptor(重定向拦截器)

源码:

     private static final int MAX_FOLLOW_UPS = 20;

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Call call = realChain.call();
        EventListener eventListener = realChain.eventListener();

        /**
         * todo  管理类,维护了 与服务器的连接、数据流与请求三者的关系。真正使用的拦截器为 Connect
         */
        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) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response;
            boolean releaseConnection = true;
            try {
                //todo 请求出现了异常,那么releaseConnection依旧为true。
                response = realChain.proceed(request, streamAllocation, null, null);
                releaseConnection = false;
            } catch (RouteException e) {
                //todo 路由异常,连接未成功,请求还没发出去
                //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) {
                //todo 请求发出去了,。(socket流正但是和服务器通信失败了在读写数据的时候断开连接)
                // HTTP2才会抛出ConnectionShutdownException。所以对于HTTP1 requestSendStarted一定是true
                //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.
                //todo 不是前两种的失败,那直接关闭清理所有资源
                if (releaseConnection) {
                    streamAllocation.streamFailed(null);
                    streamAllocation.release();
                }
            }
            //todo 如果进过重试/重定向才成功的,则在本次响应中记录上次响应的情况
            //Attach the prior response if it exists. Such responses never have a body.
            if (priorResponse != null) {
                response = response.newBuilder()
                        .priorResponse(
                                priorResponse.newBuilder()
                                        .body(null)
                                        .build()
                        )
                        .build();
            }
            //todo 处理3和4xx的一些状态码,如301 302重定向
            Request followUp = followUpRequest(response, streamAllocation.route());
            if (followUp == null) {
                if (!forWebSocket) {
                    streamAllocation.release();
                }
                return response;
            }

            closeQuietly(response.body());

            //todo 限制最大 followup 次数为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());
            }
            //todo 判断是不是可以复用同一份连接
            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;
        }
    }

客户端向服务器发送一个请求,获取对应的资源,服务器收到请求之后,发现这个资源在另一个位置,浴室读物器在返回的响应头的location字段中写入那个请求资源的正确地址,并设置reponse的状态码为30X。
image
重定向拦截器的拦截操作中做了这么几件事:
1、创建一个 StreamAllocation。
2、调用下一个chain 。
3、判断是否重定向。重定向会对request做一些修改,返回新的request,否则会返回null。
4、followUp == null 无重定向,释放资源,直接返回response。
5、比较重定向前后的 host、port、scheme是否一致,一致的话复用,否则重新创建 StreamAllocation。
6、通过 while (true) ,重复步骤 2

BridgeInterceptor(桥接拦截器)

BridgeInterceptor 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应。
源码:

    /**
     *  为我们补全请求头,并默认使用gzip压缩,同时将响应体重新设置为gzip读取。
     * @param chain
     * @return
     * @throws IOException
     */
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();

        RequestBody body = userRequest.body();
        if (body != null) {
            MediaType contentType = body.contentType();
            if (contentType != null) {
                requestBuilder.header("Content-Type", contentType.toString());
            }

            long contentLength = body.contentLength();
            if (contentLength != -1) {
                requestBuilder.header("Content-Length", Long.toString(contentLength));
                requestBuilder.removeHeader("Transfer-Encoding");
            } else {
                requestBuilder.header("Transfer-Encoding", "chunked");
                requestBuilder.removeHeader("Content-Length");
            }
        }

        if (userRequest.header("Host") == null) {
            requestBuilder.header("Host", hostHeader(userRequest.url(), false));
        }

        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 && userRequest.header("Range") == 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());
        }

        Response networkResponse = chain.proceed(requestBuilder.build());

        HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.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);
            String contentType = networkResponse.header("Content-Type");
            responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
        }

        return responseBuilder.build();
    }

1、如果这个请求有请求体,就添加 Content-Type, Content-Length等。
2、如果这个请求没有 Host,就通过 url 来获取 Host 值添加到 Header中。
3、如果这个请求没有接收的数据类型Accept-Encoding,且没指定接收的数据范围,就添加默认接受格式为 gzip。
5、去 CookieJar 中根据 url 查询 Cookie 添加到 Header。
6、如果当前没有,就添加 User-Agent 信息。
发起请求后:
7、解析响应 Header 中的 Cookie
8、如果想要数据的格式是 gzip,就创建 GzipSource 进行解压,同时移除 Content-Encoding 和 Content-Length

CacheInterceptor(缓存拦截器)、

第三个拦截器是缓存处理拦截器 CacheInterceptor,它的重要性用一句话来描述:最快的请求就是不请求,直接用缓存。

源码:

    @Override
    public Response intercept(Chain chain) throws IOException {
        //todo 通过url的md5数据 从文件缓存查找 (GET请求才有缓存)
        Response cacheCandidate = cache != null
                ? cache.get(chain.request())
                : null;

        long now = System.currentTimeMillis();

        //todo 缓存策略:根据各种条件(请求头)组成 请求与缓存
        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 applicable. Close it.
        }

        //todo 没有网络请求也没有缓存
        //If we're forbidden from using the network and the cache is insufficient, fail.
        if (networkRequest == null && cacheResponse == null) {
            return new Response.Builder()
                    .request(chain.request())
                    .protocol(Protocol.HTTP_1_1)
                    .code(504)
                    .message("Unsatisfiable Request (only-if-cached)")
                    .body(Util.EMPTY_RESPONSE)
                    .sentRequestAtMillis(-1L)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();
        }

        //todo 没有请求,肯定就要使用缓存
        //If we don't need the network, we're done.
        if (networkRequest == null) {
            return cacheResponse.newBuilder()
                    .cacheResponse(stripBody(cacheResponse))
                    .build();
        }

        //todo 去发起请求
        Response networkResponse = null;
        try {
            networkResponse = chain.proceed(networkRequest);
        } finally {
            // If we're crashing on I/O or otherwise, don't leak the cache body.
            if (networkResponse == null && cacheCandidate != null) {
                closeQuietly(cacheCandidate.body());
            }
        }

        // If we have a cache response too, then we're doing a conditional get.
        if (cacheResponse != null) {
            //todo 服务器返回304无修改,那就使用缓存的响应修改了时间等数据后作为本次请求的响应
            if (networkResponse.code() == HTTP_NOT_MODIFIED) {
                Response response = cacheResponse.newBuilder()
                        .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                        .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                        .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                        .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());
            }
        }

        //todo 走到这里说明缓存不可用 那就使用网络的响应
        Response response = networkResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();
        //todo 进行缓存
        if (cache != null) {
            if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response,
                    networkRequest)) {
                // Offer this request to the cache.
                CacheRequest cacheRequest = cache.put(response);
                return cacheWritingResponse(cacheRequest, response);
            }

            if (HttpMethod.invalidatesCache(networkRequest.method())) {
                try {
                    cache.remove(networkRequest);
                } catch (IOException ignored) {
                    // The cache cannot be written.
                }
            }
        }

        return response;
    }

1、根据Request和之前缓存的Response得到CacheStrategy
2、根据CacheStrategy决定是请求网络还是直接返回缓存
3、如果 step 2中决定请求网络,则在这一步将返回的网络响应和本地缓存对比,对本地缓存进行改增删操作

ConnectInterceptor(链接拦截器)

作用:TCP 协议需要需要建立连接才能进行通信,每次请求都建立连接会极大地影响通信效率。
OkHttp 的优点之一优化了连接的建立:内部维护了可以重复使用的 Socket 连接池,减少握手次数,加快请求响应。
源码:

  @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, chain, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();

        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }

1、连接 RealConnection 是对 Socket 的封装。
2、OkHttp 的连接复用主要是通过 StreamAllocation 来实现的,每个连接上持有一个。
3、StreamAllocation 引用的列表,以此来标识当前连接是否空闲。它里面维护了List的引用。List中StreamAllocation的数量也就是socket被引用的计数,如果计数为0的话,说明此连接没有被使用就是空闲的,需要通过下文的算法实现回收;如果计数不为0,则表示上层代码仍然引用,就不需要关闭连接。
4、判断连接是否可以重用,除了比较连接当前的 host,也可以比较路由信息。
5、连接池在添加新连接时会运行清理任务,默认最多空闲连接为 5,最长空闲时间为 5 分钟。

CallServeInteceptor(读写拦截器)

作用:它负责实现网络 IO,真正的与服务器进行链接,所有拦截器都要依赖它才能拿到响应数据。
源码:

@Override
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();

        long sentRequestMillis = System.currentTimeMillis();

       //写入请求头
        realChain.eventListener().requestHeadersStart(realChain.call());
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);

        Response.Builder responseBuilder = null;
        //如果有请求体,将body发送给服务器
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                httpCodec.flushRequest();
                realChain.eventListener().responseHeadersStart(realChain.call());
                //获取响应
                responseBuilder = httpCodec.readResponseHeaders(true);
            }

            if (responseBuilder == null) {
                // Write the request body if the "Expect: 100-continue" expectation was met.
                realChain.eventListener().requestBodyStart(realChain.call());
                long contentLength = request.body().contentLength();
                CountingSink requestBodyOut =
                        new CountingSink(httpCodec.createRequestBody(request, contentLength));
                BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

                request.body().writeTo(bufferedRequestBody);
                bufferedRequestBody.close();
                realChain.eventListener()
                        .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
            } else if (!connection.isMultiplexed()) {
              //如果未满足“Expect: 100-continue”期望,请防止重用 HTTP1 连接。否则,我们仍然有义务传输请求主体以使连接保持一致状态
                streamAllocation.noNewStreams();
            }
        }
        //完成请求
        httpCodec.finishRequest();

        if (responseBuilder == null) {
            realChain.eventListener().responseHeadersStart(realChain.call());
            //解析http的响应的header
            responseBuilder = httpCodec.readResponseHeaders(false);
        }

        Response response = responseBuilder
                .request(request)
                .handshake(streamAllocation.connection().handshake())
                .sentRequestAtMillis(sentRequestMillis)
                .receivedResponseAtMillis(System.currentTimeMillis())
                .build();

        int code = response.code();
        if (code == 100) {
            //即使没有请求,服务器也继续发送100-continue。客户端继续读取
            responseBuilder = httpCodec.readResponseHeaders(false);

            response = responseBuilder
                    .request(request)
                    .handshake(streamAllocation.connection().handshake())
                    .sentRequestAtMillis(sentRequestMillis)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();

            code = response.code();
        }

        realChain.eventListener()
                .responseHeadersEnd(realChain.call(), response);

        if (forWebSocket && code == 101) {
            // Connection is upgrading, but we need to ensure interceptors see a non-null
          // response body.
            response = response.newBuilder()
                    .body(Util.EMPTY_RESPONSE)
                    .build();
        } else {
            response = response.newBuilder()
                    .body(httpCodec.openResponseBody(response))
                    .build();
        }
        //如果请求合着服务器响应的response中的header的connection的值为close,就关闭链接
        if ("close".equalsIgnoreCase(response.request().header("Connection"))
                || "close".equalsIgnoreCase(response.header("Connection"))) {
            //关闭链接
            streamAllocation.noNewStreams();
        }

        if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
           //链接成功,但是响应内容不符合协议
            throw new ProtocolException(
                    "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
        }

        return response;
    }

1、CallServerInterceptor 首先会将请求中的 header 写给服务器。
2、如果有请求体的话,会再将 body 发送给服务器。
3、最后通过httpCodec.finishRequest() 结束 http 请求的发送。
4、然后从连接中读取服务器的响应,并构造 Response。
5、如果请求的 header或服务器响应的 header 中,Connection 的值为 close,就关闭连接。
6、最后返回 Response。

拦截器总结

okhttp使用的是责任链的设计模式,从上到下依次执行。 拦截器的讲解并没有具体的深入探究源码,只介绍了大概的流程和做的事情。有兴趣的同学自己可以深入源码看下

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值