okhttp3+拦截器+线程池连接池总结

1:OKhttp3简介:

Okhttp3是一个支持Http和Http2的java或者Android网络请求SDK.依赖于okio.okio相对于java的IO流更高效的.

2:核心:

(1)采用责任链方式的拦截器,实现分成处理网络请求,让用户对网络请求实现了更好的拓展

(2)采用GZIP处理下载数据,压缩了数据的大小.

(3)支持http缓存

(4)采用线程池(thread pool)和连接池(Socket pool)解决多并发问题,同时连接池支持多路复用(http2才支持,可以让一个Socket同时发送多个网络请求,内部自动维持顺序.相比http只能一个一个发送,更能减少创建开销))

(5)底层采用socket和服务器进行连接.

(6)采用okio实现高效的io流读写

3:拦截器

(1)作用

拦截器Interceptor是一种强大的机制,可以监视,重写和重试网络请求.主要作用就是:在把请求发送出去之前,可以对reqeust 进行重写,在应用拿到response之前,先获得response,对其中某些数据进行监控,在有必要的情况下,对response的某些内容(比如response的header,body,response内的request的header,body)进行更改。

(2)分类:

总体上来书okhttp3 可以分为2大类:

 1. 系统拦截器(核心拦截器)

1:RetryAndFollowUpInterceptor 重定向拦截器:

Okhttp内置的第一个拦截器,通过while(true)的死循环来进行对异常结果或响应结果判断是否要进行重新请求.负责失败重连和重定向的拦截器.

2:BridgeInterceptor  桥拦截器:

它可以对网络文件的类型,网页的编码和返回的数据进行解压处理.是为用户构建的一个Request请求转化为能够进行网络访问的请求,同时将网络请求回来的响应Response转化为用户可用的Response.

3:CacheIntercepor 缓存拦截器:

可以根据OkhttpClient对象的配置以及缓存策略对请求值进行缓存.

4:ConnectIntercepor  :

在Okhttp底层是通过socket的方式于服务端进行连接的,并且在连接建立之后会通过OKIO获取通向server端的输入流Source和输出流Sink.

5:CallServerInterceptor :

CallServerInterceptor 在 ConnectInterceptor 拦截器的功能就是负责与服务器建立 Socket 连接,并且创建了一个 HttpStream 它包括通向服务器的输入流和输出流。而接下来的 CallServerInterceptor 拦截器的功能使用 HttpStream 与服务器进行数据的读写操作的。

2. 自定义拦截器:

1. 应用拦截器 Application Interceptor

2. 网络拦截器 NetWork Interceptor

相同点:

(1)都能对server返回的response进行拦截

(2)都基于Interceptor接口,由开发者实现这个接口,然后将自定义的Interceptor类的对象设置到okhttpclient对象中.

(3)两者都会被add到okhttpclient内的一个ArrayList中.当请求执行的时候,多个拦截器依次执行.

不同点:

(1)okhttpclient添加两种拦截器的api不同,应用拦截器的api是addInterceptor(),而添加网络拦截器的接口是addNetWorkInterceptor();

(2)两者负责的区域不同,应用拦截器作用于okhttpCore到Application之间,网络拦截器作用于newwork和okhttpCore之间.

(3)在某些情况下网络拦截器有可能被执行多次,但application只会被执行一次.

3:ok请求网络数据要走的步骤:

1:首先,在ok执行一个异步请求,new OkhttpClient()返回了一个Okhttpclient对象, 然后new Request.Builder()返回一个request对象,okhttpclient调用newCall()方法,传入request对象,然后返回一个call对象,这个call对象是call的子类RealCall调用newRealCall()方法创建出来的,这个call就是真正发起网络请求的对象.

2:RealCall(call)调用enqueue()方法,传入一个CallBack回调.RealCall对象重写了父类Call的enqueue方法

3:创建RealCall对象时通过构造传入了一个okhttpclient对象,在RealCall的enqueue()方法里,client调用了dispatcher()返回一个Dispatch分发器对象,这个类中创建了一个线程池和几个队列,以及一些参数,在这里我介绍一下:

(1):readyAsyncCalls:待执行异步任务队列

(2).runningAsyncCalls:运行中异步任务队列

(3).runningSyncCalls:运行中同步任务队列

 (4).executorService:任务队列线程池

(5) int maxRequests =64 

最大请求并发数为64

(6)int maxRequestsPerHost =5  

'baseUrl一样的请求(域名相同),okhttp最多支持5个.

注意,此线程池的核心线程数为0,最大线程数为Integer.MAX_VALUE,线程空闲时间只能活60秒,然后用了SynchronizeQueue队列,这是一个不存储元素的阻塞队列,也就是说有任务到达的时候,只要没有空闲线程,就会创建一个新的线程来执行任务.

有了Dispatch分发器之后,通过Disapther分发器,把这次请求封装成一个AsyncCall对象,然后入队.AsyncCall里面包含了这次真正的请求RealCall,和回调CallBack.

4 : Dispatch分发器调用enqueue(),传入AsyncCall对象,然后通过readyAsyncCall.add(call)将AysncCall对象添加到等待队列,如果本次请求不是websocket,那么去正在执行的队列和等待队列中找之前的请求中是否存在host 一样的异步请求(注意是异步请求,同步请求不包括),先找正在执行的,找到了直接返回,正在执行的队列里面找不到再去等待队列中找,找到了直接返回。如果两个队列都找不到返回null,返回null 说明这个host的 请求时第一次发送。如果找到了,那么把之前请求的计数器 callsPerHost 的引用赋值个当期请求,这样才能保证所有host 一样的请求都用同一个计数器。

只有其中某一个进入执行队列开始执行就加一 asyncCall.callsPerHost().incrementAndGet(),

一旦执行完成就减一 call.callsPerHost().decrementAndGet(),该方法在Dispatcher的 finish 方法被调用,然后调用promoteAndExecute()方法.

5:在promoteAndExecute()方法中,第一步创建一个 executableCalls 空的Arraylist,用于后面遍历等待队列中任务是,把满足条件的任务加入到数组中,然后统一交给线程池去执行。

 1. 循环遍历等待队列中的任务,如果当前正在执行的任务数量大于maxRequests 64,那么直接跳循环,executableCalls 元素为空,所以不做任何操作,相当于这次请求被加入到等待队列,到此结束。知道有任务完成后在Dispatcher.finish 方法中会再次调用promoteAndExecute方法。达到轮训效果。

 2. 如果当前正在执行的任务数量不大于64,那么继续第二个判断,判断目前正在执行的任务中,和当前请求的host 相同的任务数量是否超过5个,超过5个就跳过该循环,继续下个循环。如果不超过就从等待对队列移除,并且让计数器加一,同时加入到executableCalls 中

3. 如果executableCalls 不为空,循环遍历,通过asyncCall.executeOn(executableCalls )把任务交给线程池去执行。

6:在executeOn()方法中,线程池调用executorService.execute(this);去执行当前任务,它会让线程去调用AsyncCall的execute()方法,然后通过链式调用拦截器,开始真正进行网络请求.Response response = getResponseWithInterceptorChain();

7:getResponseWithInterceptorChain()方法里,new了一个拦截器的集合,依次存入五个拦截器.然后通过if (!forWebSocket)判断是否是网络连接,如果是就在集合里存入networkInterceptors拦截器,如果是,就存入new CallServerInterceptor(forWebSocket)拦截器,然后new 一个拦截器链,传入拦截器集合,返回一个Interceptor.chain对象,然后调用proceed()方法,真正的将请求request转化为response,返回给getResponseWithInterceptorChain()方法

8:获得response对象之后,将结果通过responseCallBack传过去,不管是否成功,Dispatcher对象调用finished方法,对之前的计数器计数减一,这样的话,之前因为相同host 最大并发不能超过5而等待的异步任务就没有机会执行了。

9:finished 方法中又调用了重载的 finished(runningAsyncCalls, call),该方法中调用了上面5步中的promoteAndExecute(),继续去等待队列里面取等待执行的任务然后交给线程池去执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容