Volley框架使用及源码解析
1. Volley特点
(1) 特别适合数据量小,通信频繁的网络操作。
(2) 扩展性强。Volley 中大多是基于接口的设计,可根据需要自行定制。
(3) 一定程度符合 Http 规范,包括返回 ResponseCode(2xx、3xx、4xx、5xx)的处理,请求头的处理, 缓存机制的支持等。并支持重试及优先级定义。
(4) 提供简便的图片加载工具
GitHub地址:https//github.com/mcxiaoke/android-volley
2. 概念介绍
Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest 都是它的子类,表示某种类型的请求。我们还可以继承于它,实现自定义的Request。
RequestQueue:表示请求队列,里面包含一个CacheDispatcher(用于处理缓存请求的线程)、NetworkDispatcher数组(用于处理网络请求的调度线程,默认长度为4),一个ResponseDelivery(返回结果分发)。通过start() 函数启动时会启动CacheDispatcher和NetworkDispatcher。
CacheDispatcher:一个线程,用于调度处理缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。
NetworkDispatcher:一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
ResponseDelivery:返回结果分发接口,目前只有基于ExecutorDelivery的在传入的handler对应线程内进行分发。
HttpStack:处理 Http 请求,返回请求结果。目前 Volley 中有基于HttpURLConnection 的HurlStack和 基于 Apache HttpClient 的HttpClientStack。
Network:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
Cache:缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。NetworkDispatcher得到请求结果后判断是否需要存储在 Cache,CacheDispatcher会从 Cache 中取缓存结果。
3. 使用示例
(1) 请求json
RequestQueue mQueue = Volley.newRequestQueue(context);private void getJsonData() {String url=”http://172.17.202.36:8000/data.jason”; JsonObjectRequest jsonRequest = new JsonObjectRequest(url,new Response.Listener<JSONObject>() {@Overridepublic void onResponse(JSONObject response) {Log.d(LOG_TAG, response.toString());}}, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {Log.e(LOG_TAG, error.getMessage(), error);} }); mQueue.add(jsonRequest); }
(2) 请求图片
private RequestQueue mQueue = Volley.newRequestQueue(mContext); private ImageLoader mImageLoader = new ImageLoader(mQueue, new BitmapCache());public class BitmapCache implements ImageLoader.ImageCache {private LruCache<String, Bitmap> cache;public BitmapCache() {cache = new LruCache<String, Bitmap>(8 * 1024 * 1024) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getRowBytes() * bitmap.getHeight();}};}@Overridepublic Bitmap getBitmap(String url) {return cache.get(url);}@Overridepublic void putBitmap(String url, Bitmap bitmap) {cache.put(url, bitmap);} }private void loadImage() { String imageUrl = “http://172.17.202.36:8000/images/文字_0.png”; ImageView iv = (ImageView)findViewById(R.id.iv); ImageLoader.ImageListener listener = ImageLoader.getImageListener(iv, R.drawable.default_png, R.drawable.error_png); mImageLoader.get(imageUrl, listener); }
4. 源码解析
(1)从使用方式上可以看出,会先调用Volley.newRequestQueue(this)获取到一个RequestQueue,看下newRequestQueue的内容。
public static RequestQueue newRequestQueue(Context context) {return newRequestQueue(context, null); }public static RequestQueue newRequestQueue(Context context, HttpStack stack) {return newRequestQueue(context, stack, -1); } public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) { return newRequestQueue(context, null, maxDiskCacheBytes); } /** Default on-disk cache directory. */ private static final String DEFAULT_CACHE_DIR = "volley"; public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) { File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } if (stack == null) { if (Build.VERSION.SDK_INT >= 9) { // API-level >= 9 stack = new HurlStack(); } else { // Prior to Gingerbread, HttpUrlConnection was unreliable. stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } Network network = new BasicNetwork(stack); RequestQueue queue; if (maxDiskCacheBytes <= -1) { // No maximum size specified queue = new RequestQueue(new DiskBasedCache(cacheDir), network); } else { // Disk cache size specified queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network); } queue.start(); return queue; }
RequestQueue的构造
/** Number of network request dispatcher threads to start. */ private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; public RequestQueue(Cache cache, Network network) { this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); } public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; }
mCache : 基于DiskBasedCache的Cache对象。
mNetwork: 基于BasicNetwork的Network对象。
mDispatchers: 网络请求线程数组,默认大小为4。
mDelivery: 基于ExecutorDelivery的ResponseDelivery对象,可以看出它Handler的
线程为主线程。
(2)queue.start()
/** * Starts the dispatchers in this queue. */ public void start() { stop(); // Make sure any currently running dispatchers are stopped. // Create the cache dispatcher and start it. mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
先quit掉之前的线程,然后创建并启动一个CacheDispatcher线程和4个NetworkDispatcher线程。
(3)NetworkDispatcher
public class NetworkDispatcher extends Thread{ ... public NetworkDispatcher(BlockingQueue<Request<?>> queue, Network network, Cache cache, ResponseDelivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; mDelivery = delivery; } @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Request<?> request; while (true) { long startTimeMs = SystemClock.elapsedRealtime(); // release previous request object to avoid leaking request object when mQueue is drained. request = null; try { // Take a request from the queue. request = mQueue.take(); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); // If the request was cancelled already, do not perform the network request. if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } addTrafficStatsTag(request); // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // If the server returned 304 AND we delivered a response already, we're done -- don't deliver a second identical response. if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } // Parse the response here on the worker thread. Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } // Post the response back. request.markDelivered(); mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { ...... } } } ...... }
线程的run()方法不断从mNetworkQueue中取出request, 然后调用mNetwork.performRequest(request)进行网络请求,实际执行的是BasicNetwork.performRequest()函数。在获取NetworkResponse后,执行request.parseNetWorkResponse, 由request进行解析,并返回一个Response<?>对象。最后执行mDelivery.postResponse进行结果分发。
从这可以看到,自定义的Request必须重写parseNetWorkResponse()这个函数.
(3) BasicNetwork.performRequest()的实现
public class BasicNetwork implements Network { ...... @Override public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while (true) { HttpResponse httpResponse = null; byte[] responseContents = null; Map<String, String> responseHeaders = Collections.emptyMap(); try { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); addCacheHeaders(headers, request.getCacheEntry()); httpResponse = mHttpStack.performRequest(request, headers); StatusLine statusLine = httpResponse.getStatusLine(); int statusCode = statusLine.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders()); // Handle cache validation. if (statusCode == HttpStatus.SC_NOT_MODIFIED) { Entry entry = request.getCacheEntry(); if (entry == null) { return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } entry.responseHeaders.putAll(responseHeaders); return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } // Handle moved resources if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { String newUrl = responseHeaders.get("Location"); request.setRedirectUrl(newUrl); } // Some responses such as 204s do not have content. We must check. if (httpResponse.getEntity() != null) { responseContents = entityToBytes(httpResponse.getEntity()); } else { // Add 0 byte response as a way of honestly representing a no-content request. responseContents = new byte[0]; } // if the request is slow, log it. long requestLifetime = SystemClock.elapsedRealtime() - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine); if (statusCode < 200 || statusCode > 299) { throw new IOException(); } return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); } catch (SocketTimeoutException e) { attemptRetryOnException("socket", request, new TimeoutError()); } catch (ConnectTimeoutException e) { attemptRetryOnException("connection", request, new TimeoutError()); } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { throw new NoConnectionError(e); } if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl()); } else { VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); } if (responseContents != null) { networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { attemptRetryOnException("redirect", request, new RedirectError(networkResponse)); } else { // TODO: Only throw ServerError for 5xx status codes. throw new ServerError(networkResponse); } } else { throw new NetworkError(e); } } } } ...... }
关键的地方是通过HttpStack.performRequest()获取数据,然后根据各种情况创建返回不同的NetworkResponse对象。
(4) CacheDispatcher
public class CacheDispatcher extends Thread { private static final boolean DEBUG = VolleyLog.DEBUG; /** The queue of requests coming in for triage. */ private final BlockingQueue<Request<?>> mCacheQueue; /** The queue of requests going out to the network. */ private final BlockingQueue<Request<?>> mNetworkQueue; /** The cache to read from. */ private final Cache mCache; /** For posting responses. */ private final ResponseDelivery mDelivery; /** Used for telling us to die. */ private volatile boolean mQuit = false; public CacheDispatcher(BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue, Cache cache, ResponseDelivery delivery) { mCacheQueue = cacheQueue; mNetworkQueue = networkQueue; mCache = cache; mDelivery = delivery; } public void quit() { mQuit = true; interrupt(); } @Override public void run() { if (DEBUG) VolleyLog.v("start new dispatcher"); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // Make a blocking call to initialize the cache. mCache.initialize(); Request<?> request; while (true) { // release previous request object to avoid leaking request object when mQueue is drained. request = null; try { // Take a request from the queue. request = mCacheQueue.take(); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { request.addMarker("cache-queue-take"); // If the request has been canceled, don't bother dispatching it. if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } // Attempt to retrieve this item from cache. Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { request.addMarker("cache-miss"); // Cache miss; send off to the network dispatcher. mNetworkQueue.put(request); continue; } // If it is completely expired, just send it to the network. if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } // We have a cache hit; parse its data for delivery back to the request. request.addMarker("cache-hit"); Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed"); if (!entry.refreshNeeded()) { // Completely unexpired cache hit. Just deliver the response. mDelivery.postResponse(request, response); } else { // Soft-expired cache hit. We can deliver the cached response, but we need to also send the request to the network for refreshing. request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; // Post the intermediate response back to the user and have the delivery then forward the request along to the network. final Request<?> finalRequest = request; mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(finalRequest); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); } } } }
线程的run()方法不断从mCacheQueue中取出request, 然后尝试从mCache中查找request中key(url)对应的entry. 如果entry为空或者过期,那么直接插入mNetWorkQueue中,由NetworkDispatcher去请求获取数据。如果entry不为空也没过期,那么说明可以从mCache中直接获取,然后进行分发。
(5) ExecutorDelivery
在创建RequestQueue的时候,new ExecutorDelivery(new Handler(Looper.getMainLooper()))作为ResponseDelivery实例。看下ExecutorDelivery的实现。
public class ExecutorDelivery implements ResponseDelivery { /** Used for posting responses, typically to the main thread. */ private final Executor mResponsePoster; /** * Creates a new response delivery interface. * @param handler {@link Handler} to post responses on */ public ExecutorDelivery(final Handler handler) { // Make an Executor that just wraps the handler. mResponsePoster = new Executor() { @Override public void execute(Runnable command) { handler.post(command); } }; } /** * Creates a new response delivery interface, mockable version * for testing. * @param executor For running delivery tasks */ public ExecutorDelivery(Executor executor) { mResponsePoster = executor; } @Override public void postResponse(Request<?> request, Response<?> response) { postResponse(request, response, null); } @Override public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { request.markDelivered(); request.addMarker("post-response"); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); } @Override public void postError(Request<?> request, VolleyError error) { request.addMarker("post-error"); Response<?> response = Response.error(error); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); } /** * A Runnable used for delivering network responses to a listener on the main thread. */ @SuppressWarnings("rawtypes") private class ResponseDeliveryRunnable implements Runnable { private final Request mRequest; private final Response mResponse; private final Runnable mRunnable; public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) { mRequest = request; mResponse = response; mRunnable = runnable; } @SuppressWarnings("unchecked") @Override public void run() { // If this request has canceled, finish it and don't deliver. if (mRequest.isCanceled()) { mRequest.finish("canceled-at-delivery"); return; } // 分发核心代码块 if (mResponse.isSuccess()) { mRequest.deliverResponse(mResponse.result); } else { mRequest.deliverError(mResponse.error); } // If this is an intermediate response, add a marker, otherwise we're done // and the request can be finished. if (mResponse.intermediate) { mRequest.addMarker("intermediate-response"); } else { mRequest.finish("done"); } // If we have been provided a post-delivery runnable, run it. if (mRunnable != null) { mRunnable.run(); } } } }
因为在构造的时候传入了主线程的looper, 所以分发是直接pose到主线程,可以直接更新UI。在ResponseDeliveryRunnable.run()中调用request.deliverResponse()分发response, 所以自定义的Request还必须重写deliverResponse()。
(5) RequestQueue.add(request)
/** * Adds a Request to the dispatch queue. * @param request The request to service * @return The passed-in request */ public <T> Request<T> add(Request<T> request) { // Tag the request as belonging to this queue and add it to the set of current requests. request.setRequestQueue(this); synchronized (mCurrentRequests) { mCurrentRequests.add(request); } // Process requests in the order they are added. request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue"); // If the request is uncacheable, skip the cache queue and go straight to the network. if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } // Insert request into stage if there's already a request with the same cache key in flight. synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) {// There is already a request in flight. Queue up. Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?>>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { // Insert 'null' queue for this cacheKey, indicating there is now a request in flight. mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request); } return request; } }
首先将request插入到mCurrentRequests中。如果request不应该缓存(默认为缓存,通过调用Request.setShouldCache(false)改为不缓存),直接插入到mNetworkQueue中。否则,判断mWaitingRequests中是否含有cacheKey(url), 如果包含,则插入到mWaitingRequests中,不再重复请求,在上一个请求返回时直接发送结果;如果不包含cacheKey(url),则插入mWaitingRequests,同时加入到mCacheQueue中。
转载于:https://www.cnblogs.com/Jackwen/p/5673983.html
Volley框架使用及源码解析相关推荐
- 轻量级Rpc框架设计--motan源码解析六:client端服务发现
一, Client端初始化工作 client端通过RefererConfigBean类实现InitializingBean接口的afterPropertiesSet方法, 进行下面三项检查配置工作: ...
- php的lumen框架,Lumen框架“服务容器”源码解析
1.服务容器 "服务容器"是Lumen框架整个系统功能调度配置的核心,它提供了整个框架运行过程中的一系列服务."服务容器"就是提供服务(服务可以理解为系统运行中 ...
- Android 网络框架之Retrofit源码解析,flutter边框特效
Retrofit的构建使用了建造者模式,这个模式的优点就是可以构造复杂的对象,方便扩展,并且看起来代码比较简洁,美观: 在开始之前,我们先来看一下Retrofit的成员变量: 这里的变量并不是很多,我 ...
- 若依框架Excel导出源码解析
入口 /*** 导出接收人列表*/@PostMapping("/export")@ResponseBodypublic AjaxResult export(Wxpusher wxp ...
- MyBatis3源码解析(8)MyBatis与Spring的结合
简介 在上几篇文章中,解析了MyBatis的核心原理部分,我们大致对其有了一定的了解,接下来我们看看在日常的开发中MyBatis是如何与Spring框架结合的 源码解析 在我们的日常开发中,使用Spr ...
- Volley 源码解析之网络请求
Volley源码分析三部曲 Volley 源码解析之网络请求 Volley 源码解析之图片请求 Volley 源码解析之缓存机制 Volley 是 Google 推出的一款网络通信框架,非常适合数据量 ...
- Android Glide图片加载框架(二)源码解析之with()
文章目录 一.前言 二.如何阅读源码 三.源码解析 1.with() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图 ...
- Android经典著名的百大框架源码解析(retrofit、Okhttp、Glide、Zxing、dagger等等)
我们Android程序员每天都要和源码打交道.经过数年的学习,大多数程序员可以"写"代码,或者至少是拷贝并修改代码.而且,我们教授编程的方式强调编写代码的艺术,而不是如何阅读代码. ...
- 源码解析-Volley(转自codeKK)
Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分 项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo 分析者:grumo ...
最新文章
- Nature子刊超越诺贝尔经典理论:神经科学研究路漫漫...
- Scrum项目1.0
- CString转换成char*
- RabbitMQ学习之消息可靠性及特性
- docker pull的镜像放在哪里_Docker 安装ELK及Docker常见命令
- hotelling变换_基于Hotelling-T²的偏最小二乘(PLS)中的变量选择
- mybatis 注解传入 list 集合​​​​​​​
- 机器学习实战之决策树
- 并查集图冲突hdu1272
- php excel导出pdf文件,如何修复“无法加载PDF呈现库”使用PHPExcel TCPDF将Excel导出为PDF...
- Gym 101246(ACM ICPC 2010-2011, NEERC, Southern Subregional Contest Russia, Saratov)
- 你知道CDN是什么吗?本文带你搞明白CDN
- Android利用WifiDirect实现文件传输功能
- ppt模板如何更换表格颜色?
- 投资银行业务过关必做1500题
- 谷歌浏览器如何重置?谷歌浏览器恢复默认设置?
- 上海出差之行--领略外滩美景、RT-Thread总部之旅、嵌友面基、返程记录
- 如何获取PDF修改权限并编辑文档?
- .net 服务器推送信息,.net websocket服务端开发,实现消息推送功能
- 面试官100%会问的接口测试的知识