android Volley源码解析笔记

现在android主流用的网络请求框架是Okhttp3和Retrofit2+Rxjava两种,而我之前很多项目都是用的Volley,然后阅读开源框架源码的经验不够,所以就拿Volley练手了。
本文不会贴太多源码,我是带着问题去分析volley源码,如果想边看源码边解析的,可以看下这位童鞋的一系列文章。

Volley对于https的支持

Volley本身支持https,但是由于HurlStack中171行的mSslSocketFactory没有被实例化,导致volley默认不支持https,通过添加实现了X509TrustManager的HTTPSTrustManager类,从而使mSslSocketFactory被实例化,volley就能支持信任所有https啦。

volley不适合post大量数据,也不适合上传下载大量文件原因

  1. volley中为了提高请求处理的速度,采用了ByteArrayPool进行内存中的数据存储的,如果下载大量的数据,这个存储空间就会溢出;

  2. volley的线程池(4个工作线程+1个阻塞队列)是基于数组实现的,不会自动扩展,一旦大数据上传或者下载长时间占用了线程资源,后续所有的请求都会被阻塞。

volley适用情况?

volley存储的时候优是从ByteArrayPool中取出一块已经分配的内存区域, 不必每次存数据都要进行内存分配,而是先查找缓冲池中有无适合的内存区域,如果有,直接拿来用,从而减少内存分配的次数 ,所以他比较适合频次高但数据量少的网络数据交互情况。

volley中关于磁盘缓存

首先,磁盘缓存是针对于静态文件的,典型的就是图片文件了,如果用了volley的ImageLoader去下载图片,就会用到磁盘缓存(这里我纠结了好久,我之前还一直以为普通网络请求也可以用到磁盘缓存的)。关于磁盘缓存,主要是在DiskBasedCache类中实现。volley缓存有条件的,一个是手动设置要不要缓存,一个是看服务器返回的和缓存相关的头信息是不是空,得服务器支持才行。当用ImageLoader下载了图片时,会在/sdcard/Android/data/packageName/cache/volley目录下生成很多个文件名为一串数字的文件,命名规则为

private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}

关于http缓存

如果你返回的json数据头信息也设置了缓存,会缓存的,但是一般服务器不会设置缓存这些数据的
http缓存分为强制缓存和比较缓存两种缓存机制:
a. 对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
b. 对于比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
缓存只针对于静态文件,主要是针对web浏览器上的,volley只是多做了一步,其实没啥用。

volley如何将静态文件写入到文件中的?

在DiskBasedCache类的put()方法中,将从服务器中获取的Entry对象中的data数据写入到文件输出流中,文件命名规则是getFilenameForKey()

网络线程调度(NetworkDispatcher) vs 缓存线程调度(CacheDispatcher)

Volley初始化以后就创建了5个后台线程在处理请求。只要你没做处理,这5个线程一直在后台跑。为了节省资源,在同一个App中最好使用同一个单例Volley RequestQueue队列来处理所有请求,以免创建过多线程浪费资源。还有在退出这个应用时,应该调用 RequestQueue#stop方法来干掉所有Volley线程。如此才是使用Volley最优雅的方式
默认4个网络线程调度,1个缓存线程调度。缓存线程调度是用于静态文件的,常见的就是图片下载。NetworkDispatcher和CacheDispatcher的run()方法中,都有阻塞队列类BlockingQueue,都用了take()方法阻塞着,如果一个UI中有大于4个网络请求,最多只有4个工作线程,其他网络请求只得等这4个工作线程中有操作完了的,才能用此线程。

阻塞队列 vs 线程池

阻塞队列(BlockingQueue)和线程池(ExecutorsService)是两个不同的概念。比如说在Volley中的网络请求线程调度,默认生成4个网络请求线程,每个线程的run()方法都会有同一个阻塞队列对象在那阻塞着,相当于一个阻塞队列对象对应4个网络请求工作线程,看哪个线程工作完,就执行阻塞等待。而线程池,我发现Volley网络请求时,并没有用到线程池,只是用了阻塞队列和默认的4个工作线程。而在网络响应分发也没用线程池,只用了Executor接口而已。 所以,Volley压根就没用线程池。其实就是用4个工作线程加上1个阻塞队列,充当了线程池的角色。app端不管一个页面有多少个请求,也还是得有线程池的思想,否则当页面多时,new出来的线程越来越多,这肯定是不合理的。

线程分发机制(主要用于返回的数据)

在RequestQueue的构造方法中new了一个ExecutorDelivery实例,用于从服务器返回的数据分发。

StringRequest request = new StringRequest(Request.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {

                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {

                }
            });

ExecutorDelivery中内部类ResponseDeliveryRunnable里调用了mRequest.deliverResponse()方法,而StringRequest类的deliverResponse()方法有调用了内部类Listener的onResponse()方法,从而将服务器请求的数据,以string的形式吐了出来。

你可能感兴趣的:(android)