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源码详解(三) 拦截器的更多相关文章

  1. OkHttp3源码详解(三) 拦截器-RetryAndFollowUpInterceptor

    最大恢复追逐次数: ; 处理的业务: 实例化StreamAllocation,初始化一个Socket连接对象,获取到输入/输出流()基于Okio 开启循环,执行下一个调用链(拦截器),等待返回结果(R ...

  2. OkHttp3源码详解(一) Request类

    每一次网络请求都是一个Request,Request是对url,method,header,body的封装,也是对Http协议中请求行,请求头,实体内容的封装 public final class R ...

  3. OkHttp3源码详解(五) okhttp连接池复用机制

    1.概述 提高网络性能优化,很重要的一点就是降低延迟和提升响应速度. 通常我们在浏览器中发起请求的时候header部分往往是这样的 keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可 ...

  4. OkHttp3源码详解(六) Okhttp任务队列工作原理

    1 概述 1.1 引言 android完成非阻塞式的异步请求的时候都是通过启动子线程的方式来解决,子线程执行完任务的之后通过handler的方式来和主线程来完成通信.无限制的创建线程,会给系统带来大量 ...

  5. OkHttp3源码详解(二) 整体流程

    1.简单使用 同步: @Override public Response execute() throws IOException { synchronized (this) { if (execut ...

  6. 详解Mybatis拦截器(从使用到源码)

    详解Mybatis拦截器(从使用到源码) MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. 本文从配置到源码进行分析. 一.拦截器介绍 MyBatis 允许你在 ...

  7. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  8. Activiti架构分析及源码详解

    目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...

  9. 源码详解系列(七) ------ 全面讲解logback的使用和源码

    什么是logback logback 用于日志记录,可以将日志输出到控制台.文件.数据库和邮件等,相比其它所有的日志系统,logback 更快并且更小,包含了许多独特并且有用的特性. logback ...

随机推荐

  1. PHP常见缓存机制

    PHP常用的几种缓存机制_PHP教程 普遍缓存技术 数据缓存:这里所说的数据缓存是指数据库查询PHP缓存机制,每次访问页面的时候,都会先检测相应的缓存数据是否存在,如果不存在,就连接数据库,得到数据, ...

  2. 网站安全系列:跨站脚本攻击XSS

    本篇博文主要从概念和应用上介绍XSS,主要内容来源于<白帽子讲web安全> XSS核心本质 XSS实际上是一种HTML注入,用户输入的数据被当成HTML的一部分来执行.防御方法核心是输入检 ...

  3. ASP.NET MVC利用ActionLink实现动态组合查询

    一个文件传输数据表,内有日期.传输协议.传输方向(上传或下载).文件名等信息,完整的表内容显示如下: 现在需要分类查询,即按照协议和传输方向查询.单独的分类查询问题并不大,比如,按协议查询,在View ...

  4. (转)nginx+iis实现负载均衡

    最近在研究分布式系统架构方面的知识,包括负载均衡,数据库读写分离,分布式缓存redis等.本篇先从负载均衡服务架构入手,关于负载均衡百度百科的定义如下:负载均衡,英文名称为Load Balance,其 ...

  5. 【Kafka源码】日志处理

    目前来说,kafka的日志中记录的内容比较多,具体的存储内容见这篇博客,写的比较好.可以看到,存储的内容还是比较多的,当存储文件比较大的时候,我们应该如何处理这些日志?下面我们通过kafka启动过程的 ...

  6. SSM的XML和WEB.XML的配置

    显示层(handler/controller): request请求到springmvc的前端控制器,从处理器映射器找相应的handler(用@RequestMapping("  " ...

  7. PHP将字符串写入txt文件

    function writelog($str) { $open=fopen("e:\log.txt","a" ); fwrite($open,$str); fc ...

  8. 图像RGB2YUV与YUV2RGB格式互转介绍

    1 YUV格式与RGB格式说明 由于不同国家的电视信号系统支持的图像格式不同,有YUV格式成像,也有RGB格式成像,因此为了保证兼容性,需要进行RGB与YUV格式的互转. 另外YUV格式具有亮度信息和 ...

  9. windows 10安装gensim、nltk

    一.安装gensim 1.什么事gensim gensim是一个python的科学库,gensim包含了TF-IDF.随机投影.word2vec和document2vec算法的实现,分层Dirchle ...

  10. [译]用R语言做挖掘数据《二》

    数据探索 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou,密码shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到程序: ...