Android中OkHttp内部工作详解

234 阅读4分钟

1,OkHttp介绍

OkHttp是当前Android使用的流行的网络框架

支持Http/2并允许对同一个主机的所以请求采用同一个套接字

如果非Http/2,则通过连接池,减小请求延迟

默认请求GZip压缩数据

响应缓存,避免了重复请求

请求失败自动重试主机的其他Ip,自动重定向
QQ截图20230119104632.png

2,OkHttp的使用

QQ截图20230119111432.png

3,OkHttp的调用

同步请求

只是不去调用promoteCalls()方法

异步请求流程

Okhttp异步请求流程.png

OkHttp请求过程,最少只需要接触OkHttpClient,Request,Call,Response,但是框架内部帮助我们实现了很多逻辑,所有的逻辑大部分集中在拦截器中,但进入拦截器之前还需要调度器来调配请求任务。

分发器:内部维护队列和线程池,完成请求调配

拦截器:完成整个请求过程

QQ截图20230119112001.png

分发器

Dispatcher分发器中的enqueue方法中回去判断,请求会加入到正在执行的队列(running AsyncCall)还是准备执行队列(readyAsyncCall)

QQ截图20230119112648.png

判断条件:

// 正在请求的异步个数不大于设定的请求个数(默认是64)

runningAsyncCall.size() < maxRequests

//同一个域名的请求数不大于5个

runningCallForHost(call) maxRequestPerHost

正在执行的请求:RunningAyncCalls.add(call)

线程池跑任务:executorService.excute(call)

判断一个请求任务从readyAsyncCall转移到runningAsyncCall中

判断条件:

RealCall中execute方法

线程执行起来就会去跑RealCall中的excute()方法,

excute方法中回去执行:

// 执行请求

Response response = getResponseWithInterceptorChain()

在try{}catch{}finall{}中最终会执行client.dispathcher().finshed()

在finshed()方法中会将执行完的任务remove()掉,导致正在运行的任务减少,进而回去执行promoteCalls(),在会去判断同一域名的请求小于最大请求数,就会执行runningAsyncCalls.add (call)从readAsyncCall队列中添加一个到runningAsyncCall中去。

QQ截图20230119153925.png executorService线程池中ExcutorService.executorService()方法中,回去new一个线程池

executorService = new ThreadPoolExecutor(0, Inter.MAX_VALUE, 60, TimeUnit_SECONDs, new SynchronousQueue(), Unit.ThreadFactory("OKHttp Dispcher"), false)

线程池参数: 核心线程数, 最大线程数, 闲置时间, 任务队列, 线程创建工厂

核心线程数: 如设置x个核心线程数,一直维护X个核心线程

最大线程数: 同时执行最大的线程数量

闲置时间: 如设置60s,空闲了60s的核心线程数的线程会被回收

任务队列: 见下面详解

微信截图_20230310101317.png

创建线程池的队列是等待执行的队列

// 没有容量,只能去判断Inter的最大值, 无等待,最大并发

SynchronousQueue<Runnable> queue = new SynchronousQueue()

// 后发起的请求有可能先执行,容量设置没办法确认是多大,如果不传容量,默认是Inter的最大值,只是和ArrayBlockQueue的数据结构不同

LinkedBlockQueue<Runnable> queue = new LinkedBlockQueue()

// 后发起的请求有可能先执行,容量设置没办法确认是多大,如果不传容量,默认是Inter的最大值
ArrayBlockQueue<Runnable> queue = new ArrayBlockQueue()  

当一个任务通过execute(Runnable)方法添加到线程池

线程数量小于corePoolSize, 新建线程(核心)来处理被添加的任务

线程数量大于corePoolSize, 新任务被添加到等待队列, 添加失败

    线程数量小于maxmumPoolSize,新建线程来执行新任务

    数量等于maxmumPoolSize, 使用RejectedExcutionHandle拒绝策略(默认拒绝策略就是抛出一个异常)

4, OkHttp中拦截器

责任链模式: 对象行为模型,为请求创建一个接收者对象的链,在处理请求的时候执行对应的链,责任链上的处理者负责处理请求,客户只需要把请求发送到责任链即可,无需关心请求的处理细节和数据的传递,所以责任链将请求的发送者和处理者解耦

Response response = getResponseWithInterceptorChain()

getResponseWithInterceptorChain方法中

List<interceptor> interceptor = new ArrayList<>()

// 自定义的拦截器加入到集合中
interceptor.addAll(client.interceptors())

// 重定向拦截器

interceptor.add(retryAndFllowUpInterceptor)

// 桥连拦截器

interceptor.add(new BridgeInterceptor(client.cookieJar()))

// 开始执行缓存拦截器

interceptor.add(new CacheInterceptor(client.interceptorCache()))

// 连接拦截器

interceptor.add(new  ConnectInterceptor(client))

// 网络拦截器

interceptor.addAll(new  NetworkInterceptors())

// 请求服务器拦截器

interceptor.add(new  CallServerInterceptor(forWebSocket))

请求是从上向下一层一层传递,响应从下往上,最终建立一个Socket连接,把我们的http报文写出去,从服务器读取响应response

重定向与重试拦截器:在交给下一个拦截器之前,负责判断用户是否取消了请求,在获得结果之后会根据响应码去判断是否需要重定向,如果满足就会重启执行所有拦截器

微信截图_20230310112718.png

重试:

微信截图_20230310112820.png

桥连拦截器: 在交给下一级拦截器之前,负责将http协议必备的请求头加入其中(eg:Host), 并添加一些默认的行为(eg: GZIP),在获得结果之后,保存cookie接口,并解析GZIP数据

微信截图_20230310113005.png

缓存拦截器: 交给下一级之前,判断是否使用缓存,获得结果后判读是否缓存

缓存策略:拦截器通过CacheStrategy判断使用缓存或者网络请求,此对象的netWorkRequest和cacheResponse分别代表发起请求或者直接使用缓存

微信截图_20230310120336.png

networkRequest如果存在优先发起网络请求,否则使用cacheResponse缓存,若都不存在则请求失败

**缓存检测**

微信截图_20230310121443.png

连接拦截器: 交给下一级之前,负责找到或者新建一个连接,并获得对应的Socket流,在获得后不进行额外的操作 image.png

连接池清理

image.png

请求服务器拦截器: 进行真的的与服务器之间的通信,向服务器发送数据,解析读取的响应数据

桥连拦截器完成之后,请求的报文heads才算完整

执行流程

微信截图_20230310103711.png