OkHttp3源码详解(三) 拦截器
1.构造Demo
首先构造一个简单的异步网络访问Demo:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build(); client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("OkHttp", "Call Failed:" + e.getMessage());
} @Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("OkHttp", "Call succeeded:" + response.message());
}
});
2. 发起请求
OkHttpClient.newCall
实际是创建一个RealCall
实例:
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
RealCall.enqueue
实际就是讲一个RealCall
放入到任务队列中,等待合适的机会执行:
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
从代码中可以看到最终RealCall
被转化成一个AsyncCall
并被放入到任务队列中,任务队列中的分发逻辑这里先不说,相关实现会放在OkHttp源码分析——任务队列疑问进行介绍。这里只需要知道AsyncCall的excute方法最终将会被执行:
[RealCall.java]
@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);
}
}
}
execute
方法的逻辑并不复杂,简单的说就是:
- 调用
getResponseWithInterceptorChain
获取服务器返回 - 通知任务分发器(
client.dispatcher
)该任务已结束
getResponseWithInterceptorChain
构建了一个拦截器链,通过依次执行该拦截器链中的每一个拦截器最终得到服务器返回。
3. 构建拦截器链
首先来看下getResponseWithInterceptorChain
的实现:
源码路径:okhttp3/RealCall.java
// 开始执行整个请求
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
// 拦截器栈
List<Interceptor> interceptors = new ArrayList<>();
// 前文说过的 普通拦截器
interceptors.addAll(client.interceptors());
// 重试拦截器,网络错误、请求失败等
interceptors.add(retryAndFollowUpInterceptor);
// 桥接拦截器,主要是重构请求头即header
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 缓存拦截器
interceptors.add(newCacheInterceptor(client.internalCache()));
// 连接拦截器,连接服务器,https包装
interceptors.add(new ConnectInterceptor(client));
// 网络拦截器,websockt不支持,同样是自定义
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
// 服务拦截器,主要是发送(write、input)、读取(read、output)数据
interceptors.add(new CallServerInterceptor(forWebSocket)); // 开启调用链
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, , originalRequest);
return chain.proceed(originalRequest);
}
其逻辑大致分为两部分:
- 创建一系列拦截器,并将其放入一个拦截器数组中。这部分拦截器即包括用户自定义的拦截器也包括框架内部拦截器
- 创建一个拦截器链
RealInterceptorChain
,并执行拦截器链的proceed
方法
接下来看下RealInterceptorChain
的实现逻辑:
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final RealConnection connection;
private final int index;
private final Request request;
private int calls; public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
} @Override public Connection connection() {
return connection;
} public StreamAllocation streamAllocation() {
return streamAllocation;
} public HttpCodec httpStream() {
return httpCodec;
} @Override public Request request() {
return request;
} @Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
} public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException { ......
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + , request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next); ...... return response;
}
}
在proceed
方法中的核心代码可以看到,proceed实际上也做了两件事:
- 创建下一个拦截链。传入
index + 1
使得下一个拦截器链只能从下一个拦截器开始访问 - 执行索引为
index
的intercept方法,并将下一个拦截器链传入该方法
https://www.jianshu.com/p/db699081bc38
OkHttp3源码详解(三) 拦截器的更多相关文章
- OkHttp3源码详解(三) 拦截器-RetryAndFollowUpInterceptor
最大恢复追逐次数: ; 处理的业务: 实例化StreamAllocation,初始化一个Socket连接对象,获取到输入/输出流()基于Okio 开启循环,执行下一个调用链(拦截器),等待返回结果(R ...
- OkHttp3源码详解(一) Request类
每一次网络请求都是一个Request,Request是对url,method,header,body的封装,也是对Http协议中请求行,请求头,实体内容的封装 public final class R ...
- OkHttp3源码详解(五) okhttp连接池复用机制
1.概述 提高网络性能优化,很重要的一点就是降低延迟和提升响应速度. 通常我们在浏览器中发起请求的时候header部分往往是这样的 keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可 ...
- OkHttp3源码详解(六) Okhttp任务队列工作原理
1 概述 1.1 引言 android完成非阻塞式的异步请求的时候都是通过启动子线程的方式来解决,子线程执行完任务的之后通过handler的方式来和主线程来完成通信.无限制的创建线程,会给系统带来大量 ...
- OkHttp3源码详解(二) 整体流程
1.简单使用 同步: @Override public Response execute() throws IOException { synchronized (this) { if (execut ...
- 详解Mybatis拦截器(从使用到源码)
详解Mybatis拦截器(从使用到源码) MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. 本文从配置到源码进行分析. 一.拦截器介绍 MyBatis 允许你在 ...
- spring事务详解(三)源码详解
系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...
- Activiti架构分析及源码详解
目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...
- 源码详解系列(七) ------ 全面讲解logback的使用和源码
什么是logback logback 用于日志记录,可以将日志输出到控制台.文件.数据库和邮件等,相比其它所有的日志系统,logback 更快并且更小,包含了许多独特并且有用的特性. logback ...
随机推荐
- 【实战】Apache Shiro 1.2.4 RCE
poc: #coding: utf-8 import os import re import sys import base64 import uuid import subprocess impor ...
- (转)Asp.Net Mvc视图引擎Razor介绍
Asp.Net Mvc视图引擎Razor介绍 1.Razor介绍 程序园原创,转载请注明:http://www.kwstu.com/ArticleView/dabaomvc_2014082408205 ...
- (转)Mysql数据库主从心得整理
Mysql数据库主从心得整理 原文:http://blog.sae.sina.com.cn/archives/4666 管理mysql主从有2年多了,管理过200多组mysql主从,几乎涉及到各个版本 ...
- 【WinSCP】WinSCP 5.x使用密钥连接服务器
在WinSCP 4.x中,主页面有一个添加密钥文件的选项,如下图所示 但是在WinSCP 5.x中主界面发生了很大的变化 在主页上没有了载入密钥文件的选项,那么我们应该怎么使用密钥验证呢? WinSC ...
- Sublime 必知必会(持续更新)
1.格式化代码 Edit - Line - Reindent(中文路径则是:编辑 - 行 - 再次缩进) 2.分屏显示 view-layout-Columns:2(中文路径则是:查看 - 布局 - 列 ...
- ES6-Async & 异步
依赖文件地址 :https://github.com/chanceLe/ES6-Basic-Syntax/tree/master/js <!DOCTYPE html> <html&g ...
- 认识HDFS分布式文件系统
1.设计基础目标 (1) 错误是常态,需要使用数据冗余 (2)流式数据访问.数据批量读而不是随机速写,不支持OLTP,hadoop擅长数据分析而不是事物处理. (3)文件采用一次性写多次读的模型, ...
- mysql 索引数据结构及原理
原文:http://www.uml.org.cn/sjjm/201107145.asp 1 索引的本质 MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构.提取句子 ...
- 那些年,我们一起误解过的REST
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由sammyshen 发表于云+社区专栏 最近几年REST API越来越流行,特别是随着微服务的概念被广泛接受和应用,很多Web Ser ...
- XML的基本概念和Android下的使用
1. XML的基本概念 1. 什么是XML: 1). XML是指可扩展标记语言(eXtensible Markup Language),它是一种标记语言,很类似HTML.它被设计的宗旨是表示数据,而非 ...