Volley源码阅读

从Volley的使用步骤来看源码,首先是new一个RequestQueue,来看Volley.java的newRequestQueue方法:

通过重载方法的调用最终会调用执行这个方法:

在这个方法里首先初始化cacheDir,然后根据API版本是否大于9初始化一个HurlStack对象或是一个HttpClientStack对象。紧接着初始化一个BasicNetwork对象和一个RequestQueue对象,并且调用了RequestQueue的start方法。一下子多这么多对象,一个个来看看这些都是干些啥的?首先是HurlStack查看源码发现HurlStack继承HttpStack接口,而HttpStack接口只有一个performRequest方法。

返回来看HurlStack类:


查看源码大概能看出来HurlStack类的作用通过HttpURLConnection处理Http请求并返回结果。HttpClientStack对象同样是通过HttpClient处理Http请求。接着将stack传入BasicNetwork对象中,在BasicNetwork中调用HttpStack.performRequest的方法得到了一个httpResponse,在封装成NetworkResponse类返回。

最后RequestQueue这个类顾名思义是一个请求调度队列,来看他的start方法。


start方法中又new 了一个CacheDispatcher,CacheDispatcher这个看名字是一个缓存调度线程,紧接着循环new出多个(默认四个)NetworkDispatcher网络请求调度线程,并且调用了线程的start方法。这样一共就有五个线程在运行等待请求进入。
按照Volley的使用流程,创建完请求队列后就是创建各种Request请求通过调用RequestQueue请求队列的add方法,将Request加入队列。所以接着看RequestQueue的add方法。

add方法中238行首先是判断传进来的Request是否允许缓存,如果不允许就直接加入网络请求队列mNetworkQueue。允许再判断等待请求队列中是否已经有了相同的请求如果有就加入到mWaitingRequests队列中,没有就加入缓存队列mCacheQueue中。到此add方法结束,add方法中并没有请求网络或操作缓存。当将请求加入到缓存线程或者网络请求线程中时,在之前RequestQueue中start初始化缓存调度线程CacheDispatcher和网络请求调度线程NetworkDispatcher,就会轮询各自的队列,发现请求任务就开始执行。

接下来首先CacheDispatcher的run方法里的代码:



while(true)首先看到的是这个,就说明这是个一直执行的线程。mCacheQueue.take()先从缓存队里取出一个请求,判断请求是否被取消,没被取消再判断缓存中的响应结果是否为null,如果为null就把请求加入网络请求队列,如果没有再判断响应是否过期,过期了同样再把请求加入网络请求队列,没过期调用request.parseNetworkResponse方法解析Response。接着再判断返回响应是否需要refresh,不需要就调用ResponseDelivery的postResponse方法将请求和响应回调到主线程,如果需要同样就调用ResponseDelivery的postResponse方法将请求和响应回调到主线程但是同时还将该请求加入到网络请求队列中刷新响应。

接着查看NetworkDispatcher网络请求调度线程的run方法

同样的while(true)一直循环执行,从队列中取出一个请求判断是否被取消,没被取消调用Network的performRequest方法传入request返回一个NetworkRespose结果。这里的Network就是最初在newRequestQueue中初始化的BasicNetwork,BasicNetwork的performRequest方法中再次调用了 mHttpStack.performRequest,这里的mHttpStack同样是之前在newRequestQueue方法中根据API等级创建的HurlStack或HttpClientStack对象,分别是使用HttpURLConnection和HttpClient来发送完成网络请求。最终会封装成一个NetworkRespose结果对象返回。

接下来拿到返回结果后125行调用request.parseNetworkResponse解析返回的数据,并且根据是否缓存写入缓存,最后和CacheDispatcher同样调用ResponseDelivery的postResponse方法将请求和响应回调到主线程。在这里我们看到解析数据是调用的Request类的方法,是因为不同Request解析方法不一样,这就是我们自定义Request时候必须重写parseNetworkResponse方法的原因。
接下来来看ResponseDelivery这个接口具体是怎样将响应返回到主线程的:

NetworkDispatcher里的ResponseDelivery由构造函数初始化传入,最终在RequestQueue的构造函数里找到:

这里new了一个ExecutorDelivery传入的是主线程的Handler,继续查看ExecutorDelivery

ExecutorDelivery的构造函数中初始化一个mResponsePoster,在execute方法中通过传入的主线程的handler处理Runnable,就可以在主线程执行了。接着来看postResponse方法:

调用mResponsePoster.execute传入了一个ResponseDeliveryRunnable对象,继续深入ResponseDeliveryRunnable是一个私有内部类:

可以看到run方法里在判断请求响应是否成功后分别调用了 mRequest.deliverResponse方法和 mRequest.deliverError方法。这两个方法又是Request的方法,deliverError在Request类中返回的是一个VolleyError类型,而deliverResponse在Request类中是一个抽象方法,在不同的Request中实现不同,例如StringRequest:

这个mListener就是在创建StringRequest时候传入的Listener,执行请求成功后的操作。同理ImageRequest中返回的就是个Bitmap:

至此 Volley的使用流程也就走通了,整个流程的源码也就看完一遍,了解了Voelly中从请求队列创建再往队列里加入Request请求,最终网络请求执行完成回调到主线程的过程。整体的流程图如下:

那么为什么说适合数据量小,通信频繁的网络操作呢?数据量大的不适合不能做吗?
答案还是要从源码中寻找,找到网络请求调度线程,从这里来看:

在NetworkDispatcher里调用了Network的performRequest方法来获得请求结果,Network接口的实现类是BasicNetwork,进入BasicNetwork里来看performRequest方法:

这里之前看过实际上是调用了HttpStack.performRequest去获得httpResponse。在得到httpResponse之后又判断了httpResponse中的实体Entity是否为null,不为null调用了一个entityToBytes方法,从名字可以看出这应该是把实体Entity转成byte[]数组的方法,最终将byte数组传入NetworkResponse对象返回。

再深入entityToBytes方法查看:

从这个方法可以看出这就是从entity中读出content到bytes里,就是个输入输出流的读写过程,通过toByteArray方法返回一个byte[]数组。和一般不同的是read所用的byte[]不是直接new出来的是通过 mPool.getBuf(1024)获得的,也就是说不用每次从内存新分配一块区域,从这个Pool缓存池取就可以了,那么来看这个Pool是个啥?

从74行可以看出这个缓存池的大小是4096字节,具体还要看ByteArrayPool这个类

ByteArrayPool中有两个byte[]的List,mBuffersByLastUse是按byte数组使用时间先后排序存放,mBuffersBySize是按byte数组大小长度来存放。

ByteArrayPool主要就这三个方法,getBuf()从mBuffersBySize取出一个适合大小的byte[],从代码来看循环mBuffersBySize找到长度大于所需长度的数组就返回这个byte[],并且从mBuffersBySize和mBuffersByLastUse中移除这个byte[],当前Pool长度也减去该byte[]的长度,如果过没有找到就new一个新的适合长度的byte[]返回。returnBuf()方法是当取出的byte[]使用结束后返回到Pool中,从代码看首先把这个刚用完的byte[]添加到mBuffersByLastUse最后,再通过二分查找法找到byte[]长度适合的在mBuffersBySize里的位置,将byte[]放入mBuffersBySize,并且将当前Pool长度加上该byte[]的长度,然后调用清理方法trim(),判断当前Pool长度是否超过最大长度,超过了的话就从mBuffersByLastUse移除第0个byte[],并且同时从mBuffersBySize移除这个byte[],当前Pool长度也减去相应的长度。
回过头来,再回到为什么说Volley适合数据量小,通信频繁的网络操作的问题上,首先可以看出每次请求返回结果都需要保存在内存中,并且需要通过ByteArrayPool转换,但是ByteArrayPool长度默认只有4096字节,所以返回结果数据量太大会造成溢出。其次也是因为使用ByteArrayPool不用每次都重新分配新的内存空间,而是先从ByteArrayPool中寻找是否有合适的byte[],减少了内存分配的次数,所以说Volley适合数据量小,通信频繁的网络网络请求的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值