Volley网络请求框架源码解析

xiaoxiao2021-02-28  119

Volley网络请求框架源码解析 Volley框架是谷歌公司于2013年推出的一款适用于请求数据量小,但拥有高并发特性的网络请求框架。通过观看以下这张图就可以看出,该框架比较适合处理请求数量小,但请求较为频繁,并发量比较大的应用场景。 Volley框架在使用时非常简单,仅需要三个步骤即可实现不同的请求操作,主要分为以下三个步骤。 第一步:创建RequestQueue请求队列,这是Volley框架的核心部分之一,关于这部分源码,将在后面进行详细介绍。 第二步:创建一个请求Request,在Volley框架中,提供了多种不同的请求类型,它们都是基于Request接口进行了封装,如StringRequest、JsonRequest、ImageRequest等等。 第三步:将创建出的Request,添加到RequestQueue队列中。仅需要这三个步骤,Volley框架便可以实现对客户端网络请求的分发、处理和反馈。 通过阅读上面这段打开可以看出,通过Volley进行网络请求时,完成RequestQueue队列创建后,只需要不断往这个队列中添加Request即可。 以下这张图是Volley框架的核心架构图,其中,这张图的红色方框部分,为Volley框架的核心层,接下来我们主要分析该框架的核心层代码部分。 一、Volley框架核心层代码解析 结合上面关于Volley框架的整体架构图可以看出,Volley框架执行的流程首先根据客户端添加到RequestQueue中的请求Request,然后分别由CacheDispatcher或NetWorkDispatcher处理客户端的请求,当完成请求的处理后,解析得到响应Response,最后由ExecutorDelivery将请求的处理结果Response,反馈给上层的调用者,这样一来,一个完整的Http请求就完成了。 首先我们分析Volley框架执行网络请求的第一步,RequestQueue请求队列的创建以及RequestQueue内部源码解析。 由于执行网络请求前,需要首先构建出RequestQueue,在Volley框架中,提供了用于构建RequestQueue的代码,客户端可以通过Volley中提供的newRequestQueue方法,构建出该队列,代码如下所示。 构建RequestQueue的方法如下所示,在该方法的最后几行代码中,首先构建了一个BasicNetWork,这个主要用于将网络请求的结果进行转换,暂且这里我们先不分析它。接下来通过实例化的方式创建了一个RequestQueue请求队列,并通过start方法将其启动,最后该方法将创建的这个RequestQueue反馈给了调用者,使调用者可以向RequestQueue中添加请求Request。 在该方法的前几行代码中,首先是客户端缓存的操作,接下来根据设备的SDK版本,使用了不同的网络请求方式,针对Android2.3以上的系统,则是通过HttpUrlConnection实现的网络请求操作;而针对Android2.3以下的系统,则是通过HttpClient实现的网络请求,这与Android系统有很大关系,在Android2.3以上的系统中,HttpClient会存在一些问题,所以,Volley框架在设计时,对于2.3以上的系统,就通过HurlStack对HttpUrlConnection进行封装,实现的网络请求处理。而对于Android2.3以下的系统,则是通过HttpClientStack实现的网络请求具体操作,其内部对HttpClient进行了一层包装。 在Volley框架中,关于Http网络协议层的封装设计,都是基于HttpStack这个接口展开的。由于在Android2.3以下的系统中,网络请求最终实现是通过HttpClient实现的;而在Android2.3以上的系统中,网络请求又是通过HttpUrlConnection实现的。所以,Volley框架通过这个HttpStack接口协议,分别实现了满足不同系统版本的HttpClientStack和HurlStack,以确保该框架对Android平台兼容性。 通过阅读HttpStack的接口描述可以看出,该接口是对Http请求协议的一个抽象封装,而下面的performRequest方法则是根据指定的请求Request以及请求头header,实现网络请求操作。由此可以看出,HttpStack接口的具体实现者,是实现网络请求的部分。 在HttpClientStack的源码中,查看构造方法可以看出,这里完成了HttpClient的初始化操作,而在HttpClientStack的performRequest方法中,最终是通过HttpClient执行execute方法实现的网络请求,而HttpClientStack中的HttpClient实例,则是Volley框架在执行RequestQueue创建时,传递进来的,代码如下所示。 通过观察上图可以看出,HttpClientStack在创建时,通过AndroidHttpClient的newInstance方法,构建出了HttpClient实例,并传递到了HttpClientStack中,这样一来,HttpClientStack中的HttpClient就被初始化成功了。关于HttpClientStack的构造方法以及请求的具体操作,代码如下所示。 HttpClientStack的performRequest方法首先根据客户端的请求Request以及请求头headers,构建出了HttpUrlRequest,接下来向请求中添加请求头信息,然后进行请求参数的一些设置,如连接超时时长,最后通过HttpClient执行execute方法将HttpUrlRequest发送给服务器。 在HurlStack的源码中,performRequest方法处理Http请求时,则是通过HttpUrlConnection实现的,源代码如下所示。 接下来我们继续分析Volley中的newRequestQueue方法,当创建HttpStack之后,接下来会根据HttpStack创建出一个BasicNetWork,通过阅读BasicNetWork源码可以看出,这里对HttpStack又进行了一层封装,BasicNetWork在执行时可根据客户端的请求Request,然后将请求派发给具体的HttpStack,来最终实现网络请求,并且在请求结束后,构建出NetWorkResponse响应,并返回给上一层。 观察BasicNetWork的performRequest方法可以看出,这个方法接收一个客户端的请求Request,然后根据客户端的请求,组织了headers请求头数据,接下来通过mHttpStack执行了performRequest方法,得到了httpResponse响应头。基于java的多态特性,mHttpStack执行的performRequest方法,由具体的实现来完成响应头的处理,在BasicNetWork中,这里仅对HttpStack进行了接口层的依赖。 在performRequest的方法末尾,BasicNetWork则根据响应头信息,构建出了NetworkResponse响应,并进行了返回,代码如下所示。 以上这些都是对Volley网络请求框架中,一些细节和网络请求具体实现部分的源码解析,可以看出,Volley网络请求框架通过对HttpClient和HttpUrlConnection进行了一系列的接口封装,使框架可以对Android2.3以下的系统进行兼容,接下来我们将分析Volley框架的主干流程。 首先从RequestQueue的源码开始分析,这里我们将主要分析RequestQueue的作用,它与CacheDispatcher、NetworkDispatcher之间是如何进行协调的。 我们知道,通过Volley框架进行网络请求时,首先需要创建RequestQueue,然后需要创建具体的请求Request,最后还需要将Request添加到RequestQueue中,这样网络请求才会被真正执行。 我们先从RequestQueue的创建部分开始分析,结合Volley中的newRequestQueue方法,最终RequestQueue会执行这个构造方法,代码如下所示。 在这个构造方法中,初始化了Cache缓存、Network以及用于处理Http请求响应的ResponseDelivery等,可以看到,在该方法中,还初始化了一个NetworkDispatcher数组,并指定了数组大小。 由于在Volley的newRequestQueue方法的最后,通过RequestQueue执行了start方法,因此,接下来我们查看RequestQueue中的start方法具体都做了些什么。 在RequestQueue的start方法中,首先将当前正在运行的分发器给停止了。接下来则根据缓存队列mCacheQueue、mNetworkQueue以及mDelivery创建了CacheDispatcher,然后将其启动。通过字面意思我们可以看出,CacheDispatcher是Volley框架中的缓存分发器,我们暂且先不关心它在Volley框架中主要起到了什么作用,先继续向下分析。 接下来则根据构造方法在执行时创建的NetworkDispatcher数组,对该数组进行了遍历,并在遍历过程中,依次创建了NetworkDispatcher,而创建NetworkDispatcher时,所使用的参数与创建CacheDispatcher时所使用的数据相同,唯一不同的是还传入了mNetwork这个实例。 虽然在前面的内容中我们没有对Network这个实例进行分析,但通过分析BasicNetWork可以看出,它是Network接口的一个具体实现,而BasicNetWork又是对HttpStack进行了一层封装。接下来我们再结合CacheDispatcher和NetworkDispatcher在构建时的不同就可以看出,CacheDispatcher是专门用于处理缓存操作的,也就是说,当某个请求可以通过缓存的方式获取结果时,就可以通过CacheDispatcher;而NetworkDispatcher则是真正进行网络请求的分发器,因为NetworkDispatcher分发器中,执行网络请求时,会通过Network接口产生一个多态调用,从而发起网络请求,并最终处理网络请求得到的响应。 由此可以得出结论,在Volley框架中,CacheDispatcher是不具备网络请求功能的,它主要用于处理对缓存的访问,这也是Volley框架能高效处理频繁的网络请求的因素之一,当遇到频繁的网络请求操作时,只要NetworkDispatcher得到了响应数据,就会将响应的数据,更新到CacheDispatcher缓存中,这样一来,当第二次执行相同的请求时,如果这个缓存没有失效,那么就可以直接通过CacheDispatcher获取到响应,而不需要再通过NetworkDispatcher进行网络请求了,从而为客户端节约流量。 而NetworkDispatcher则是专门用于处理网络请求的,在RequestQueue的start方法中,被构建的NetworkDispatcher被一一放入到了mDispatcher数组中,并逐一启动了这些NetworkDispatcher,这也就可以得出结论。Volley框架对网络请求的处理,是通过多线程并发的形式实现的,也就是说,Volley框架可以同时处理多个不同的网络请求操作,这也就印证了2013年谷歌IO大会上对Volley框架的那张急促射箭的图的含义。关于RequestQueue的start方法源码,如下所示。 RequestQueue的start方法仅仅将缓存和网络分发器进行了启动,而确保网络请求的执行,还需要调用RequestQueue中的add方法,将Request传递到该方法中才可以执行。因此,接下来我们按照Volley请求的第三个步骤,将请求添加到RequestQueue中,所以,接下来我们分析RequestQueue中的add方法。 执行add方法时,会首先将请求Request添加到mCurrentRequests这个队列中,通过观察mCurrentRequests的字面含义可以看出,这个队列用于存放当前正准备执行的网络请求,代码如下所示。 接下来则检查当前请求Request是否可以被缓存处理,如果这个请求不能被缓存记录,则将其添加到网络请求队列mNetworkQueue队列中,然后直接返回。 相反,如果请求Request此时可以进行缓存处理,则上面的这段代码不会执行,接下来的操作会以缓存的主键cacheKey进行,代码如下所示。 由于以上这段代码主要是根据缓存cacheKey以及mWaitingRequests队列进行的,而等待队列mWaitingRequests在Volley框架中,也会被NetworkDispatcher这个线程进行访问,所以,这里首先进行了线程安全保护措施。首先,这时会根据Request获取缓存的cacheKey,接下来根据cacheKey检查等待队列中是否存在该请求。我们首先认为此时该请求不存在,这时,就会根据cacheKey,向mWaitingRequests中添加一个空值,最后返回调用add方法时传递的Request。这样一来,mWaitingRequests等待队列中就存在了这个请求所对应的cacheKey,只是value是空的。 接下来如果该请求再次被执行时,由于mWaitingRequests队列中已经存在这个请求对应的cacheKey,这时再次执行到这里时,就会执行以下这段操作。如果此时请求Request为第二次执行,那么,根据cacheKey从mWaitingRequests队列中获取到的stageRequests为空,这时会初始化stageRequests,并将当前请求Request添加到stageRequests中,接下来对mWaitingRequests队列进行更新。 结合这段逻辑继续分析,如果这时请求被重复执行至第三遍,就不会重新初始化stageRequests了,只会对stageRequests中的内容进行更新,并最终对mWaitingRequests队列进行更新,而客户端的请求Request,最终是被记录到stageRequests中的。关于Volley框架RequestQueue中的add方法,就分析到这里了。 接下来回到RequestQueue的start方法继续分析,由于通过Volley进行网络请求时,第一步是创建RequestQueue,第二步是创建具体的Request请求(如StringRequest、JsonRequest等),第三步将创建的Request,添加到RequestQueue队列中即可进行网络请求操作。客户端在构建RequestQueue时,会执行start方法。在RequestQueue的start方法中,关于网络请求和缓存的处理,分别通过两种不同的分发器进行的,一种是缓存分发器CacheDispatcher,另一个是NetworkDispatcher网络分发器。 因此,接下来将会分别介绍这两种分发器的源码,看看网络请求以及缓存处理是如何通过分发器实现的。 NetworkDispatcher是专门用于处理网络请求的分发器,它实际上是一个线程,通过继承Thread实现。NetworkDispatcher在构建时会根据Volley框架中的网络请求队列mNetworkQueue、mNetwork、mCache以及mDelivery完成。 其中,mNetworkQueue队列用于存放网络请求Request,当NetworkDispatcher执行时,会不断从mNetworkQueue队列中获取需要处理的网络请求,客户端通过RequestQueue执行add方法时,如果当前请求不被缓存支持,则会将请求Request放入到mNetworkQueue队列中,这样一来就能确保网络请求分发器在执行时,可以从这个队列中,获取到网络请求,并最终完成请求操作。 mNetwork实际上是Volley框架中的Network接口,这个接口是用来实现具体网络请求操作处理的。我们知道,Volley框架针对Android2.3以上的系统,是通过HttpUrlConnection实现的网络请求操作;而针对Android2.3以下的系统,则是通过HttpClient实现的网络请求。这里被Volley统一定义成了一套接口层的协议标准,即HttpStack接口。可以这样理解,HttpStack接口是对HttpUrlConnection和HttpClient的接口封装,分别对应两种不同的实现,而在实际执行中,Volley则会根据系统版本来决定具体使用哪一个具体的实现。 Network接口则是对网络请求的具体实现进行了封装,具体实现是通过BasicNetwork来完成的。前面已经针对BasicNetwork进行了源码分析,这里大致总结一下,BasicNetwork根据客户端请求Request以及HttpStack的具体事例,来决定使用HttpUrlConnection或HttpClient完成请求,并根据请求的结果,返回NetworkResponse响应。 接下来我们分析NetworkDispatcher网络分发器的具体执行过程,在run方法中,会开启一个死循环,并在死循环过程中,会不断从mNetworkQueue队列中获取客户端的请求Request,代码如下所示。 接下来会通过Request中的isCanceled方法来检查客户端当前请求是否被取消,因为如果此时取消了这个请求,就不会再继续向下执行了,而是继续进行循环,从mNetworkQueue队列中继续取出下一个请求,代码如下所示。 如果请求此时没有被取消,接下来则会根据Request通过mNetwork调用performRequest方法进行网络请求,并得到NetworkResponse,代码如下所示。这个mNetwork虽然是Network接口,但具体实现为BasicNetwork。关于BasicNetwork的源码,在前面已经介绍过,这里就不再分析它了。 当NetworkDispatcher获取到NetworkResponse后,如果这时遇到了304响应,或此时已经有响应正在进行,则不会继续执行,这时会从队列中取出下一个Request,并进行以上这些操作。 如果这时没有发生以上这些情况,则根据BasicNetwork返回的网络请求结果NetworkResponse,对其进行解析,将其解析为Response响应,代码如下所示。 接下来检查请求是否可以进行缓存处理,符合这两个条件时,会根据Request请求的cacheKey作为主键,将响应response的cacheEntry作为value,记录到缓存mCache中,代码如下所示。 完成以上这些操作后,会通过mDelivery将网络请求的Request以及响应response,反馈给应用层,在这个过程中,将会完成子线程与主线程之间的切换。 因此,通过分析Volley框架的NetworkDispatcher的代码,可以将其执行流程归纳为以下这样,如下图所示。 关于NetworkDispatcher网络分发器,在Volley框架中,当RequestQueue通过start方法启动时,会同时创建多个NetworkDispatcher,并将其启动。因此,Volley框架可以同时支持数据量小,但请求次数较多的高并发网络操作,这也是Volley框架在这方面高效的原因之一。 接下来我们介绍CacheDispatcher是如何工作的,在Volley框架中,相同请求且缓存未失效的情况下,第二次执行时,就会通过CacheDispatcher获取缓存中的请求结果,从而避免客户端产生更多的流量消耗。由于CacheDispatcher也是通过继承Thread实现的,因此,CacheDispatcher中对缓存的操作,都是在run方法中异步执行的。 CacheDispatcher缓存分发器与网络请求分发器NetworkDispatcher相比,存在一定的不同的之处。首先,CacheDispatcher主要作用是维护缓存,并通过对缓存的访问,直接根据缓存数据解析得到响应,并通过mDelivery将响应结果进行返回。再有就是,CacheDispatcher不负责执行网络请求操作,但它会在缓存失效的情况下,将客户端的请求Request添加到RequestQueue的网络请求队列中,从而确保框架自身可以在缓存失效的情况下,通过网络请求得到最新的响应数据。 接下来开始分析CacheDispatcher的源代码,首先从构造方法入手,其源代码如下所示。可以看出,CacheDispatcher在构建时,接收一个用于存放网络请求的缓存队列mCacheQueue、网络请求队列mNetworkQueue,当缓存失效时,会将Request放入到这个mNetworkQueue队列中,还接收一个mCache缓存接口,这个缓存容器主要用于存放缓存的数据,在Volley框架的网络分发器NetworkDispatcher中,可以看到当开启缓存,并且缓存的cacheEntry存在时,会记录到mCache缓存中。最后,mDelivery用于将请求所得到的结果进行反馈。 在CacheDispatcher中,关于缓存的操作,同样是在死循环中进行的。首先会从队列中获取客户端请求Request,如果此时该请求已经被取消,则不再继续向下执行,接下来CacheDispatcher则会从队列中继续取出下一个Request请求,代码如下所示。 如果这时请求尚未被取消,则会根据Request中的cacheKey,从缓存中获取entry,接下来则检查该请求对应的entry缓存是否存在。如果此时该缓存不存在,那么,会将该请求Request放入到mNetworkQueue队列中,此时表明客户端所发起的这个网络请求,缓存中没有,需要通过NetworkDispatcher根据该请求,从网络中获取数据。由于NetworkDispatcher是通过不断从mNetworkQueue队列中取出请求Request并完成网络请求的,所以,在CacheDispatcher中,一旦entry为空,此时就会认定该请求需要通过网络来完成。当请求被放入到队列后,就不会再继续向下执行了,代码如下所示。 当CacheDispatcher从缓存中获取到非空的entry后,接下来还要检查此时缓存是否失效,如果该缓存已经失效,同样会将请求放入到mNetworkQueue队列中,并不再向下执行,继续回到循环的起始点,代码如下所示。 如果此时以上这几种情况都没有发生,这时将会根据缓存entry中的data,通过request进行解析,从而直接根据缓存得到Response响应。 接下来CacheDispatcher会对缓存entry进行检查,验证缓存是否需要更新。如果此时缓存不需要进行任何更新,则直接通过mDelivery将从缓存中得到的响应Response返回给应用层,代码如下所示。 相反,如果此时缓存需要进行更新,这里将会首先为请求设置一个过期的缓存,接下来会通过mDelivery进行线程切换,然后再次将请求Request放入到网络请求队列mNetworkQueue中,代码如下所示。 通过分析CacheDispatcher的代码执行逻辑,关于Volley框架中的缓存分发器的执行流程,如下图所示。

转载请注明原文地址: https://www.6miu.com/read-46588.html

最新回复(0)