当前位置:网站首页 > 技术博客 > 正文

okhttp官方文档



一直都想写一篇有关的源码文章,今天推荐一篇关于解析源码的文章,希望大家能有所收获。

作者:RayC 链接:https://juejin.cn/post/

OkHttp是Android开发中非常常用的一个网络请求库了,它支持HTTP1、HTTP2等多种协议,是我们日常开发中非常给力的帮手。本篇文章基于OkHttp4.9.0版本代码,从OkHttp的创建开始,探究OkHttp是如何发起一次网络请求的。如果你有耐心看完,相信你会对OkHttp有了一个深刻的了解。如果觉得太长不看的话,看一下总结也是不错的呢(手动滑稽)

使用OkHttp的第一步,自然都是创建OkHttpClient了:

通过构造方法进入,看看这个OkHttpClient到底是什么:

可以看出OkHttpClient实例就是个配置类,当实际发起请求的时候,会采用用户设置的配置。采用了Builder模式的设计,让用户更方便的配置这一系列参数,灵活的构造较多的配置。

基础的请求构造类,基本使用:

Request类用来描述单次请求的参数信息等,包含域名、请求方式、请求头、请求体等一系列信息。通过Builder我们可以链式调用,比较优雅的配置这一系列信息。Request类和OkHttpClient类一样,本质是一个描述对象。

请求创建完成,就可以调用OkHttpClient来发起一次请求了,我们需要通过OkHttpClient的实例方法来发起一次请求:

Call是一个接口,定义如下:

enqueue

异步请求方法会创建一个AsyncCall,并调用OkHttpClient配置的Dispatcher处理此请求。

AsyncCall实现了Runnable接口,最终会被调度器的线程池进行执行,具体后续再来分析。

execute

调用了execute之后请求会被加入同步请求队列,然后创建响应责任链发起请求。请求完成会从调度器中移除本次请求。

getResponseWithInterceptorChain

重点来了,OkHttp发起一次请求都需要进行的方法,代码如下:

getResponseWithInterceptorChain方法会按一定的顺序构建拦截器列表,这里用到了责任链模式,处理完拦截器列表后,会创建拦截器责任链,拦截器会按顺序依次调用,处理完成之后,再将返回信息返回给用户。

cancel

一次请求的取消其实就是取消了后续的IO操作和断开连接,然后进行事件通知。因为调用此方法的时候连接和IO可能还未开始,所以需要进行判空。

通过追踪一次同步请求的发起,我们会发现最终会创建一个RealInterceptorChain实例,并调用了其proceed方法,接下来就来追踪其代码,看看内部到底是如何实现的。

proceed的方法也不复杂,里面有一系列的检测方法,核心代码其实只有几行,大致逻辑如下:

为什么可以链式调用下去呢?这里可以看一下Interceptor的接口定义

Interceptor只有一个方法,实现了intercept方法后需要调用传递进来的Chain,上面我们已经知道了这是下一个拦截器。调用了chain.proceed方法返回Response,将逻辑交由下一个拦截器处理。

再回过头看异步请求,上面我们可以知道,一次异步请求最终是调用了dispatcher.enqueue的方法,那么Dispatcher负责了什么呢?

Dispatcher主要负责异步请求的执行逻辑。Dispatcher中可以定义maxRequests来管理最大并发请求数量,maxRequestsPerHost来确定单个host的最大并发请求数量。

调用了enqueue方法后,会先上锁,然后在异步队列readyAsyncCalls中加入此请求,再检查当前请求的host有无其他call,找到了,则复用其他call的请求计数器。最后走到promoteAndExecute去执行。

promoteAndExecute方法会遍历异步请求队列,如果当前并发请求数量上限了,则会跳出,不执行任何请求。如果一个host的并发请求数量达到了上限,会跳过此请求。最后,为可以执行的请求进行调用。如果用户没有自行设置线程池,则Dispatcher内部会创建一个的线程池用来执行异步网络请求。

上面我也说过了,AsyncCall本身实现了Runable接口,这里被执行之后,会调用run方法,执行内部逻辑,具体逻辑和同步请求的逻辑基本一致,这里就不再赘述。请求完成后,不管结果成功失败,都会调用Dispatcher的finished方法。

finished方法被调用后会从请求队列中移除当前请求,再尝试执行剩余的请求。Dispatcher内部也维护了同步请求队列,当同步请求完成之后也会走类似的逻辑。

这个拦截器用来进行错误重试和重定向。拦截器内部是一个死循环。

网络请求的异常会被catch,然后会判断是否要重新进行请求。如果能正常走下去,则会对重定向相关进行判断,创建对应的请求。

ExchangeFinder

这个类在RetryAndFollowUpInterceptor中调用call.enterNetworkInterceptorExchange(request, newExchangeFinder)后被创建。这个类用来在RealConnectionPool连接池中找到一个当前请求可用的RealConnection,然后开启连接,进行接下来的IO操作。

这个拦截器会对指定的服务器打开连接,然后执行其他的拦截器

这个拦截器会调用RealCall的initExchange方法,并把当前的责任链传递过过去。

initExchange里会使用ExchangeFinder来寻找一个ExchangeCodec,这是一个网络请求的编码器,针对不同的协议会采用不同的方式来进行编码传输。

ExchangeFinder的find方法会去尝试找到一个与服务器之间的连接。追踪findHealthyConnection代码我们会发现它内部是一个死循环,不断的调用findConnection方法去寻找一个可用的连接。findConnection的代码就比较长了,这里就不贴出来了。大概的逻辑就是优先从连接池中找连接,如果没有找到可用的连接,则会创建一个RealConnection对象,存入缓存池中。

RealConnection

RealConnection是OkHttp实际建立连接的地方。通过connect方法建立与服务器的链接。通过追踪源码我们会发现RealConnection底层还是通过Socket建立连接的。

这是所有拦截器中的最后一个拦截器。在这个拦截器里会进行IO操作与服务器交互。OkHttp底层使用了OkIO来进行IO操作。

CallServerInterceptor的intercept方法很长,不过逻辑并不复杂。大致流程如下:

因为CallServerInterceptor是最后一个Interceptor,所以返回的Response会一级一级的向上传递,最后用户就能拿到包装后的响应Response了。

这个拦截器是应用和网络交互的一个桥梁。首先他会获取Request里的信息,根据请求内容在Request中添加或者一些请求头,这些都是用户未感知到的。同时这个拦截器还会读取Cookie配置,如果有Cookie信息,也会通过请求头带到服务端。

在Request信息完善后,会调用后续的责任链去处理完善的Request,并等待后续的返回。

收到后续的返回后,会对Response进行处理,并精简一些响应头。最终将完善的Response回传给用户。很多操作都是用户无感知的完成的,深藏功与名。

这就没什么好说的了吧?在经过一些列链式调用和处理,最终用户可以拿到一个Response对象。在这里用户就可以拿到请求的各种信息以及相应,剩下的就交给用户自行处理了。在用户拿到Response后,一次网络请求也算完成啦!

话不多说,先上图

再来文字总结一下,OkHttpClinent类是一个网络请求的配置类,我们通过构建Request来描述我们的请求信息,接着使用newCall方法可以创建一个RealCall实例。

RealCall可以选择同步执行或者是异步执行,他是我们与网络交互的一个桥梁。如果是同步请求,则会直接调用gerResponseWithInterceptorChain()方法创建拦截器责任链。如果是异步请求,则会分发到Dispatcher,Dispatcher最终会使用线程池执行这个请求,最终也还是会走到gerResponseWithInterceptorChain()方法。

在gerResponseWithInterceptorChain()方法中,会将所有的拦截器按照一定顺序封装进一个列表,构建一个RealInterceptorChain。proceed方法会调用各个拦截器的intercept方法,拦截器在处理完成自己的职责后,继续调用proceed方法。

RetryAndFollowUpInterceptor负责处理错误重试和重定向,BridgeInterceptor负责包装请求和返回数据,CacheInterceptor负责缓存的处理,而ConnectInterceptor则真正的打开了连接,最后通过CallServerInterceptor进行网络IO,发送和处理报文。

OkHttp底层采用了Socket进行网络连接,采用OkIO进行网络IO,有连接池逻辑,会存储RealConnection实例,减少过多连接产生的开销。

阅读源码不难,难的是写源码时候应该具备的思想。通过阅读OkHttp源码可以让我们学习到很多设计思想:通过Builder模式构建复杂对象、通过责任链模式有序的分发任务和回传、功能的抽象等等。好的开源框架的源码阅读真的能让我们收获很多,我们应该学以致用,这样就算有一天别人让你做一个类似的功能时候,你也不会毫无头绪。希望这篇文章能让你有所收获,当然我更建议你自己阅读一遍源码,也许会有不一样的收获。

版权声明


相关文章:

  • linux发行版大全2025-06-16 21:30:01
  • 网络调试助手app源码2025-06-16 21:30:01
  • sql聚合函数sum的功能是2025-06-16 21:30:01
  • seek怎么用2025-06-16 21:30:01
  • getline会读取空格吗2025-06-16 21:30:01
  • 思科模拟器配置vlan2025-06-16 21:30:01
  • c语言中括号匹配2025-06-16 21:30:01
  • 录制gif的app2025-06-16 21:30:01
  • 系统封装后怎么备份2025-06-16 21:30:01
  • linux中rename命令详解2025-06-16 21:30:01