OKHttp 源码解析(一)

游戏SDK架构设计之代码实现——网络框架
OKHttp 源码解析(一)
OKHttp 源码解析(二)拦截器

前言

目前大多数的网络框架都是使用 OKHttp ,Retrofit 也是基于 OKHttp,OKHttp 使用简单,但出现的问题不少,也借此机会了解一下 OKHttp 的源码。

本文的 OKHttp 源码基于 3.4.2 版本。

OKHttp 的官方文档:https://square.github.io/okhttp/

OKHttp 的基本使用流程

在 OKHttp 的源码入口就提示了如何简单实用 OKHttp:

OkHttpClient client = ...   
// 创建 OkHttpClient
OkHttpClient clientWith30sTimeout = client.newBuilder()
																		.readTimeout(30, TimeUnit.SECONDS)
															      .build();
// 调用 newCall,传入请求体,execute执行任务
Response response = clientWith30sTimeout.newCall(request).execute();

1、OkHttpClient

OkHttpClient 采用建造者模式,通过 Builder 去配置初始化变量,没有配置的采用默认值。成员变量如下:

public Builder() {
// 调度器
      dispatcher = new Dispatcher();
			// 协议,默认的有 Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1
      protocols = DEFAULT_PROTOCOLS;
      
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
			connectionSpecs = DEFAULT_CONNECTION_SPECS;
		//域名校验verifyAsIpAddress(host)? verifyIpAddress(host, certificate): verifyHostname(host, certificate)
// 会检验 IP 地址和域名,可以通过这个接口检验更多内容
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
// 身份验证器
      authenticator = Authenticator.NONE;
//连接池,默认 5 个空闲链接,5分钟内不活动被释放
      connectionPool = new ConnectionPool();
//DNS设置
      dns = Dns.SYSTEM;
//是否从HTTP重定向到HTTPS
      followSslRedirects = true;
//是否重定向
      followRedirects = true;
//连接失败时是否重连,这个重连只有1次
      retryOnConnectionFailure = true;
	//连接超时,默认 10s
      connectTimeout = 10_000;
//读取超时
      readTimeout = 10_000;
//写入超时
      writeTimeout = 10_000;
    }

2、Request

这个类也是实用的建造者设计模式,构建请求体,成员变量比较简单。

		// 请求地址
		private HttpUrl url;
// 请求方式,GET(默认) 、POST、DELETE、PUT、PATCH、HEAD
    private String method;
// 请求头,Headers 类来设置请求头内容
    private Headers.Builder headers;
// 请求体,设置 contentType 和 请求参数
    private RequestBody body;
// 标志,用来标志某个线程,可用于取消某个线程请求
    private Object tag;

3、RealCall 请求调用接口

由调用 newCall 返回一个 RealCall 对象

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }

RealCall 实现了 Call 接口,可执行 execute (同步请求) 、enqueue (异步请求)、cancel 方法,是应用端和服务端的桥梁,展示应用端的请求和服务端返回的数据responseCallback

4、同步请求 execute

  1. 判断 Call 是否请求过,一个 Call 只能执行一次,如果已经执行过则抛出异常。

  2. 调用 executed 方法将 Call 添加到队列里。

    client.dispatcher().executed(this);
    
    // 将 Call 添加到队列里
    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
    // 其中 runningSyncCalls
    /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
      private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
  3. 调用 getResponseWithInterceptorChain 构建拦截器链,遍历拦截器,执行请求,执行完成时返回结果。

    // 同步请求
    try {
    			// 调用 executed 方法将 Call 添加到队列里。
          client.dispatcher().executed(this);
    			// 调用 getResponseWithInterceptorChain 构建拦截器链
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } finally {
    			// 将 Call 从队列中移除
          client.dispatcher().finished(this);
        }
    
    private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
    		// 网络
          interceptors.addAll(client.networkInterceptors());
        }
    	// 请求服务
        interceptors.add(new CallServerInterceptor(
            retryAndFollowUpInterceptor.isForWebSocket()));
    
    		// 创建拦截器链
        Interceptor.Chain chain = new RealInterceptorChain(
            interceptors, null, null, null, 0, originalRequest);
    		// 拦截器链执行结果
        return chain.proceed(originalRequest);
      }
    
  4. Call 从队列中移除 client.dispatcher().finished(this);

5、异步请求,当执行异步任务时,由内部类 AsyncCall 实现,AsyncCall 就是一个 Runnable 对象。

执行步骤和同步任务差不多,都会先检查 Call 是否执行过,然后调用 getResponseWithInterceptorChain 构建拦截器链,遍历拦截器,执行请求,执行完成时返回结果。

拦截器的源码解析见下篇文章:

final class AsyncCall extends NamedRunnable {
		// ...
	@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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

}

6、Dispatcher

OKHttp 请求的调度器,内部维护了一个线程池和一些队列。

// 并行的最大请求数,有接口可修改
private int maxRequests = 64;
//每台主机同时执行的最大请求数
private int maxRequestsPerHost = 5;
// 线程池
private 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;
  }

// 队列
// 已经准备好的异步请求
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

// 正在运行的异步请求,包括已经取消的和运行未完成的
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

OKHttp 的请求流程总结

  1. OkHttpClient 通过 newCall 访问 RealCall
    1. 如果是同步请求,调用 execute
    2. 如果是异步请求,调用 enqueue
  2. 以上都是通过DispatcherCall 添加到队列
  3. 调用 getResponseWithInterceptorChain 遍历拦截器,执行请求
  4. 获取结果 getResponse
  5. Call 从 队列移除。

参考文章

  1. 建造者模式:https://blog.csdn.net/a745233700/article/details/83625236
  2. 建造者模式:https://mikechen.cc/8868.html#5种创建型模式
  3. 数据结构——队列:https://www.cnblogs.com/bigsai/p/11363071.html
  4. 数据结构——队列:https://zhuanlan.zhihu.com/p/81018602
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值