基本使用:

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.html

http://www.jianshu.com/p/1873287eed87

http://blog.csdn.net/itachi85/article/details/51190687

一个最简单的DEMO

public class OkHttp3BasicActivity extends Activity {@BindView(R.id.sn_tv)TextView tv;private OkHttpClient client ;String str = "";private Handler handler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){case 1:tv.setText(msg.obj.toString());break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.simple_network_main);ButterKnife.bind(this);new Thread(new Runnable() {@Overridepublic void run() {okrun();}}).start();}private void okrun(){client = new OkHttpClient();httpUrl = httpUrl+"?"+httpArg;Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);str = response.body().string();Message msg = Message.obtain();msg.what =1;msg.obj = str;handler.sendMessage(msg);}});}
}

两个需要注意的点:

  1. okhttp3不能在ui线程中运行
  2. onresponse在子线程中,需要用handler回调才能操作ui

请求网络原理解析:

http://liuwangshu.cn/application/network/7-okhttp3-sourcecode.html

HTTP请求执行流程分析

http://www.jianshu.com/p/230e2e2988e0

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

整个实现流程如下:

  • 创建OkHttpClient对象。OkHttpClient为网络请求执行的一个中心,它会管理连接池,缓存,SocketFactory,代理,各种超时时间,DNS,请求执行结果的分发等许多内容。
  • 创建Request对象。Request用于描述一个HTTP请求,比如请求的方法是"GET"还是"POST",请求的URL,请求的header,请求的body,请求的缓存策略等。
  • 利用前面创建的OkHttpClient对象和Request对象创建Call对象。Call是一次HTTP请求的Task,它会执行网络请求以获得响应。OkHttp中的网络请求执行Call既可以同步进行,也可以异步进行。调用call.execute()将直接执行网络请求,阻塞直到获得响应。而调用call.enqueue()传入回调,则会将Call放入一个异步执行队列,由ExecutorService在后台执行。
Call对象
如果使用enqueue方法,则调用dispatch.enqueue(),发送到线程池
如果使用execute,则不需要dispatch发送到线程池处理,直接同步处理。
线程池 executorService()

如果正在运行的异步任务队列数量小于最大请求数,线程池调用execute()执行该任务,否则加入准备队列

默认情况下,这是一个不限容量的线程池。但Dispatcher会限制每个host同时执行的最大请求数量,默认为5,同时也会限制同时执行的总的最大请求数量

用户可以通过Dispatcher的构造函数来定制ExecutorService,这需要通过OkHttpClient.Builder在OkHttpClient的构建过程中间接的做到。

线程池execute()

由getResponseWithInterceptorChain()来执行网络请求,得到response

Response response = getResponseWithInterceptorChain();

成功后回调CallBack的onResponse方法

responseCallback.onResponse(RealCall.this, response);

可以看到这里对回调接口是同步调用,也就是回调方法将在后台线程中被调用。

getResponseWithInterceptorChain()加上了一系列的interceptor,然后执行chain.proceed(request)

private Response getResponseWithInterceptorChain() throws IOException {  //构建全栈拦截器  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);//获取Response  }  

由此可见OkHttp中,Http请求的实际处理流程将大致如下图这样:

RetryAndFollowUpInterceptor

重试与重定向拦截器,用来实现重试和重定向功能,内部通过while(true)死循环来进行重试获取Response(有重试上限,超过会抛出异常)。followUpRequest主要用来根据响应码来判断属于哪种行为触发的重试和重定向(比如未授权,超时,重定向等),然后构建响应的Request进行下一次请求。当然,如果没有触发重新请求就会直接返回Response。

RetryAndFollowUpInterceptorintercept()中首先从client取得connection pool,用所请求的URL创建Address对象,并以此创建StreamAllocation对象。

RetryAndFollowUpInterceptor对重定向的响应也不会无休止的处理下去,它处理的最多的重定向级数为20次,超过20次时,它会抛异常出来。

总结一下RetryAndFollowUpInterceptor做的事情:

  1. 创建StreamAllocation对象,为后面流程的执行准备条件。
  2. 处理重定向的HTTP响应。
  3. 错误恢复。

BridgeInterceptor

桥接拦截器,用于完善请求头

这个Interceptor做的事情比较简单。可以分为发送请求和收到响应两个阶段来看。在发送请求阶段,BridgeInterceptor补全一些http header,这主要包括Content-TypeContent-LengthTransfer-EncodingHostConnectionAccept-EncodingUser-Agent,还加载Cookie,随后创建新的Request,并交给后续的Interceptor处理,以获取响应。

而在从后续的Interceptor获取响应之后,会首先保存Cookie。如果服务器返回的响应的content是以gzip压缩过的,则会先进行解压缩,移除响应中的header Content-EncodingContent-Length,构造新的响应并返回;否则直接返回响应。

CacheInterceptor

缓存拦截器,首先根据Request中获取缓存的Response,然后根据用于设置的缓存策略来进一步判断缓存的Response是否可用以及是否发送网络请求(CacheControl.FORCE_CACHE因为不会发送网络请求,所以networkRequest一定为空)。如果从网络中读取,此时再次根据缓存策略来决定是否缓存响应。

对于CacheInterceptor.intercept(Chain chain)的分析同样可以分为两个阶段,即请求发送阶段和响应获取之后的阶段。这两个阶段由chain.proceed(networkRequest)来分割。

在请求发送阶段,主要是尝试从cache中获取响应,获取成功的话,且响应可用未过期,则响应会被直接返回;否则通过后续的Interceptor来从网络获取,获取到响应之后,若需要缓存的,则缓存起来。

关于HTTP具体的缓存策略这里暂时不再详述。

RealCall.getResponseWithInterceptorChain()可见CacheInterceptor的cache同样来自于OkHttpClient。OkHttp已经有实现Cache的整套策略,在Cache类,但默认情况下不会被用起来,需要自己在创建OkHttpClient时,手动创建并传给OkHttpClient.Builder。


ConnectInterceptor

 连接拦截器,用于打开一个连接到远程服务器。说白了就是通过StreamAllocation获取HttpStream和RealConnection对象,以便后续读写。

 

CallServerInterceptor

调用服务拦截器,拦截链中的最后一个拦截器,通过网络与调用服务器。通过HttpStream依次次进行写请求头、请求头(可选)、读响应头、读响应体。

CallServerInterceptor首先将http请求头部发给服务器,如果http请求有body的话,会再将body发送给服务器,继而通过httpStream.finishRequest()结束http请求的发送。

随后便是从连接中读取服务器返回的http响应,并构造Response。

如果请求的header或服务器响应的header中,Connection值为closeCallServerInterceptor还会关闭连接。

总结一下这几个Interceptor的职责:
RetryAndFollowUpInterceptor --->创建StreamAllocation对象,处理http的redirect,出错重试。对后续Interceptor的执行的影响:修改request及StreamAllocation。
BridgeInterceptor-------------->补全缺失的一些http header。对后续Interceptor的执行的影响:修改request。
CacheInterceptor-------------->处理http缓存。对后续Interceptor的执行的影响:若缓存中有所需请求的响应,则后续Interceptor不再执行。
ConnectInterceptor------------>借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响:创建了httpStream和connection。
CallServerInterceptor----------->处理IO,与服务器进行数据交换。对后续Interceptor的执行的影响:为Interceptor链中的最后一个Interceptor,没有后续Interceptor。

在RealInterceptorChain.proceed()中,除了对状态及获取的reponse做检查之外,最主要的事情即是构造新的RealInterceptorChain对象,获取对应Interceptor,并调用Interceptor的intercept(next)了。在这里,index充当迭代器或指示器的角色,用于指出当前正在处理的Interceptor。

public final class RealInterceptorChain implements Interceptor.Chain {private final List<Interceptor> interceptors;private final StreamAllocation streamAllocation;private final HttpStream httpStream;private final Connection connection;private final int index;private final Request request;private int calls;public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,HttpStream httpStream, Connection connection, int index, Request request) {this.interceptors = interceptors;this.connection = connection;this.streamAllocation = streamAllocation;this.httpStream = httpStream;this.index = index;this.request = request;}

RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpStream, connection, index + 1, request);Interceptor interceptor = interceptors.get(index);Response response = interceptor.intercept(next);

RealInterceptorChain + Interceptor实现了装饰器模式,实现了请求/响应的串式或流式处理。只不过内层装饰器不是外层装饰器的成员变量,而是接口方法中创建的临时变量。

@Override public Response intercept(Chain chain) throws IOException {  RealInterceptorChain realChain = (RealInterceptorChain) chain;  Request request = realChain.request();  StreamAllocation streamAllocation = realChain.streamAllocation();  // We need the network to satisfy this request. Possibly for validating a conditional GET.  boolean doExtensiveHealthChecks = !request.method().equals("GET");  HttpStream httpStream = streamAllocation.newStream(client, doExtensiveHealthChecks);  RealConnection connection = streamAllocation.connection();  return realChain.proceed(request, streamAllocation, httpStream, connection);  }

在Http1xStram中,它利用Okio对Socket的读写操作进行封装,而创建HttpStream 对象的过程涉及到 StreamAllocationRealConnection,代码较长,这里就不展开,这个过程概括来说,就是找到一个可用的RealConnection,再利用RealConnection 的输入输出(BufferedSourceBufferedSink)创建HttpStream 对象,供后续步骤使用。

里我们可以看到,核心工作都由HttpStream对象完成,而HttpStream实际上利用的是 Okio,而 Okio 实际上还是用的Socket,所以没什么神秘的,只不过一层套一层,层数有点多。

其实 Interceptor 的设计也是一种分层的思想,每个Interceptor 就是一层。为什么要套这么多层呢?分层的思想在 TCP/IP 协议中就体现得淋漓尽致,分层简化了每一层的逻辑,每层只需要关注自己的责任(单一原则思想也在此体现),而各层之间通过约定的接口/协议进行合作(面向接口编程思想),共同完成复杂的任务。

个人理解:

OkHttpClient.newCall(request)进行execute或者enqueue操作

Request保存了url,method,body,head等信息

1.调用dispatcher的enqueue方法

dispatcher维护了一个类似于CachedThreadPool的线程池,比较适合执行大量的耗时比较少的任务。线程池正在运行的请求主机数小于5时则把请求加载到runningAsyncCalls中并在线程池中执行。

2.进入拦截器链

重试和重定向:处理重定向事件

bridge:完善请求头,加载cookie

Cache:读取缓存(ETAG,LastModified),判断缓存是否过期,如果未过期则直接返回,不再进入后续拦截器。如果开启网络连接则判断是否要缓存响应

Connection:建立连接

其中,Cache这里的缓存都是基于Map,key是请求中url的md5,value是在文件中查询到的缓存,页面置换基于LRU算法

建立连接后,核心工作都由HttpStream对象完成,而HttpStream实际上利用的是 Okio,而 Okio 实际上还是用的Socket

转载于:https://www.cnblogs.com/qlky/p/7246331.html

Okhttp3 使用和原理(DEMO)相关推荐

  1. android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)...

    一,原生的DownloadManager 从Android 2.3(API level 9)开始,Android以Service的方式提供了全局的DownloadManager来系统级地优化处理长时间 ...

  2. 通过ConnectInterceptor源码掌握OKHttp3网络连接原理 呕心沥血第十弹【十】

    ConnectInterceptor 系列 前言 连接拦截器 Http协议发展 OKHttp创新 源码分析 ConnectionPool StreamAllocation RealConnection ...

  3. PHP实现个人免签约微信支付接口原理+源码

    什么是个人免签支付 个人免签支付就是给个人用的支付接口,一般的支付接口都需要营业执照才能申请,个人很难申请的到,或者是没有资质去申请,要和支付商进行签约的.免签,顾名思义就是不需要签约.那么个人免签支 ...

  4. Jquery 图片轮播实现原理总结

    以前要做图片轮播效果的时候,总是在网上找一段jquery的复制粘贴进去,只索取不奉献,今个就把我对这个的实现原理讲解一下. 首先说下,我在网上找的例子全是用的UL 实现,其实大可不必,只要是能包含im ...

  5. 容器网络Calico进阶实践 | 褚向阳

    各位晚上好,我是数人云的褚向阳,接下来要跟大家分享的主题是<容器网络Calico进阶实践>. 距离上次聊 Calico 已经过去快半年的时间了,数人云也一直在努力将容器网络方案应用到企业客 ...

  6. Consul + fabio 实现自动服务发现、负载均衡

    目录 Consul Fabio 服务发现的特点 工作原理 Demo 结合kubernetes扩容 Consul hashicorp团队开发 就是大名鼎鼎开发 vagrant 的团队. Consul 是 ...

  7. JAVA——Okhttp封装工具类

    基本概念 OKhttp:一个处理网络请求的开源项目,是安卓端最火热的轻量级框架. Maven <!--OK HTTP Client--><dependency><grou ...

  8. 树莓派超声波模块测距

    参考:树莓派超声波模块测距及C语言demo 作者:一只青木呀 发布时间: 2020-07-22 16:54:16 网址:https://blog.csdn.net/weixin_45309916/ar ...

  9. dll 源码_【技术分享】 | 一个JAVA内存马的源码分析

    前言 偶然接触到了这样一个JAVA内存马,其作者也是冰蝎的作者,项目地址: https://github.com/rebeyond/memShell 正好最近在接触JAVA,借此机会学习下大佬的代码, ...

  10. “约见”面试官系列之常见面试题第十一篇之canvas(建议收藏)

    目录 前言 介绍 小试牛刀 应用案例 动画 API介绍 Demo 游戏 三要素 Demo 截图 API介绍 canvas.style.width 和 canvas.width 的区别 Demo 合成图 ...

最新文章

  1. java 开发环境配置_Java 开发环境配置
  2. Navicat for MySQL连接MYSQL出错,错误代码1045的解决方法
  3. Algorithm:C++语言实现之SimHash和倒排索引算法相关(抽屉原理、倒排索、建立查找树、处理Hash冲突、Hash查找)
  4. 解读:一种来自Facebook团队的大规模时间序列预测算法(附github链接)
  5. python实现多线程的三种方法threading.Thread(模块)的继承实现和函数实现;以及concurrent.futures模块的线程池实现
  6. Svchost.exe进程详解及Svchost.exe病毒清除方法
  7. Vue.js(学习Vue3之前必须要掌握的知识)
  8. vue可以配合jade以及sass吗_在vue中如何使用Jade模板 - echart
  9. JSP EL表达式中11个隐藏对象file:///D:/SoftwareFile/qq file/MobileFile/34090016310074682(1).png
  10. Resource Hacker-资源替换工具
  11. 本科计算机的逻辑学,逻辑学本科专业介绍
  12. 基于嵌入式Linux的语音识别系统硬软件设计
  13. cocos creator 全局变量的几种方法
  14. python线程锁和线程池
  15. 传音控股以科技立品牌,2021一季度净利大增125.93%
  16. matlab 重复博弈,横向稳定杆的侧倾角刚度仿真与试验研究
  17. 如何在Github上配置ssh key的密钥
  18. GD32F103C读写flash
  19. BJDCTF 2nd-WP
  20. esp8266 OLED SSD1306程序集合

热门文章

  1. 解决 xshell 上面 zsh 的 home end 无效问题
  2. python第三方模块
  3. ajax跨域访问问题
  4. 系统中了莫名奇怪的毒,杀毒软件不起作用了,怎么办
  5. C#中跨工程跨项目注释的显示
  6. C# 用装饰模式实现蝌蚪变青蛙的过程-续
  7. 【Webcam设计】 ffmpeg与x264差异
  8. java-信息安全(十三)-数字签名,代码签名【Java证书体系实现】
  9. Qt 本地化(翻译)
  10. kotlin使用spring data jpa(三)