OkHttp-3.12.x

文章目录

  • 0.简介
  • 1.从demo说起
  • 2. OkHttpClient源码解析
    • 1)有两种方式创建OkHttpClient:
    • 2)为什么要用建造者模式?
    • 3)看看有哪些属性
    • 4)看看Builder这个内部类
    • 5)再看看OkHttpClient构造方法
    • 3.Request源码解析
  • 4.Call源码解析
    • 1)首先看看是怎么用的
    • 2)这个newCall方法来自于哪?
    • 3)OkHttpClient是怎么调用的?
    • 4)EventListener源码
  • 5.发起请求-同步get请求
    • 1)如何发起请求?
    • 2)okhttp3.Dispatcher#executed
  • 6.发起请求-异步get请求
    • 1)如何发起请求?
    • 2)okhttp3.Dispatcher#enqueue
    • 3)okhttp3.Dispatcher#promoteAndExecute
    • 4)executorService是个啥?
    • 5)okhttp3.RealCall.AsyncCall#executeOn
    • 6)okhttp3.RealCall.AsyncCall#execute
    • 7)okhttp3.Dispatcher属性
  • 7 okhttp3.RealCall#getResponseWithInterceptorChain
  • 8.OkHttp主流程
    • 1)各拦截器作用
    • 2)拦截器链设计好处
  • 9. okhttp3.Interceptor接口
  • 10 自定义拦截器
  • 11 okhttp3.internal.http.RetryAndFollowUpInterceptor拦截器
    • 1)简化代码
    • 2)过程细节
    • 3)okhttp3.internal.http.RetryAndFollowUpInterceptor#recover
  • 12.okhttp3.internal.cache.CacheInterceptor
  • 13.okhttp3.internal.connection.ConnectInterceptor
  • 14 okhttp3.internal.http.CallServerInterceptor
  • 总结
  • 附件
    • 1)demo
    • 2)origin-3.12.x

0.简介

概括起来说OkHttp是一款优秀的HTTP框架,它支持get请求和post请求,支持基于Http的文件上传和下载,支持加载图片,支持下载文件透明的GZIP压缩,支持响应缓存避免重复的网络请求,支持使用连接池来降低响应延迟问题。

特点如下:

  • 基于socket,可以对http进行更完全的封装
  • 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
  • 连接池,减少请求延时
  • 透明的GZIP压缩,减少响应数据的大小
  • 缓存响应内容,避免一些完全重复的请求
  • 当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0。

1.从demo说起

先要说一下,是怎么使用的

package com.meituan.okhttp.client;

import com.meituan.okhttp.client.interceptor.LoggingInterceptor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.BufferedSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;

@Slf4j
public class OkHttpRequest {
	private static final String URL = "http://www.baidu.com";
	private static final MediaType MEDIA_TYPE_JSON = MediaType
			.parse("application/json; charset = utf-8");

	//OkHttpClient client = new OkHttpClient();
	OkHttpClient client = new OkHttpClient.Builder().build();

	public void syncGet() {//同步get方法
		Request request = new Request.Builder().url(URL).build();
		final Call call = client.newCall(request);

		//因为同步阻塞会阻塞主方法的运行,所以另起线程,当然也可以用线程池,还不如用异步get方法
		new Thread(new Runnable() {
			public void run() {
				try {
					Response response = call.execute();//调用call拿到返回结果
					log.info("syncGet: " + response.body().string());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void aSyncGet() {//异步get方法
		Request request = new Request.Builder()
				.url(URL)
				.get()//默认是get 可以不写
				.build();
		Call call = client.newCall(request);

		call.enqueue(new Callback() {//异步发起的请求会被加入到队列中通过线程池来执行。通过回调方式拿到结果
			public void onFailure(Call call, IOException e) {
				log.info("onFailure");
			}

			public void onResponse(Call call, Response response)
					throws IOException {
				Request request1 = call.request();//这个只是说明可以拿到request。
				log.info("request: " + request1);
				log.info("aSyncGet onSuccess: " + response.body().string());
			}
		});

	}

	public void aSyncPost() {//异步post方法
		RequestBody requestBody = new RequestBody() {
			@Nullable
			@Override
			public MediaType contentType() {
				return MediaType
						.parse("application/json; charset = utf-8");
			}

			@Override
			public void writeTo(@NotNull BufferedSink bufferedSink)
					throws IOException {
				bufferedSink.write("zcz".getBytes());

			}
		};
		Request request = new Request.Builder()
				.url(URL)
				.post(requestBody)
				.build();

		client.newCall(request).enqueue(new Callback() {
			@Override
			public void onFailure(@NotNull Call call,
			                      @NotNull IOException e) {
				log.info("onFailure: post");
			}

			@Override
			public void onResponse(@NotNull Call call,
			                       @NotNull Response response) throws IOException {
				log.info("onSuccess: post" + response.body().string());
			}
		});
	}

	public void testInterceptor() {//添加自定义拦截器
		OkHttpClient okHttpClient = 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();
		okHttpClient.newCall(request).enqueue(new Callback() {
			@Override
			public void onFailure(Call call, IOException e) {
				log.info("onFailure: " + e.getMessage());
			}

			@Override
			public void onResponse(Call call, Response response) throws IOException {
				ResponseBody body = response.body();
				if (body != null) {
					log.info("onResponse: " + response.body().string());
					body.close();
				}
			}
		});

	}
}

这里get方法的同步和异步只体现在Call这个对象上,只是调用的方法不同,具体有什么不同呢?这个以后再说。还有一个需要注意的地方就是异步方法通过回调方式拿到Response。

2. OkHttpClient源码解析

1)有两种方式创建OkHttpClient:

OkHttpClient client = new OkHttpClient();
OkHttpClient client = new OkHttpClient.Builder().build();

其实推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。

第一种没什么说的(属性保持默认)。

说一下第二种,看到这个你会想到什么呢?没错!就是builder(建造者)模式,那么问题来了,

2)为什么要用建造者模式?

首先要了解建造者模式。设想一个场景:当一个类有很多属性要初始化,并且大多都保持默认,只需要变动其中的某个或者某几个,如果普通方法需要创建很多的重载的构造方法。建造者模式就是专门来解决这个痛点的。那么怎么解决的呢?

一般情况下,有一个内部类通常叫做Builder,其属性与外部类相同,内部类的的“set方法”,除了set属性之外,还会返回本身。这样做的目的就是可以“链式”设置属性。外部类的构造方法就可以只传入这个内部类(外部类.属性=内部类.属性),而内部类的builder()方法对外部类进行初始化(调用外部类的构造方法),并返回外部类。没错!OkHttpClient就是使用了这种方式,理解了这个,就好理解OkHttpClient这个类了

放源代码太麻烦,放一张结构图,就是上面所述的那样。

3)看看有哪些属性

okhttp3.OkHttpClient:

final Dispatcher dispatcher;    // 调度器
    final @Nullable
    Proxy proxy; // 代理
    final List<Protocol> protocols;  // 协议
    final List<ConnectionSpec> connectionSpecs;  // 传输层版本和连接协议
    final List<Interceptor> interceptors;  // 拦截器
    final List<Interceptor> networkInterceptors;  // 网络拦截器
    final EventListener.Factory eventListenerFactory;
    final ProxySelector proxySelector; // 代理选择器
    final CookieJar cookieJar;  // cookie
    final @Nullable
    Cache cache;  // 缓存
    final @Nullable
    InternalCache internalCache;  // 内部缓存
    final SocketFactory socketFactory;  // socket 工厂
    final SSLSocketFactory sslSocketFactory;  // 安全套接层 socket 工厂,用于 https
    final CertificateChainCleaner certificateChainCleaner; // 验证确认响应证书 适用 HTTPS 请求连接的主机名
    final HostnameVerifier hostnameVerifier; // 主机名字验证
    final CertificatePinner certificatePinner; // 证书链
    final Authenticator proxyAuthenticator; // 代理身份验证
    final Authenticator authenticator; // 本地身份验证
    final ConnectionPool connectionPool;  // 连接池
    final Dns dns;  // 域名
    final boolean followSslRedirects;  // 安全套接层重定向
    final boolean followRedirects;  // 本地重定向
    final boolean retryOnConnectionFailure;  // 重试连接失败
    final int callTimeout;
    final int connectTimeout;
    final int readTimeout;
    final int writeTimeout;
    final int pingInterval;

没错真的有很多属性!属性都有注释,不一一解释,感兴趣的可以细看,注意的是缓存,拦截器,链接池等字眼,以后再解释!

4)看看Builder这个内部类

okhttp3.OkHttpClient.Builder

public static final class Builder {
        Dispatcher dispatcher;//任务调度器
        @Nullable
        Proxy proxy;
        List<Protocol> protocols;
        List<ConnectionSpec> connectionSpecs;
        final List<Interceptor> interceptors = new ArrayList<>();
        final List<Interceptor> networkInterceptors = new ArrayList<>();
        EventListener.Factory eventListenerFactory;
        ProxySelector proxySelector;
        CookieJar cookieJar;
        @Nullable
        Cache cache;
        @Nullable
        InternalCache internalCache;
        SocketFactory socketFactory;
        @Nullable
        SSLSocketFactory sslSocketFactory;
        @Nullable
        CertificateChainCleaner certificateChainCleaner;
        HostnameVerifier hostnameVerifier;
        CertificatePinner certificatePinner;
        Authenticator proxyAuthenticator;
        Authenticator authenticator;
        ConnectionPool connectionPool;
        Dns dns;
        boolean followSslRedirects;
        boolean followRedirects;
        boolean retryOnConnectionFailure;
        int callTimeout;
        int connectTimeout;
        int readTimeout;
        int writeTimeout;
        int pingInterval;

        public Builder() {
            dispatcher = new Dispatcher();//任务调度器
            protocols = DEFAULT_PROTOCOLS;//协议
            connectionSpecs = DEFAULT_CONNECTION_SPECS;
            eventListenerFactory = EventListener.factory(EventListener.NONE);
            proxySelector = ProxySelector.getDefault();
            if (proxySelector == null) {
                proxySelector = new NullProxySelector();
            }
            cookieJar = CookieJar.NO_COOKIES;
            socketFactory = SocketFactory.getDefault();
            hostnameVerifier = OkHostnameVerifier.INSTANCE;
            certificatePinner = CertificatePinner.DEFAULT;
            proxyAuthenticator = Authenticator.NONE;
            authenticator = Authenticator.NONE;
            connectionPool = new ConnectionPool();//链接池
            dns = Dns.SYSTEM;
            followSslRedirects = true;
            followRedirects = true;
            retryOnConnectionFailure = true;
            callTimeout = 0;
            connectTimeout = 10_000;//超时时间
            readTimeout = 10_000;
            writeTimeout = 10_000;
            pingInterval = 0;
        }
        public Builder addInterceptor(Interceptor interceptor) {
            if (interceptor == null)
                throw new IllegalArgumentException("interceptor == null");
            interceptors.add(interceptor);
            return this;
        }

属性一一对应。设值返回this,就像之前所讲的。

5)再看看OkHttpClient构造方法

okhttp3.OkHttpClient#OkHttpClient

public OkHttpClient() {
          this(new Builder());
      }
  OkHttpClient(Builder builder) {
        this.dispatcher = builder.dispatcher;
        this.proxy = builder.proxy;
        this.protocols = builder.protocols;
        this.connectionSpecs = builder.connectionSpecs;
        this.interceptors = Util.immutableList(builder.interceptors);
        this.networkInterceptors = Util
                .immutableList(builder.networkInterceptors);
        this.eventListenerFactory = builder.eventListenerFactory;
        this.proxySelector = builder.proxySelector;
        this.cookieJar = builder.cookieJar;
        this.cache = builder.cache;
        this.internalCache = builder.internalCache;
        this.socketFactory = builder.socketFactory;

        boolean isTLS = false;
        for (ConnectionSpec spec : connectionSpecs) {
            isTLS = isTLS || spec.isTls();
        }

        if (builder.sslSocketFactory != null || !isTLS) {
            this.sslSocketFactory = builder.sslSocketFactory;
            this.certificateChainCleaner = builder.certificateChainCleaner;
        } else {
            X509TrustManager trustManager = Util.platformTrustManager();
            this.sslSocketFactory = newSslSocketFactory(trustManager);
            this.certificateChainCleaner = CertificateChainCleaner
                    .get(trustManager);
        }

        if (sslSocketFactory != null) {
            Platform.get().configureSslSocketFactory(sslSocketFactory);
        }

        this.hostnameVerifier = builder.hostnameVerifier;
        this.certificatePinner = builder.certificatePinner
                .withCertificateChainCleaner(certificateChainCleaner);
        this.proxyAuthenticator = builder.proxyAuthenticator;
        this.authenticator = builder.authenticator;
        this.connectionPool = builder.connectionPool;
        this.dns = builder.dns;
        this.followSslRedirects = builder.followSslRedirects;
        this.followRedirects = builder.followRedirects;
        this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
        this.callTimeout = builder.callTimeout;
        this.connectTimeout = builder.connectTimeout;
        this.readTimeout = builder.readTimeout;
        this.writeTimeout = builder.writeTimeout;
        this.pingInterval = builder.pingInterval;

        if (interceptors.contains(null)) {
            throw new IllegalStateException(
                    "Null interceptor: " + interceptors);
        }
        if (networkInterceptors.contains(null)) {
            throw new IllegalStateException(
                    "Null network interceptor: " + networkInterceptors);
        }
    }

这个类到此为止,感兴趣可以自己看。

3.Request源码解析

由于也使用了建造者模式,细节就不分析了,只分析属性,大部分可以见名知意,难理解的已经标注

public final class Request {
    final HttpUrl url;
    final String method;
    final Headers headers;
    final @Nullable
    RequestBody body;
    //通过tags来同时取消多个请求。当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。
    // 之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call。
    final Map<Class<?>, Object> tags;

    //相当于请求头中的Cache-Control:
    private volatile @Nullable CacheControl cacheControl; // Lazily initialized.

    Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;//方法,默认get
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tags = Util.immutableMap(builder.tags);
    }
  • 设置url。可以是String类型、URL类型和HttpUrl类型。最终都是用到HttpUrl类型。
  • 设置method,包含get、post方法等。默认的是get方法。post方法要传RequestBody,类似的还有delete、put、patch。
  • 设置header,方法有addHeader(String name, String value)、 removeHeader(String name)、header(String name, String value)、headers(Headers headers)。headers(Headers,headers)调用之后其它的header都会被移除,只添加这一个header。而header(String name, String value)方法调用之后,其它与这个name同名的header都会被移除,只保留这一个header。
  • 设置tag,设置tag可以用来取消这一请求。如果未指定tag或者tag为null,那么这个request本身就会当做是一个tag用来被取消请求。
  • 设置cacheControl,这个是设置到请求头中。用来替换其它name是"Cache-Control"的header。如果cacheControl是空的话就会移除请求头中name是"Cache-Control"的header。

4.Call源码解析

分析完OkHttpClient和Request,继续看demo

1)首先看看是怎么用的

 Call call = client.newCall(request);

每一个请求任务都封装为一个Call,其实现为RealCall。

2)这个newCall方法来自于哪?

okhttp3.Call.Factory#newCall

public interface Call extends Cloneable {
...
interface Factory {
        Call newCall(Request request);
    }
}

3)OkHttpClient是怎么调用的?

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
....
		@Override
    public Call newCall(Request request) {//实现接口,重写方法
        return RealCall.newRealCall(this, request, false /* for web socket */);//默认实现
    }
}

其实是调用okhttp3.RealCall#newRealCall这个方法实现的。

		static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // Safely publish the Call instance to the EventListener.
        RealCall call = new RealCall(client, originalRequest, forWebSocket);//调用下面的方法
        call.eventListener = client.eventListenerFactory().create(call);//必定会添加事件监听器,用于跟踪等
        return call;
    }
    
    private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.forWebSocket = forWebSocket;
        //创建了一个RetryAndFollowUpInterceptor,用于处理请求错误和重定向等,
        // 这是 Okhttp 框架的精髓 interceptor chain 中的一环,
        // 默认情况下也是第一个拦截器,除非调用 OkHttpClient.Builder#addInterceptor(Interceptor) 来添加全局的拦截器。
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
        this.timeout = new AsyncTimeout() {
            @Override
            protected void timedOut() {
                cancel();
            }
        };
        this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
    }

4)EventListener源码

创建call的时候添加了一个事件监听器,看看源码是做什么的,这里包含了很多方法。其实可以理解为"call"的生命周期。不仔细看了。

在OkHttpClient中,我们可以传入EventListener的工厂方法,为每一个请求创建一个EventListener,来接收非常细的事件回调

5.发起请求-同步get请求

1)如何发起请求?

看demo

Response response = call.execute();//调用call拿到返回结果

之前分析过,这里的call其实是RealCall,直接看其execute()方法,okhttp3.RealCall#execute

/**
     * 检测这个 call 是否已经执行了,保证每个 call 只能执行一次。
     * 通知 dispatcher 已经进入执行状态,将 call 加入到 runningSyncCalls 队列中。
     * 调用 getResponseWithInterceptorChain() 函数获取 HTTP 返回结果。
     * 最后还要通知 dispatcher 自己已经执行完毕,将 call 从 runningSyncCalls 队列中移除。
     * @return
     * @throws IOException
     */
    @Override
    public Response execute() throws IOException {//模板方法实现
        synchronized (this) {
            // 每个 call 只能执行一次
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        timeout.enter();
        eventListener.callStart(this);
        try {
            // 请求开始, 将自己加入到runningSyncCalls队列中
            client.dispatcher().executed(this);
            // 通过一系列拦截器请求处理和响应处理得到最终的返回结果
            Response result = getResponseWithInterceptorChain();//调用 getResponseWithInterceptorChain()获得响应内容
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            e = timeoutExit(e);
            eventListener.callFailed(this, e);
            throw e;
        } finally {
            // 请求完成, 将其从runningSyncCalls队列中移除
            client.dispatcher().finished(this);
        }
    }

2)okhttp3.Dispatcher#executed

/** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

所以其实这里只是加入到一个队列里面,并没有发起请求,发起请求还是通过getResponseWithInterceptorChain()实现的。这里先卖一下关子,一会再讲这个方法,先讲一下异步的方式,因为都会调用这个方法。

6.发起请求-异步get请求

1)如何发起请求?

看demo

call.enqueue(new Callback() {//异步发起的请求会被加入到队列中通过线程池来执行。通过回调方式拿到结果
			public void onFailure(Call call, IOException e) {
				log.info("onFailure");
			}

			public void onResponse(Call call, Response response)
					throws IOException {
				Request request1 = call.request();//这个只是说明可以拿到request。
				log.info("request: " + request1);
				log.info("aSyncGet onSuccess: " + response.body().string());
			}
		});

在异步请求中,我们通过Callback来获得简单清晰的请求回调(onFailure、onResponse)

其实是调用okhttp3.RealCall#enqueue这个方法实现的。

@Override
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");//每个请求只能执行一次
            executed = true;
        }
        captureCallStackTrace();//为retryAndFollowUpInterceptor加入了一个用于追踪堆栈信息的callStackTrace
        eventListener.callStart(this);//之前加的listener,在这里调用
        //这里创建了一个 AsyncCall 并将Callback传入,接着再交给任务分发器 Dispatcher 来进一步处理。
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

2)okhttp3.Dispatcher#enqueue

void enqueue(AsyncCall call) {
        // 将AsyncCall加入到准备异步调用的队列中
        synchronized (this) {
            readyAsyncCalls.add(call);
        }
        promoteAndExecute();
    }

3)okhttp3.Dispatcher#promoteAndExecute

/**
     * Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
     * them on the executor service. Must not be called with synchronization because executing calls
     * can call into user code.
     * 

* 将 readyAsyncCalls 中的任务移动到 runningAsyncCalls中,并交给线程池来执行。 *

* 从准备异步请求的队列中取出可以执行的请求(正在运行的异步请求不得超过64,同一个host下的异步请求不得超过5个), * 加入到 executableCalls 列表中。 * 循环 executableCalls 取出请求 AsyncCall 对象,调用其 executeOn 方法。 * * @return true if the dispatcher is currently running calls. */ private boolean promoteAndExecute() { assert (!Thread.holdsLock(this)); List<AsyncCall> executableCalls = new ArrayList<>(); boolean isRunning; synchronized (this) { //若条件允许,将readyAsyncCalls中的任务移动到runningAsyncCalls中,并交给线程池执行 for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall asyncCall = i.next(); if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.大于最大请求数64,跳出 if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity. i.remove(); executableCalls.add(asyncCall); runningAsyncCalls.add(asyncCall); } isRunning = runningCallsCount() > 0; } for (int i = 0, size = executableCalls.size(); i < size; i++) { AsyncCall asyncCall = executableCalls.get(i); asyncCall.executeOn(executorService());//传入线程池 } return isRunning; }

异步发起的请求会被加入到 Dispatcher 中的 runningAsyncCalls双端队列中通过线程池来执行。

4)executorService是个啥?

okhttp3.Dispatcher#executorService

 /**
     * 这个线程池没有核心线程,线程数量没有限制,空闲60s就会回收,适用于大量耗时较短的任务;
     * 与 Executors.newCachedThreadPool() 比较类似;
     * 

* 虽然线程池无任务上限,但是Dispatcher对入口enqueue()进行了把关, * 最大的异步任务数默认是64,同一个主机默认是5,当然这两个默认值是可以修改的,Dispatcher提供的修改接口; */ 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; }

5)okhttp3.RealCall.AsyncCall#executeOn

void executeOn(ExecutorService executorService) {
            assert (!Thread.holdsLock(client.dispatcher()));
            boolean success = false;
            try {
                executorService.execute(this);//这里的this指AsyncCall
                success = true;
            } catch (RejectedExecutionException e) {
                InterruptedIOException ioException = new InterruptedIOException("executor rejected");
                ioException.initCause(e);
                eventListener.callFailed(RealCall.this, ioException);
                responseCallback.onFailure(RealCall.this, ioException);
            } finally {
                if (!success) {
                    client.dispatcher().finished(this); // This call is no longer running!
                }
            }
        }

AsyncCall必然实现Runable接口,因此反过来看其run()方法,看引用(okhttp3.RealCall.AsyncCall#executeOn)知道AsyncCall其实是RealCall的一个内部类

现实是AsyncCall并没有直接实现Runable接口

final class AsyncCall extends NamedRunnable {
...
}

但是其父类NamedRunnable实现了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();
}

这是一个模板方法的典型实现,因此需要看子类的execute()方法

6)okhttp3.RealCall.AsyncCall#execute

 @Override
        protected void execute() {
            boolean signalledCallback = false;
            timeout.enter();
            try {
                //异步和同步走的是同样的方式,只不过在子线程中执行
                // 请求网络获取结果
                Response response = getResponseWithInterceptorChain();//调用 getResponseWithInterceptorChain()获得响应内容
                signalledCallback = true;//这个标记主要是避免异常时2次回调
                responseCallback.onResponse(RealCall.this, response);//回调Callback,将响应内容传回去
            } catch (IOException e) {
                e = timeoutExit(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); //回调Callback告知失败
                }
            } catch (Throwable t) {
                cancel();
                if (!signalledCallback) {
                    IOException canceledException = new IOException("canceled due to " + t);
                    responseCallback.onFailure(RealCall.this, canceledException);
                }
                throw t;
            } finally {
                //不管请求成功与否,都进行finished()操作
                // 调度完成,移出队列
                client.dispatcher().finished(this);
            }
        }

好了,同步和异步都要调用okhttp3.RealCall#getResponseWithInterceptorChain这个方法获取Response。

之前说了很多队列,这里总结一下,其实是三个队列,都是在okhttp3.Dispatcher类里面

7)okhttp3.Dispatcher属性

public final class Dispatcher {
    private int maxRequests = 64;//最大请求数量
    private int maxRequestsPerHost = 5;//每台主机最大的请求数量
    private @Nullable Runnable idleCallback;

    /**
     * Executes calls. Created lazily.
     */
    private @Nullable
    ExecutorService executorService;//线程池,跟CachedThreadPool非常类似,这种类型的线程池,适用于大量的耗时较短的异步任务。

 
    //异步请求,Dispatcher 是通过启动 ExcuteService 执行,线程池的最大并发量 64,
    // 异步请求先放置在 readyAsyncCalls,可以执行时放到 runningAsyncCalls 中,执行结束从runningAsyncCalls 中移除。
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//准备执行的请求队列

    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在运行的请求队列

    //同步请求,由于它是即时运行的, Dispatcher 只需要运行前请求前存储到 runningSyncCalls,
    // 请求结束后从 runningSyncCalls 中移除即可。
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//一个正在运行的同步请求队列
...
}

7 okhttp3.RealCall#getResponseWithInterceptorChain

/**
 * 这里先是创建了一个 Interceptor 的ArrayLIst,然后将各类 interceptor 全部加入到ArrayLIst中,之后按照顺序执行。包含以下 interceptor:
 *
 * interceptors:配置 OkHttpClient 时设置的 inteceptors
 * RetryAndFollowUpInterceptor:负责失败重试以及重定向
 * BridgeInterceptor:负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应
 * CacheInterceptor:负责读取缓存直接返回、更新缓存
 * ConnectInterceptor:负责和服务器建立连接
 * networkInterceptors:配置 OkHttpClient 时设置的 networkInterceptors
 * CallServerInterceptor:负责向服务器发送请求数据、从服务器读取响应数据
 * @return
 * @throws IOException
 */
//拦截器链
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();//这是一个List,是有序的
    interceptors.addAll(client.interceptors());//首先添加的是用户添加的全局拦截器
    interceptors.add(retryAndFollowUpInterceptor);//错误、重定向拦截器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));//封装request和response拦截器,桥接拦截器,桥接应用层与网络层,添加必要的头、
    //构造这个拦截器的时候传入的是我们构造的OkHttpClient中设置的interanlCache,
    // 而当我们用默认方式构造OkHttpClient的时候是不会创建缓存的,也就是internalCache=null的
    interceptors.add(new CacheInterceptor(client.internalCache()));//缓存处理,Last-Modified、ETag、DiskLruCache等
    interceptors.add(new ConnectInterceptor(client));//负责和服务器建立连接拦截器
    //从这就知道,通过okHttpClient.Builder#addNetworkInterceptor()传进来的拦截器只对非网页的请求生效
    //配置 OkHttpClient 时设置的 networkInterceptors
    if (!forWebSocket) {
        interceptors.addAll(client.networkInterceptors());
    }
    //真正访问服务器的拦截器
    //负责向服务器发送请求数据、从服务器读取响应数据(实际网络请求)
    interceptors.add(new CallServerInterceptor(forWebSocket));

			//注意这里的0,即从头开始执行,这里传入的参数很多都是null,之后再创建
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
            originalRequest, this, eventListener, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());

    //最后通过RealInterceptorChain#proceed(Request)来执行整个 interceptor chain
    Response response = chain.proceed(originalRequest);
    if (retryAndFollowUpInterceptor.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
    }
    return response;
}

OkHttp的拦截器链可谓是其整个框架的精髓,用户可传入的 interceptor 分为两类:
①一类是全局的 interceptor,该类 interceptor 在整个拦截器链中最早被调用,通过 OkHttpClient.Builder#addInterceptor(Interceptor) 传入;
②另外一类是非网页请求的 interceptor ,这类拦截器只会在非网页请求中被调用,并且是在组装完请求之后,真正发起网络请求前被调用,所有的 interceptor 被保存在 List interceptors 集合中,按照添加顺序来逐个调用,具体可参考 RealCall#getResponseWithInterceptorChain() 方法。通过 OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 传入;

这里举一个简单的例子,例如有这样一个需求,我要监控App通过 OkHttp 发出的所有原始请求,以及整个请求所耗费的时间,针对这样的需求就可以使用第一类全局的 interceptor 在拦截器链头去做。

在OkHttp3中,其灵活性很大程度上体现在可以 intercept 其任意一个环节,而这个优势便是okhttp3整个请求响应架构体系的精髓所在,先放出一张主框架请求流程图,接着再分析源码。

8.OkHttp主流程

OkHttp-3.12.x_第1张图片
简述 OkHttp 的请求流程:

  • OkHttpClient 实现了 Call.Fctory,负责为 Request 创建 Call。

  • RealCall 是 Call 的具体实现,它的异步请求是通过 Dispatcher 调度器利用ExcutorService实现,而最终进行网络请求时和同步请求一样,都是通过 getResponseWithInterceptorChain 方法实现。

  • getResponseWithInterceptorChain 方法中采用了责任链模式,每一个拦截器各司其职,主要做两件事。

拦截上一层拦截器封装好的 Request,然后自身对这个 Request 进行处理,处理后向下传递。

接收下一层拦截器传递回来的 Response,然后自身对 Response 进行处理,返回给上一层。

1)各拦截器作用

retryAndFollowUpInterceptor——失败和重定向过滤器BridgeInterceptor——封装request和response过滤器CacheInterceptor——缓存相关的过滤器,负责读取缓存直接返回、更新缓存ConnectInterceptor——负责和服务器建立连接,连接池等networkInterceptors——配置 OkHttpClient 时设置的 networkInterceptorsCallServerInterceptor——负责向服务器发送请求数据、从服务器读取响应数据(实际网络请求)

2)拦截器链设计好处

高内聚,低耦合,可扩展,单一职责。

9. okhttp3.Interceptor接口

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */
public interface Interceptor {
    Response intercept(Chain chain) throws IOException;//主要方法

    interface Chain {
        Request request();

        Response proceed(Request request) throws IOException;

        /**
         * Returns the connection the request will be executed on. This is only available in the chains
         * of network interceptors; for application interceptors this is always null.
         */
        @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);
    }
}

各个拦截器都实现了这个接口,如果想自己扩展也可以实现该接口比如

10 自定义拦截器

com.meituan.okhttp.client.interceptor.LoggingInterceptor

package com.meituan.okhttp.client.interceptor;

import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
@Slf4j
public class LoggingInterceptor implements Interceptor {
	private static final String TAG = "LoggingInterceptor";
//	private static final Logger logger = LogManager.getLogger();
	@NotNull
	@Override
	public Response intercept(@NotNull Chain chain)
			throws IOException {
		Request request = chain.request();
		long startTime = System.nanoTime();
		Response response = chain.proceed(request);
		log.info("time" + (System.nanoTime()-startTime));
		return response;
	}

}

com.meituan.okhttp.client.OkHttpRequest#testInterceptor

public void testInterceptor() {//添加自定义拦截器
		OkHttpClient okHttpClient = 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();
		okHttpClient.newCall(request).enqueue(new Callback() {
			@Override
			public void onFailure(Call call, IOException e) {
				log.info("onFailure: " + e.getMessage());
			}

			@Override
			public void onResponse(Call call, Response response) throws IOException {
				ResponseBody body = response.body();
				if (body != null) {
					log.info("onResponse: " + response.body().string());
					body.close();
				}
			}
		});

	}

11 okhttp3.internal.http.RetryAndFollowUpInterceptor拦截器

其interceptor方法很长,但是简化以后

1)简化代码

@Override public Response intercept(Chain chain) throws IOException {
    。。。
    while (true) {
    。。。
      try {
        response = realChain.proceed(request, streamAllocation, null, null);
      }
    。。。
    if(满足条件){
        return response;
    }
    。。。
      //不满足条件,一顿操作,赋值再来!
      request = followUp;
      priorResponse = response;
    }
  }

其实就流程来上,我认为宏观上代码缩减到这样就够了,甚至可以再删点,这里先从流程上理解.其实代码成这样,基本上大家都能理解了,一个while(true)表明这是个循环体,循环体主要做的事可以看到其实是递归的主要方法。

response = realChain.proceed(request, streamAllocation, null, null);

执行了这个方法后,就会交给下一个过滤器继续执行,所以单从这里来看,我们可以简单的理解为这个过滤器其实没做什么。但是当出现了一些问题,导致不满足条件的时候,就需要进行一系列的操作,重新复制Request,重新请求,这也就是while的功能,对应的也就是这个过滤器的主要功能:重试和重定向。这里我们宏观上已经对RetryAndFollowUpInterceptor有了一个基本的理解了。

2)过程细节

@Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Call call = realChain.call();
        EventListener eventListener = realChain.eventListener();

        //创建一个StreamAllocation,刚传入的时候是null,这个类大概可以理解为是处理Connections,Streams,Calls三者之间的关系
        StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
                createAddress(request.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;

        //统计重定向次数,不能大于20
        int followUpCount = 0;
        Response priorResponse = null;
        while (true) {
            //取消
            if (canceled) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response;
            boolean releaseConnection = true;//初始化时赋值为true
            try {
                //调用下一个interceptor的来获得响应内容
                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.getFirstConnectException();
                }
                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.
                //没有捕获到的异常,最终要释放
                //由于releaseConnection初始化为true,
                // 而当正常执行realChain.proceed或在执行过程中捕捉到异常时设置为false,
                // 所以当执行过程中捕捉到没有检测到的异常时,需要释放一些内容。

                if (releaseConnection) {
                    streamAllocation.streamFailed(null);
                    streamAllocation.release();
                }
            }

            // Attach the prior response if it exists. Such responses never have a body.
            //这里基本上都没有讲,priorResponse是用来保存前一个Resposne的,
            // 这里可以看到将前一个Response和当前的Resposne结合在一起了,
            // 对应的场景是,当获得Resposne后,发现需要重定向,则将当前Resposne设置给priorResponse,再执行一遍流程,
            //直到不需要重定向了,则将priorResponse和Resposne结合起来。
            if (priorResponse != null) {
                response = response.newBuilder()
                        .priorResponse(priorResponse.newBuilder()
                                .body(null)
                                .build())
                        .build();
            }

            Request followUp;
            try {
                //重定向处理
                //判断是否需要重定向,如果需要重定向则返回一个重定向的Request,没有则为null
                followUp = followUpRequest(response, streamAllocation.route());
            } catch (IOException e) {
                streamAllocation.release();
                throw e;
            }

            if (followUp == null) {//不需要重定向
                //是WebSocket,释放
                streamAllocation.release();
                return response;//返回response
            }

            //需要重定向,关闭响应流
            closeQuietly(response.body());

            //重定向次数++,并且小于最大重定向次数MAX_FOLLOW_UPS(20)
            if (++followUpCount > MAX_FOLLOW_UPS) {
                streamAllocation.release();
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }
            //是UnrepeatableRequestBody, 刚才看过也就是是流类型,没有被缓存,不能重定向
            if (followUp.body() instanceof UnrepeatableRequestBody) {
                streamAllocation.release();
                throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
            }
            //判断是否相同,不然重新创建一个streamConnection
            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;
        }
    }

3)okhttp3.internal.http.RetryAndFollowUpInterceptor#recover

/**
     * Report and attempt to recover from a failure to communicate with a server. Returns true if
     * {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only
     * be recovered if the body is buffered or if the failure occurred before the request has been
     * sent.
     */
    private boolean recover(IOException e, StreamAllocation streamAllocation,
                            boolean requestSendStarted, Request userRequest) {
        streamAllocation.streamFailed(e);

        // The application layer has forbidden retries.
        //如果OkHttpClient直接配置拒绝失败重连,return false,默认时true
        if (!client.retryOnConnectionFailure()) return false;

        // We can't send the request body again.
        //如果请求已经发送,并且这个请求体是一个UnrepeatableRequestBody类型,则不能重试。
        //StreamedRequestBody实现了UnrepeatableRequestBody接口,是个流类型,不会被缓存,所以只能执行一次,具体可看。
        if (requestSendStarted && requestIsUnrepeatable(e, userRequest)) return false;

        // This exception is fatal.
        //一些严重的问题,就不要重试了
        if (!isRecoverable(e, requestSendStarted)) return false;

        // No more routes to attempt.
        //没有更多的路由就不要重试了
        if (!streamAllocation.hasMoreRoutes()) return false;

        // For failure recovery, use the same route selector with a new connection.
        return true;
    }

12.okhttp3.internal.cache.CacheInterceptor

/**
     * 1.通过Request尝试到Cache中拿缓存(里面非常多流程),当然前提是OkHttpClient中配置了缓存,默认是不支持的。
     * 2.根据response,time,request创建一个缓存策略,用于判断怎样使用缓存。
     * 3.如果缓存策略中设置禁止使用网络,并且缓存又为空,则构建一个Resposne直接返回,注意返回码=504
     * 4.缓存策略中设置不使用网络,但是又缓存,直接返回缓存
     * 5.接着走后续过滤器的流程,chain.proceed(networkRequest)
     * 6.当缓存存在的时候,如果网络返回的Resposne为304,则使用缓存的Resposne。
     * 7.构建网络请求的Resposne
     * 8.当在OKHttpClient中配置了缓存,则将这个Resposne缓存起来。
     * 9.缓存起来的步骤也是先缓存header,再缓存body。
     * 10.返回Resposne。
     *
     *
     * @param chain
     * @return
     * @throws IOException
     */
    @Override
    public Response intercept(Chain chain) throws IOException {
        //1.尝试通过这个Request拿缓存
        //默认cache为null,可以配置cache,不为空尝试获取缓存中的response
        Response cacheCandidate = cache != null
                ? cache.get(chain.request())
                : null;

        long now = System.currentTimeMillis();

        //根据response,time,request创建一个缓存策略,用于判断怎样使用缓存
        //对应的是CacheStrategy这个类,里面主要涉及Http协议中缓存的相关设置
        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.
        }

        // If we're forbidden from using the network and the cache is insufficient, fail.
        //2.如果不允许使用网络并且缓存为空,新建一个504的Resposne返回。
        //如果缓存策略中禁止使用网络,并且缓存又为空,则构建一个Resposne直接返回,注意返回码=504
        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();
        }

        // If we don't need the network, we're done.
        //不使用网络,但是又缓存,直接返回缓存
        if (networkRequest == null) {
            return cacheResponse.newBuilder()
                    .cacheResponse(stripBody(cacheResponse))
                    .build();
        }

        Response networkResponse = null;
        try {
            //调用下一个拦截器进行网络请求
            //3.如果不允许使用网络,但是有缓存,返回缓存。
            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.
        //4.链式调用下一个过滤器。
        networkResponse = chain.proceed(networkRequest);

        //当缓存响应和网络响应同时存在的时候,选择用哪个
        if (cacheResponse != null) {
            if (networkResponse.code() == HTTP_NOT_MODIFIED) {
                //如果返回码是304,客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户
                // 只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
                //则使用缓存的响应
                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());
            }
        }

        //使用网络响应
        //6、7.使用网络请求得到的Resposne,并且将这个Resposne缓存起来(前提当然是能缓存)。
        // 接下来就是脑袋都大的细节了,我也不敢说分析的十分详细,只能就我的理解总体分析,学习。
        Response response = networkResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();

        //所以默认创建的OkHttpClient是没有缓存的
        //存缓存这个步骤的分析了,其实前面的取缓存的分析结束后,
        // 这里对存缓存不难猜测其实是想对应的,也就比较好理解了,对应的大体应该是header存入clean[0],body存入clean[1]。
        if (cache != null) {
            //9。 将响应缓存
            if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
                // Offer this request to the cache.
                //缓存Resposne的Header信息
                CacheRequest cacheRequest = cache.put(response);
                //缓存body
                return cacheWritingResponse(cacheRequest, response);
            }

            //只能缓存GET....不然移除request
            if (HttpMethod.invalidatesCache(networkRequest.method())) {
                try {
                    cache.remove(networkRequest);
                } catch (IOException ignored) {
                    // The cache cannot be written.
                }
            }
        }

        return response;
    }

13.okhttp3.internal.connection.ConnectInterceptor

@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 httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        //获取连接
        RealConnection connection = streamAllocation.connection();

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

14 okhttp3.internal.http.CallServerInterceptor

@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();

        //开始写入header
        realChain.eventListener().requestHeadersStart(realChain.call());
        //HttpCodec其实是一个接口,对应的使用策略模式分别根据是Http还是Http/2请求,这里就看一下Http1Codec的实现吧。
        httpCodec.writeRequestHeaders(request);//1.开始写入header
        //写入结束
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);

        Response.Builder responseBuilder = null;//首先构建了一个null的responseBuilder。
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
            // Continue" response before transmitting the request body. If we don't get that, return
            // what we did get (such as a 4xx response) without ever transmitting the request body.
            //2.当Header为Expect: 100-continue时,只发送请求头
//            当Header为Expect: 100-continue时,只发送请求头
//            发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
//            接收到Server返回的100-continue应答以后, 才把数据POST给Server

//            1.如果可以继续请求,则responseBuilder=null
//            2.如果不行,则responseBuilder不为空,并且为返回的Header
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                httpCodec.flushRequest();//刷新请求
                realChain.eventListener().responseHeadersStart(realChain.call());
                //读取response的header信息,并返回一个responseBuilder赋值给responseBuilder。
                responseBuilder = httpCodec.readResponseHeaders(true);
            }

            //如果可以继续请求,则Responsebuilder=null,执行if判断里的内容,可以看到就是对于请求体的写入操作,当然任然是使用Okio进行写入操作。
            if (responseBuilder == null) {
                // Write the request body if the "Expect: 100-continue" expectation was met.
                //得到响应后,根据Resposne判断是否写入请求体
                // 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);

                //3.写入请求体
                request.body().writeTo(bufferedRequestBody);
                //写入完成
                bufferedRequestBody.close();
                realChain.eventListener()
                        .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
            } else if (!connection.isMultiplexed()) {
                // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
                // from being reused. Otherwise we're still obligated to transmit the request body to
                // leave the connection in a consistent state.
                streamAllocation.noNewStreams();
            }
        }

        //4.结束请求
        httpCodec.finishRequest();

        if (responseBuilder == null) {
            realChain.eventListener().responseHeadersStart(realChain.call());
            //5.得到响应头
            responseBuilder = httpCodec.readResponseHeaders(false);
        }

        //6.构建初步响应
//        通过返回得到的responseBuilder构建携带有响应头的Reponse。
        Response response = responseBuilder
                .request(request)
                .handshake(streamAllocation.connection().handshake())
                .sentRequestAtMillis(sentRequestMillis)
                .receivedResponseAtMillis(System.currentTimeMillis())
                .build();

        int code = response.code();
        if (code == 100) {
            // server sent a 100-continue even though we did not request one.
            // try again to read the actual response
            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);

//        剩下的其实就是对弈返回码的判断,对应正常的返回码的话,构建响应体到Response中,最后将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();
        }

        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.首先发现了计算机网络的重要性,Http协议,准备后面入一本书,再回顾学习一下。
2.线程安全各种方式,各种数据结构。
3.设计模式,粗略回顾一下:建造者模式,责任链模式,策略模式…其实不用强行使用设计模式,其实主要是设计思想
4.面向接口编程,这个不用说,越看越重要。
5.方法的命名,其实我感觉挺有趣,也挺讲究的。
6.注释,其实和5差不多,基本上就是编程风格了。

附件

1)demo

https://github.com/ouyangxizhu/okhttp-demo.git

2)origin-3.12.x

https://github.com/ouyangxizhu/okhttp-origin-3.12.x.git

你可能感兴趣的:(okhttp,java)