OKHttp 源码解析(一)
创始人
2025-05-29 07:56:01
0

游戏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_1protocols = 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重定向到HTTPSfollowSslRedirects = true;
//是否重定向followRedirects = true;
//连接失败时是否重连,这个重连只有1次retryOnConnectionFailure = true;//连接超时,默认 10sconnectTimeout = 10_000;
//读取超时readTimeout = 10_000;
//写入超时writeTimeout = 10_000;}

2、Request

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

		// 请求地址private HttpUrl url;
// 请求方式,GET(默认) 、POST、DELETE、PUT、PATCH、HEADprivate 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 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 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(), Util.threadFactory("OkHttp Dispatcher", false));}return executorService;}// 队列
// 已经准备好的异步请求
private final Deque readyAsyncCalls = new ArrayDeque<>();// 正在运行的异步请求,包括已经取消的和运行未完成的
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */private final Deque runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */private final Deque 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

相关内容

热门资讯

学习笔记20230319 目录 一、final 二、List和Set 三、HashMap扩容机制原理 四、ArrayList...
arcpy基础篇(2)-访问空... 1.检查数据的存在性 在Python脚本中,可以使用Exists函数来检查当前工作空间...
幼儿园毕业典礼活动方案 关于幼儿园毕业典礼活动方案(通用10篇)  为有力保证事情或工作开展的水平质量,常常需要预先准备方案...
消防应急培训演练方案 消防应急培训演练方案  为了确保工作或事情顺利进行,通常需要预先制定一份完整的方案,方案是综合考量事...
同学聚会活动方案 同学聚会活动方案(精选15篇)  为了确保工作或事情能高效地开展,预先制定方案是必不可少的,方案的内...
国务院关于职工工作时间的规定 (1994年2月3日中华人民共和国国务院令第146号发布 根据1995年3月25日《国...
部门团建活动方案 部门团建活动方案(2篇)  为了确保事情或工作得以顺利进行,常常需要预先制定方案,方案指的是为某一次...
C语言函数:判断字符函数,判断... iscntrl:判断是否是控制字符isspace:判断是否是空白字符...这些函数的参数都是一个字符...
内核实验(八):实现O-NON... 一、篇头 继续使用qemu调试内核的实验。本章复习阻塞与非阻塞IO的概念和机制,然后对...
门店运营计划书 门店运营计划书(通用8篇)  光阴迅速,一眨眼就过去了,又迎来了一个全新的起点,此时此刻我们需要开始...
JavaWeb——Idea模板... Idea模板创建Servlet 第一步  第二步  第三步  此处的Servlet模板也可以定...
综合实践活动方案 综合实践活动方案(通用23篇)  为了确保工作或事情有序地进行,往往需要预先制定好方案,方案是从目的...
青少年体能训练计划方案 青少年体能训练计划方案  青少年体能训练计划方案(通用10篇)  为有力保证事情或工作开展的水平质量...
全国爱牙日活动方案   2014年9月20日是我国第二十四个全国“爱牙日”,为发挥家庭的优势和作用,提高家庭成员口腔保健...
第5讲 cameraserve... 本讲是Android Camera Native Framework专题的第5讲,我们...
11-STM32F1 -DMA... 11-STM32F1 -DMA(1) DMA:Data Memory A...
促销活动方案 实用的促销活动方案集锦9篇  为了确保工作或事情有序地进行,常常需要预先制定方案,方案是书面计划,是...
清明节主题党日活动方案 清明节主题党日活动方案(通用7篇)  为了确保活动有序有效开展,我们需要事先制定活动方案,活动方案是...
施工现场扬尘专项防治方案 施工现场扬尘专项防治方案  什么是方案  方案是从目的、要求、方式、方法、进度等都部署具体、周密,并...
家电促销活动方案 家电促销活动方案通用15篇  为保证事情或工作高起点、高质量、高水平开展,往往需要预先进行方案制定工...