
在生产实践中,浅出常常会遇到这样的源码用实场景 :需要针对某一类 Http 请求做统一的处理,例如在 Header 里添加请求参数或者修改请求响应等等 。解析及应践这类问题的深入一种比较优雅的解决方案是使用拦截器来对请求和响应做统一处理。
在 Android 和 Java 世界里 OkHttp 凭借其高效性和易用性被广泛使用。浅出作为一款优秀的源码用实开源 Http 请求框架,深入了解它的解析及应践实现原理,可以学习优秀软件的深入设计和编码经验,香港云服务器帮助我们更好到地使用它的浅出特性 ,并且有助于特殊场景下的源码用实问题排查。本文尝试从源代码出发探究 OkHttp 的解析及应践基本原理 ,并列举了一个简单的深入例子说明拦截器在我们项目中的实际应用 。本文源代码基于 OkHttp 3.10.0 。浅出
OkHttp 可以用来发送同步或异步的请求 ,异步请求与同步请求的主要区别在于异步请求会交由线程池来调度请求的执行 。高防服务器使用 OkHttp 发送一个同步请求的代码相当简洁,示例代码如下 :
同步 GET 请求示例
复制// 1.创建OkHttpClient客户端 OkHttpClient client = new OkHttpClient(); public String getSync(String url) throws IOException { OkHttpClient client = new OkHttpClient(); // 2.创建一个Request对象 Request request = new Request.Builder() .url(url) .build(); // 3.创建一个Call对象并调用execute()方法 try (Response response = client.newCall(request).execute()) { return response.body().string(); } }1.2.3.4.5.6.7.8.9.10.11.12.13.其中 execute() 方法是请求发起的入口 ,RealCall 对象的 execute() 方法的源代码如下:
RealCall 的 execute() 方法源代码
复制@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); // 将当前对象从“运行中”队列移除 } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.execute() 方法首先将当前请求标记为“已执行”,然后会为重试跟踪拦截器添加堆栈追踪信息,接着事件监听器记录“调用开始”事件,调度器将当前对象放入“运行中”队列 ,之后通过拦截器发起调用并获取响应,最后在 finally 块中将当前请求从“运行中”队列移除 ,异常发生时事件监听器记录“调用失败”事件。建站模板其中关键的方法 是
getResponseWithInterceptorChain() 其源代码如下 :
复制Response getResponseWithInterceptorChain() throws IOException { // 构建一个全栈的拦截器列表 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, ……); return chain.proceed(originalRequest); }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.该方法中按照特定的顺序创建了一个有序的拦截器列表 ,之后使用拦截器列表创建拦截器链并发起 proceed() 方法调用。在chain.proceed() 方法中会使用递归的方式将列表中的拦截器串联起来依次对请求对象进行处理。拦截器链的实现是 OkHttp 的一个巧妙所在,在后文我们会用一小节专门讨论。在继续往下分析之前 ,源码库通过以上的代码片段我们已经大致看到了一个请求发起的整体流程 。
2.2 OkHttp 核心执行流程一个 OkHttp 请求的核心执行过程如以下流程图所示 :

图 2-1 OkHttp请求执行流程图
图中各部分的含义和作用如下 :
OkHttpClient:是整个 OkHttp 的核心管理类,从面向对象的抽象表示上来看它代表了客户端本身 ,是请求的调用工厂,用来发送请求和读取响应。在大多数情况下这个类应该是被共享的云计算,因为每个 Client 对象持有自己的连接池和线程池 。重复创建则会造成在空闲池上的资源浪费。Client对象可以通过默认的无参构造方法创建也可以通过 Builder 创建自定义的 Client 对象。Client 持有的线程池和连接池资源在空闲时可以自动释放无需客户端代码手动释放 ,在特殊情况下也支持手动释放 。Request :一个 Request 对象代表了一个 Http 请求 。它包含了请求地址 url ,请求方法类型 method ,请求头 headers ,请求体 body 等属性 ,亿华云该对象具有的属性普遍使用了 final 关键字来修饰,正如该类的说明文档中所述 ,当这个类的 body 为空或者 body 本身是不可变对象时 ,这个类是一个不可变对象 。Response