手把手讲解-OkHttp硬核知识点(1),一文全懂

3、完整的一个请求执行出去,流程如下图:
image.png


OkHttp源码核心类之一:分发器详解

上述,提到Call类,可以选择性执行 同步或者异步请求,但是无论同步异步,都一定会经过一个门户:“分发器” :
索引进源码(okhttp v3.10.0):

异步请求.png
同步请求.png

虽然用户不需要直接操作分发器,但是 分发器,作为OkHttp架构的一个门户层,是所有请求的必经之路,其中的代码还是有必要了解细节的。

同步请求

进入分发器 Dispatcher之后, 会执行 getResponseWithInterceptorChain() 来执行这个Call任务,得到一个Response,其中的细节分为两步:

1、client.dispatcher().executed(this); ,进入源码可以看到 仅仅是执行了 runningSyncCalls.add(call);,将call对象加入到了一个双端队列 Deque<RealCall> runningSyncCalls 中。
2、 getResponseWithInterceptorChain() 是执行网络请求的核心内容,涉及到拦截器,在这一节上暂时不详述。

同步请求的执行步骤十分简单,将任务加入到 runningSyncCalls列表,并且直接执行核心方法,同步阻塞拿到response。

异步请求

异步请求进入分发器之后,
image.png

可能会被加入到 Deque<AsyncCall> runningAsyncCalls 这么一个双端队列中,然后 executorService().execute(call);实际上是用了线程池来执行了这个异步任务。
但是,请注意(还是刚才的enqueue方法代码)这里有一个判断条件 if分支 :image.png
这个条件是否满足,将会直接决定是直接执行这个任务,还是将任务加入到 readyAsyncCalls 双端队列.

那么设置这个条件的目的是什么呢?从变量命名来看:
runningAsyncCalls 执行中的异步任务
runningCallsForHost 同一个域名正在执行的任务数
readyAsyncCalls 预备执行的任务队列(尚未执行)

当正在执行的任务数小于最大值(默认为64)并且,同一个域名正在请求的任务数小于最大值(默认5)时,才会立即执行,否则,这个任务会被加入到 readyAsyncCalls中等待安排。

那么问题来了,readyAsyncCalls中的任务什么时候会被执行?
追踪代码:追踪 readyAsyncCalls 的使用代码,找到遍历这个队列的地方:

image.png
继续追踪,找到了这个 finish方法:
image.png

继续追踪finish在哪里调用的,找到两处:

image.png
image.png

所以,得出结论:
在一个任务(无论同步还是异步)结束之后,分发器中的异步任务,存在两个队列,一个running队列,一个ready队列,当 running队列的size小于最大值,并且同一个域名正在执行的任务数小于最大值时,可以直接加入到running队列,立即执行。 如果不满足这条件,这个异步任务就会被加入到 ready队列.

在任意一个任务(无论同步或是异步任务)执行完毕(无论成败)之后,就会遍历ready队列,每次从ready队列中取出一个任务,判断同时执行的异步任务数是否达到上限,并且同一主机的访问数是否达到上限,如果都满足,就加入到running队列,并且立即执行,不满足,就停止遍历。周而复始,直到所有的异步任务都执行完。

文字不够形象,画个图表示。

未命名文件.jpg

关于okhttp的分发器Dispatcher用到的线程池

同步请求,没有用到线程池。

image.png

但是异步请求的代码中,有这么一句。

image.png

我们知道,为什么这里会用到线程池呢?

1.观察 同步或者异步的call的实例。
image.png
那么这个Call是什么?它是一个接口,它的唯一实现类是RealCall,image.png
RealCall中,异步请求的执行方法,enqueue() 其实是交给了 分发器一个AsyncCall对象,它继承自NamedRunnable 可命名的Runnable任务。所以,这里可以用 线程池ExecutorService来执行这个Runnable.

进一步观察这个线程池的细节:

image.png
它是一个核心线程数为0的线程池,并且使用了一个无容量的阻塞队列作为参数。
其实也不不必自己去创建线程池,而可以直接使用Executors.newCachedThreadPool(); 来创建,效果一样。
线程池,系统提供了有多种默认实现
image.png

为什么okhttp偏偏选择了这一种?

答:为了实现最大并发量。

详解如下:
既然这里提到了线程池,那么就把线程池的基本机制整理一下:

线程池的工作流程图.jpg
线程池的构造函数中,有一个阻塞队列参数。
image.png
它有3个实现类:ArrayBlockingDeque/LinkedBlockingDeque / SynchronousQueue 是我们线程池经常用的。
前面2个都是有容量的,而第三个是无容量的,加入进去,一定会失败。而参照上面线程池的工作流程图,如果加入失败,就会尝试去非核心线程执行任务。这样,便保证了每一个提交进来的异步任务,都会立即尝试去执行,而不是塞入等待队列中等待空闲线程,从而确保了 异步任务的并发。


OkHttp源码核心类之一:拦截器简述

上面讲解分发器的时候,提到了 RealCall类的getResponseWithInterceptorChain() 方法。它是一个网络请求执行的真正核心方法。
进入方法:

核心方法.png

  • 新建一个拦截器List,并且放入各种拦截器对象
  • 将拦截器list,交给RealInterceptorChain,进行责任链模式的调用,最终得出Response.

首先解释一下责任链模式,它是21种基本设计模式中,行为模式中一种。下面的案例可以很好地解释它:

责任链模式案例
当一个国企要采购一批设备的时候,按照上图整个任务流程中,存在5个对象,都能对采购流程造成影响,采购任务开始的时候,是从上到下依次对采购流程负责。而总经理,他才不关心下面的人怎么操作,他只关心最后的结果。
正如此案例中所述,okhttp的责任链模式,使用者也不需要关心这个请求到底经历了哪些过程,他只知道,我给了request,你就要给我response,而过程中,发生作用的各类拦截器,无需使用者知道,这样就达成了 面向对象程序开发中的最少知道原则

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

需要的朋友可以私信我【答案】或者点击这里免费领取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值