Volley 是谷歌推出的一款轻便的网络请求库,所有不足 50 个文件,与 OkHttp 相比实在算不上臃肿。使用上也比较方便,你能够自由地定义本身的 Request 类,直接将返回数据转换成本身须要的格式,若是你须要使用 ImageView 展现网络图片的话那就更巧了,Volley 提供了一个 NetworkImageView 类,只须要像设置本地图片文件同样将网络图片的 url 设置进去,它就会自动加载图片并展现。apache
轻便并不意味着功能的不丰富,从自定义 Request 到缓存,再到返回数据的分发,Volley 都提供了可供用户自定义功能的接口,只要你须要的功能没有那么苛刻,Volley 基本都能知足平常的网络访问须要。json
同时,轻便意味着负载小,从发起网络请求,到收到返回数据,在 Volley 中走过的流程并很少,而且它内部还有一个轻量级的线程池,可以更有效地执行请求。缓存
下面先从一个标准的网络请求过一遍 Volley 的代码。安全
使用 Volley 以前要先建立一个 RequestQueue 用于分发执行 Request ,建立 RequestQueue 实例有两种方法,一种是使用工厂类 Volley ,Volley 提供了四个方法用于建立 RequestQueue 实例,分别是:bash
publicstaticRequestQueuenewRequestQueue(Contextcontext);
publicstaticRequestQueuenewRequestQueue(Contextcontext, BaseHttpStackstack);
publicstaticRequestQueuenewRequestQueue(Contextcontext, HttpStackstack);复制代码
第2、三个方法的第二个参数分别是 BaseHttpStack 和 HttpStack ,它们的做用是执行具体的网络请求,用户能够继承/实现它们使用本身的网络请求功能,如能够内置使用 Okhttp 执行实际的网络请求,不过须要将 Okhttp 返回的 Resopnse 转换成 Volley 的 HttpResponse 。markdown
两个类分别要实现的方法是:网络
// BaseHttpStack
publicabstractHttpResponseexecuteRequest(
Request<?>request, Map<String, String>additionalHeaders)
throwsIOException, AuthFailureError;
// HttpStack
org.apache.http.HttpResponseperformRequest(Request<?>request, Map<String, String>additionalHeaders)
throwsIOException, AuthFailureError;复制代码
目前 HttpStack 已经被标志为 Deprecated,缘由是它使用的仍是 org.apache.http.HttpResponse ,而最新的 BaseHttpStack 使用的则是 Volley 本身实现的 HttpResponse ,这里为了实现兼容,Volley 专门提供了两个类 BaseHttpStack 和 AdapterHttpStack 用于两者之间的转换,这里先按下不表。多线程
再说 RequestQueue 实例的建立,除了使用 Volley 以外,用户也能够直接调用 RequestQueue 的构造方法,构造方法有三个重载,分别是:app
publicRequestQueue(
Cachecache, Networknetwork, intthreadPoolSize, ResponseDeliverydelivery);
publicRequestQueue(Cachecache, Networknetwork, intthreadPoolSize);
publicRequestQueue(Cachecache, Networknetwork);复制代码
NetWork ,它声明了一个方法NetworkResponse performRequest(Request<?> request) throws VolleyError;
,用于从 Request 获得 NetworkResponse ,即网络请求的实现,Volley 有一个默认的实现 BasicNetwork ,即在使用 Volley 类建立 RequestQueue 时使用的类,但它也并非真正的网络请求类,上面也说到真正的执行网络请求的是 HttpStack 和 BaseHttpStack,Volley 也有一个默认的实现,继承自 BaseHttpStack 的 HurlStack 和实现自 HttpStack 的 HttpClientStack 。ide
四个参数的意义都很明确,Cache 中声明了一些缓存相关的方法,在 Volley 进行网络请求的时候它会将本身认为须要缓存的请求使用 Cache 缓存,Cache 能够由用户本身实现,也可使用 Volley 提供了一个默认 Cache ,DiskBaseCache ,该缓存会将数据保存在文件中,若是你以为保存在文件中比较慢,也能够利用 LruCache 实现一个内存缓存类。
Cache,缓存类
Network,网络请求类
threadPoolSize,线程池大小
ResponseDelivery,Response 分发类
后者都是在增长了一个默认参数的状况下调用前者,因此只看第一个构造方法,它有四个参数:
threadPoolSize 就是线程池中存在的线程数,RequestQueue 以后会根据这个参数建立线程池。
ResponseDelivery ,用于分发 Response ,从 Volley 的默认实现来看,它能够用做线程切换,并对 Request 和 Response 作一些额外的处理,如将其加入缓存等。 建立实例以后,还须要再调用 RequestQueue 的start()
方法完成启动,方法实现以下:
/** Starts the dispatchers in this queue. */ publicvoidstart() { stop(); // Make sure any currently running dispatchers are stopped. // Create the cache dispatcher and start it. mCacheDispatcher=newCacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. for(inti=0; i<mDispatchers.length; i++) { NetworkDispatchernetworkDispatcher= newNetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] =networkDispatcher; networkDispatcher.start(); } }复制代码
这个方法建立了一个 CacheDispatcher 和 n 个 NetworkDispacher ,这里的 n 就是构造方法中传入的 threadPoolSize 。每个 Dispatcher 都是一个小型的 Looper ,它们从 RequestQueue 中取 Request 并执行请求。用户调用add()
方法将一个 Request 放入 RequestQueue 中。在add()
方法中,RequestQueue 先判断 Request 是否能够缓存,而后将其加入到 mCacheQueue 或是 mNetworkQueue ,这两种不一样的队列正是对应着start()
方法中初始化的两种 Dispatcher 。
Volley 中共有两种 Dispatcher ,其中 NetwrokDispatcher 分发的是须要直接执行的请求,CacheDispatcher 分发的是可缓存的请求,换句话说,请求以前也须要检查是否已经存在缓存,缓存是否可用等。
它们有着共同的逻辑,都是继承于 Thread ,启动以后便不断调用processRequest()
方法从队列中取出 Request ,而后调用processRequest(Request)
处理 Request ,两者也就区别于此。
在 CacheDispatcher 中,processRequest(Request)
会先根据 Request key 从 Cache 中查找有没有缓存,若是没有,则将其加入到 NetwordDispatcher 中等待处理,不过在加入 NetworkDispatcher 以前还须要通过一步判断,调用maybeAddToWaitingRequests(Request)
方法判断这个方法是否须要交给 NetworkDispatcher 处理。由于在某些状况下,CacheDispatcher 可能会短期内同时处理两个相同的请求,可是这两个请求都没有缓存,因此要交给 NetworkDispatcher 处理,可是又没必要都交给 NetworkDispatcher 处理,由于两个请求相同,第二个请求只须要复用第一个请求的结果便可,这个判断过程就是maybeAddToWaitingRequests(Request)
要执行的,它会将后续的请求放到暂存到一个列表中,并监听第一个 Request 的请求状况,当请求完成的时候,就会再将结果分发给这些相同的 Request。
总结来讲,就是 CacheDispatcher 会优先从查找缓存,若是没有,就会将请求交给 NetworkDispatcher ,而且会截断重复请求。
NetworkDispatcher 与 CacheDispatcher 有着相似的逻辑,不一样之处在于processRequest(Request)
方法会调用 Network 完成网络请求,方法内部的执行大概分为三步:
判断 Request 的有效性
调用 Network 完成网络请求,并利用 Request 将 NetworkResponse 转换成 Response<T>
将 Request 和 Response 分发出去,并在须要缓存的时候将其加入缓存
分发的时候会调用 ResponseDelivery 的postResponse()
方法,这个方法的默认实现就是使用主线程的 Handler 将线程切换到主线程。
RequestQueue 提供了网络请求的一些功能,但具体的数据仍是由 Request 和 Response 持有。
Request 是一个抽象类,共有两个抽象方法:
/** * Subclasses must implement this to parse the raw network response and return an appropriate * response type. This method will be called from a worker thread. The response will not be * delivered if you return null. * * @param response Response from the network * @return The parsed response, or null in the case of an error */ protectedabstractResponse<T>parseNetworkResponse(NetworkResponseresponse); /** * Subclasses must implement this to perform delivery of the parsed response to their listeners. * The given response is guaranteed to be non-null; responses that fail to parse are not * delivered. * * @param response The parsed response returned by {@link * #parseNetworkResponse(NetworkResponse)} */ protectedabstractvoiddeliverResponse(Tresponse);复制代码
parseNetworkReponse()
负责将 NetwrokResponse 转换成 Response<T>,deliverResponse()
则负责将最终的数据分发出去。
除此以外 Request 里就存有一些请求参数、优先级以及 URL 和请求方法等。
要使用 Request 就须要一个继承 Request 的子类,在 Volley 中内置了四种 Request ,分别是 StringRequest、JsonRequest、ImageRequest 和 ClearCacheRequest 。前三者就是常规性的格式转换 Request ,可以分别将请求到的原始数据转换为字符串、Json对象或 Bitmap ,以 StringRequest 为例,它的parseNetworkResponse()
的实现以下:
protectedResponse<String>parseNetworkResponse(NetworkResponseresponse) { Stringparsed; try{ parsed=newString(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch(UnsupportedEncodingExceptione) { // Since minSdkVersion = 8, we can't call // new String(response.data, Charset.defaultCharset()) // So suppress the warning instead. parsed=newString(response.data); } returnResponse.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); }复制代码
而最后一个是功能性 Request ,它的做用就是清空缓存。
publicbooleanisCanceled() { // This is a little bit of a hack, but hey, why not. mCache.clear(); if(mCallback!=null) { Handlerhandler=newHandler(Looper.getMainLooper()); handler.postAtFrontOfQueue(mCallback); } returntrue; }复制代码
在必定会调用的方法isCanceled()
中调用了 Cache 的clear()
方法,实现清空缓存,但大部分时候并不须要这么麻烦,直接调用 Cache 的方法便可,考虑过是不是为了使对 Cache 的操做都处于一个线程中,可是在 Volley 中,默认的 BasicDiskCache 是线程安全的,而对其的操做也是分布在 CacheDispatcher 和 NetwrokDispatcher 中的(它们分别在不一样的线程),因此抛去了这种考虑,那就实在想不出 ClearCacheRequest 的存在有什么特殊的意义了。
Response 对应的类在 Volley 中共有两个,分别是 NetworkResponse 和 Response<T>,前者对应着网络请求获得的实际响应,调用Network.performRequest()
以后获得,而后再由Request.parseNetworkResponse()
对其进行格式转换,通常须要与 Request 结合使用,每个自定义的 Request 用于解析一种数据类型,如 StringRequest 对应 String 类型,一样,与之对应的 Response 返回的类型也是 String ,两者在 StringRequest 的构造方法中统一:
publicStringRequest(
intmethod,
Stringurl,
Listener<String>listener,
@NullableErrorListenererrorListener);复制代码
此处的 Listener 就是用户设置的监听器,当请求完成以后,会回调Listener.onResponse()
方法,将结果传递给调用方,且此时已通过 ResponseDelivery 处理,会在用户设置的线程中回调。
Volley 的轻便性,我认为体如今这几个方面。
首先,Volley 为网络照片的展现提供了十分便利的方式——一个自定义的 NetworkImageView ,为了实现这个 ImageView ,它还提供了相关的请求类,如 ImageLoader 、ImageRequest 等,利用这三个类,咱们能够比较轻松的完成普通的网络图片的加载,固然,它本质上来讲只是一个网络请求库,没法提供像 Glide 那样丰富的图片加载功能,可是 Volley 胜在简单方便。
其次,Volley 的 Request 这个特色让咱们处理某种特定类型的数据变得十分简单,当咱们实现了一个 XXXRequest ,它可以十分方便的工做,就像它 Request 本来就是为此工做的同样。
最后就是,Volley 这个库自己的实现很简单,总共不过40多个文件,却构成了一个完整的网络请求库,不只如此,它也没有拉下该有的功能,当咱们想快速实现某个功能的时候,它给全部组件都提供了默认的实现,当咱们想细化某一功能的时候,咱们也可使用自定义的组件实现某个功能。
另外一方便,对于一个想了解 Volley 内部实现过程的开发者来讲也是十分友好的,Volley 有比较少的类,同时条理也比较清晰,在 Volley 中没有过多的设计。
对于 Volley 的可扩展性,及用户的自定义上来讲,能够从如下几个接口一窥究竟。
首先,Cache 是一个接口,定义了诸如get()
、put()
等方法,这个类 Volley 基于文件读写实现了一个默认的 BasicDiskCache 类。Cache 会在 RequestQueue 的构造方法中传进系统,并在以后传递给 CacheDispatcher 和 NetworkDispatcher ,因此,当这个默认的缓存不知足须要时,咱们彻底能够自定义一个缓存类替换掉这个默认的,如实现一个内存缓存类,或者更有甚者,咱们能够在get()
方法中截断某些 Request 的网络请求,返回一个由 Cache 本身生成的响应结果。
Network 只有一个方法,即根据 Request 进行网络请求,获得 NetworkResponse ,这个类的实例也是在 RequestQueue 的构造方法中传入,与 Cache 同样,Network 有本身的实现,可是咱们一样可使用自定义的 Network 完成网络的加载,如在某些状况下可能要对 Request 进行额外的处理以后再进行网络请求,如给全部 Request 加一个请求头字段,即可以在自定义类的performRequest()
方法中对 Request 进行处理,不只如此,若是以为 Volley 提供的默认的网络请求功能有效率或者其余方面的问题,还能够在这个方法中使用其余的库进行加载,好比能够在里面再套一层 Okhttp ,可是这样使用的问题就是要先把 Volley 的 Request 类转换成 Okhttp 的 Request 类,并将 Okhttp 的 Response 再转回 NetworkResponse 。
NetworkDispatcher 的数量,即同时在 Volley 中进行网络请求的线程数量,也就是简化的线程池,这样即可以充分利用多线程的优点,更快地完成网络请求。用户能够设置不一样的数量以知足不一样的需求,可是 threadPoolSize 的最小值是 1,也就是说任什么时候候,Volley 中都至少有两个线程在工做(还有一个 CacheDispatcher 所在的线程)。
这是一个接口,拥有三个方法,定义以下:
/** Parses a response from the network or cache and delivers it. */ voidpostResponse(Request<?>request, Response<?>response); /** * Parses a response from the network or cache and delivers it. The provided Runnable will be * executed after delivery. */ voidpostResponse(Request<?>request, Response<?>response, Runnablerunnable); /** Posts an error for the given request. */ voidpostError(Request<?>request, VolleyErrorerror);复制代码
Request 是一个抽象类,这也就意味着它提供了必定的扩展功能,这在上文也提到过,用户能够自定义本身的 Request 用于将原始数据解析成特定的格式。大部分时候,咱们调用的 API 都返回某种格式的 json 数据,在网络请求完成以后咱们每每会先调用一个方法解析数据,而在使用 Volley 的时候,就不须要再有这额外的操做了,直接定义一个 Request ,重写它的parseNetworkResponse()
方法,那么返回的 Response 数据便会是咱们须要的格式。
按照这个逻辑,用户能够轻松地替换掉默认的 ResponseDelivery ,本身实现一个可以切换到更多线程的功能,如 RxJava 中,能够切换到所在线程、主线程或者是其余的子线程等。
这些方法会在 CacheDispatcher 和 NetworkDispatcher 获得结果以后调用,调用所在的线程是它们所在的子线程,因此为了知足回到主线程工做的须要,ResponseDelivery 的默认实现 ExecutorDelivery 所作的事情就是将线程切换到主线程,利用的是一个主线程的 Handler ,向主线程提交了一个 Runnable ,在这个 Runnable 中,Request 会调用本身的deliverXXX()
方法将结果回调给本身的 Listener ,完成整个网络请求。
Volley 中除了以上的这些以外,还有一个比较独特的地方,即 HttpStack 和 BaseHttpStack 这几个执行实际网络请求的类上面。下面一一列出:
HttpStack
接口,声明了一个网络请求方法performRequest()
,它的返回参数是 org.apache.http.HttpResponse,已被标注弃用。
BaseHttpStack
抽象类,实现了 HttpStack 接口,并增长了一个新的方法executeRequest()
,它的返回参数是 HttpResponse 。
它实现了 HttpStack 的performRequest()
方法,可是在这个方法内部实际是调用executeRequest()
方法获取到了 HttpResponse 实例,而后再将其转换为 org.apache.http.HttpResponse 返回。
AdapterHttpStack
继承自 BaseHttpStack ,实例化时须要一个 HttpStack 实例,而后在实现executeRequest()
方法的时候使用了performRequest()
方法获得 org.apache.http.HttpResponse 实例,而后将其转换为 HttpResponse 返回。
HurlStack
继承自 BaseHttpStack ,使用 HttpUrlConnection 实现了executeRequest()
方法。
HttpClientStack
实现了 HttpStack 接口,使用 HttpClient 实现了performRequest()
方法,已被标注弃用。
通览整个 HttpStack 家族的五个类,你是否明白了它们的工做原理?首先从两个实现类 HurlStack 和 HttpClientStack 提及,它们分别使用了 HttpUrlConnection 和 HtttClient 实现,这两个类都是 Android 原生提供的网络请求类,可是它们适用于不一样的 Android 版本,在 5.1 版本以后,HttpClient 便被 Google 弃用,并推荐使用 HttpUrlConnection 。因此 Volley 在使用这两个类的时候也作了版本上的兼容。
最开始 Volley 仅提供了一个 HttpStack ,这个类默认就是使用 HttpClient 进行网络请求,可是在以后的版本中 HttpClient 被弃用,因此又出现了 BaseHttpStack ,它提供了一个不须要依赖 HttpClient 的方法executeRequest()
,可是它并不能避免用户继续调用performRequest()
方法,因此重写了这个方法在内部调用executeRequest()
,可是还有一个问题,若是用户一直使用的都是自定义的 HttpStack 类,那在从新使用 BaseHttpStack 的时候必然还要从新实现一遍,因此,又有了 AdapterHttpStack 的存在,它继承自 BaseHttpStack ,构造方法中传入了 HttpStack 实例,可是与 BaseHttpStack 相反,它内部的逻辑是exetureRequest()
的实现依赖于performRequest()
的实现,用户能够将本身本来实现了 HttpStack 的类实例做为参数传递给 AdapterHttpStack ,即可以获得一个可工做的 BaseHttpStack 实例了。