转载请注明出处:http://blog.csdn.net/crazy1235/article/details/71340320
一般使用Volley的步骤是:
创建请求队列 requestQueue = Volley.newRequestQueue(this); 创建一个请求 StringRequest stringRequest = new StringRequest(Request.Method.GET, Constants.URL_WEATHER_INFO, new Response.Listener<String>() { @Override public void onResponse(String response) { Log.d("MainActivity", response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.d("MainActivity", "error"); } }); 将请求添加到请求队列中 requestQueue.add(stringRequest);在回调函数中得到请求结果
Activity/Fragment销毁时,取消请求
requestQueue.cancelAll(Constants.URL_WEATHER_INFO);从第一步开始看:
Volley.newRequestQueue(this);关于RequestQueue(请求队列)的创建,关键代码就在 Volley.java 中!
public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null); } public static RequestQueue newRequestQueue(Context context, HttpStack stack) { 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) { } // 1. 创建HttpStack对象 if (stack == null) { if (Build.VERSION.SDK_INT >= 9) { // 1. Android 2.3版本及以上 stack = new HurlStack(); } else { // 2. Android 2.3版本之前,HttpUrlConection 不可用 // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } // 2. 创建Network对象 (发起网路请求) Network network = new BasicNetwork(stack); // 3. 创建请求队列 RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); // 4. 队列启动 queue.start(); return queue; }HttpStack 是一个接口,只有一个函数,表示处理网络请求并返回处理结果
public interface HttpStack { public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError; }它有两个子类 HurlStack & HttpClientStack
Network 也是一个接口,有一个唯一实现子类 BasicNetwork
Network 的作用就是调用HttpStack处理网络请求,并将网络请求结果封装成 NetworkResponse。
下面来看RequestQueue的初始化!
最终都是调用的四个参数的构造,创建了一个长度是4的NetworkDispatcher数组 !
NetworkDispatcher 表示网络分发器,启动之后,不断的从请求队列中取出请求,处理结果交给ResponseDelivery进行分发 (根据请求的情况,成功/失败/异常);如果队列为空,则等待!
关于NetworkDispatcher中的具体操作后面再讲!
ResponseDelivery 是网络请求结果投递接口!有一个实现类 ExecutorDelivery
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); } }; }从ExecutorDelivery的构造看出Executor的所有执行都交给handler来处理!
而传入的handler是主线程的handler对象,所以网络请求的结果都投递给主线程来处理了!
看到这里,RequestQueue 对象创建完毕,接着就是调用 start() 函数!
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(); } } public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } }首先,调用stop()函数停止正在运行的所有线程!
接着,创建了一个 CacheDispatcher 和 n个 NetworkDispatcher ,并全部启动!
RequestQueue 内部有两个 基于优先级的阻塞队列 :
private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>(); private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();当没有任何Request请求添加进入队列时,CacheDispatcher 和 NetworkDispatcher 虽然已经启动,但是会“空转”,也就是阻塞等待队列中有值!
还有一个正在请求但是未完成 的请求集合!
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();还有另外一个等待请求的集合!
private final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();OK, 这时Volley请求已经准备完毕,来看看添加一个请求会发生什么!
在这里,一个请求的处理分为三种情况:
如果不允许被缓存,则直接添加到 mNetworkQueue 这个队列中,在NetworkDispatcher 中进行处理!
如果允许被缓存,则进一步判断是否已经有相同的url请求正在进行,如果有,则将当前请求添加到url对应的链表中!(等待正在处理的那个结果回来之后,然后通知所有相同url的请求)
如果没有相同的url正在处理,则添加一条新的记录到 mWaitingRequests 中,并将请求添加到 mCacheQueue 中 ,由CacheDispatcher 进行处理!
处理流程如下:
所以现在只需关注 NetworkDispatcher 和 CacheDispatcher 对 Request 的处理!
在构造RequestQueue的时候,创建了一个 CacheDispatcher 对象,并启动该线程!
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start();在构造CacheDispatcher的时候,传进来的参数有:
mCacheQueue – 缓存队列
mNetworkQueue – 网络请求队列
mCache – 缓存对象
mDelivery – 结果分发器
@Override public void run() { if (DEBUG) VolleyLog.v("start new dispatcher"); // 设置线程优先级 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // 1. 初始化缓存 mCache.initialize(); // 循环 while (true) { try { // 2. 从缓存阻塞队列中取request,如果没有的话,会一直阻塞,直到能take到一条请求 final Request<?> request = mCacheQueue.take(); request.addMarker("cache-queue-take"); // 3. 如果request被取消了,则不在处理 if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } // 4. 尝试从缓存中获取 Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { request.addMarker("cache-miss"); // 5. 缓存没有命中,则将请求放入网路请求队列中 mNetworkQueue.put(request); continue; } // 6. 如果缓存到期,则添加到网路请求队列中 if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } // 7. 此时有缓存命中,将data数据交付给request request.addMarker("cache-hit"); Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed"); // 8. 判断是否需要刷新数据 if (!entry.refreshNeeded()) { // 完全命中cache,直接将结果交付request mDelivery.postResponse(request, response); } else { // Soft-expired 缓存命中,此时可以交付。但是同时需要再次请求以便刷新数据 request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; // 9. 投递缓存的结果,并将请求添加到mNetworkQueue中以便刷新数据 mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } } }处理流程图:
首先是缓存的初始化,也就是将缓存的信息读取出来方便匹配request!
对于一个request,我们上面再说 add() 函数的时候提到了三种情况,一般情况下一个request是第三种情况,也就是既添加到缓存队列中,又添加到等待请求队列中!
从上面的流程中可以看出,先把请求从缓存中去匹配,如果匹配到了(不管过期的情况)则通过 ResponseDelivery 投递结果;如果匹配失败再去添加到 mNetworkQueue (网络请求队列)中交给 NetworkDispatcher 去处理!
关于mCache 下面来说!
CacheDispatcher 的处理离不开 Cache!
Cache 是一个接口
主要方法有:
/** * 通过key得到缓存的实体对象 */ public Entry get(String key); /** * 保存一个请求的实体对象 */ public void put(String key, Entry entry); /** * 初始化 */ public void initialize(); /** * 根据key移除一个缓存实体 */ public void remove(String key); /** * 清除缓存 */ public void clear();还有一个静态内部类 Entry
public static class Entry { /** * 请求返回的数据 */ public byte[] data; /** * Http响应头里面的实体标签 */ public String etag; /** * Http的响应时间 */ public long serverDate; /** * 缓存的过期时间 */ public long ttl; /** * 缓存的刷新周期 */ public long softTtl; /** * http的相应头部 */ public Map<String, String> responseHeaders = Collections.emptyMap(); /** * 判断是否到期 */ public boolean isExpired() { return this.ttl < System.currentTimeMillis(); } /** * 判断是否需要刷新数据 */ public boolean refreshNeeded() { return this.softTtl < System.currentTimeMillis(); } }有两个实现子类,NoCache就是空实现;DiskBasedCache 就是在Volley.java 初始化RequestQueue的时候传进去的mCache对象!
DiskBasedCache定义了一个 CacheHeader 类,与Cache.Entry类似,表示缓存信息的摘要!
网络请求的缓存写入与读取都是通过CacheHeadler类来操作的!
在 DiskBasedCache 类中定义了一些变量:
/** 缓存CacheHeader的map集合 */ private final Map<String, CacheHeader> mEntries = new LinkedHashMap<String, CacheHeader>(16, .75f, true); /** 缓存容量的总大小 */ private long mTotalSize = 0; /** 缓存的根目录 */ private final File mRootDirectory; /** 当前缓存的总大小 */ private final int mMaxCacheSizeInBytes; /** 默认缓存最大容量 */ private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024; /** 缓存大小的阈值 */ private static final float HYSTERESIS_FACTOR = 0.9f; /** magic number */ private static final int CACHE_MAGIC = 0x20120504;从上面两个函数看出,初始化时,遍历读取缓存目录下的所有缓存文件,组装成一个个的CacheHeader对象! 并且保存到map集合中!
关于文件流的读取,Volley定义了一套加密规则!具体规则就不研究了,但是它保证了写入之后在读取时获取到的就是原值!
public static CacheHeader readHeader(InputStream is) throws IOException { CacheHeader entry = new CacheHeader(); int magic = readInt(is); if (magic != CACHE_MAGIC) { // 判断magic throw new IOException(); } entry.key = readString(is); entry.etag = readString(is); if (entry.etag.equals("")) { entry.etag = null; } entry.serverDate = readLong(is); entry.ttl = readLong(is); entry.softTtl = readLong(is); entry.responseHeaders = readStringStringMap(is); return entry; }从上面代码看出,删除文件时总是从map的第一位开始的,当不超过默认总缓存容量阈值的大小时才会停止删除缓存文件!
当一个请求需要被缓存分发器处理以及缓存的相关操作已经介绍完毕,下面来看网络请求分发器!
每一个 NetworkDispatcher 对象都是一个线程!来看run()函数!
@Override public void run() { // 设置线程优先级 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Request<?> request; // 死循环 while (true) { try { // 1. 从请求队列中取出一个请求 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"); // 2. 如果请求被取消,则不在处理 if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } // 3. 添加流量统计 addTrafficStatsTag(request); // 4. 访问网络得到数据组装成NetworkResponse NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // 5. 判断服务器是否返回304,如果是则直接读取缓存即可 if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } // 6. 在工作线程上解析 networkResponse Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // 7. 将请求结果写入缓存 if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } request.markDelivered(); // 8. 投递处理结果 mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); mDelivery.postError(request, new VolleyError(e)); } } }处理流程图:
注释写的比较详细了,重点来看下面一行代码:
NetworkResponse networkResponse = mNetwork.performRequest(request); // ... Response<?> response = request.parseNetworkResponse(networkResponse);mNetwork是从Volley类中构造RequestQueue传进来的 BasicNetwork !
而BasicNetwork的构造根据系统的版本传入HurlStack 对象 或者 HttpClientStack 对象!
先来看BasicNetwork是在怎么处理 Request请求的!
不管是那种类型,最后请求的结果都被分装成了 HttpResponse 对象。 HttpResponse对象是 apache包中的类
从上面代码看出,当出现socket连接异常,连接超时异常,IO异常时都会尝试重新请求数据!
其实 performRequest 中是个while(true)循环,当正常返回结果是就跳出了该循环;否则在异常情况下重试还是在循环里面再次请求而已!
下面来看两种HttpStack具体怎么处理得到HttpResponse的!
HrulStack是一个接口,只有一个函数 performRequest()
HurlStack 是通过 HttpURLConnection 方式访问网络
@Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = request.getUrl(); HashMap<String, String> map = new HashMap<String, String>(); map.putAll(request.getHeaders()); // 我们通常在这个函数中添加header !!! map.putAll(additionalHeaders); if (mUrlRewriter != null) { String rewritten = mUrlRewriter.rewriteUrl(url); if (rewritten == null) { throw new IOException("URL blocked by rewriter: " + url); } url = rewritten; } // 1. 构造HttpURLConnection对象 URL parsedUrl = new URL(url); HttpURLConnection connection = openConnection(parsedUrl, request); // 2. 添加header信息 for (String headerName : map.keySet()) { connection.addRequestProperty(headerName, map.get(headerName)); } // 3. 设置参数 setConnectionParametersForRequest(connection, request); // 4. 判断响应码 ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); int responseCode = connection.getResponseCode(); if (responseCode == -1) { // -1 is returned by getResponseCode() if the response code could not be retrieved. // Signal to the caller that something was wrong with the connection. throw new IOException("Could not retrieve response code from HttpUrlConnection."); } // 5. 封装成 BasicHttpResponse 对象 StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); response.setEntity(entityFromConnection(connection)); for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); response.addHeader(h); } } return response; } static void setConnectionParametersForRequest(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { switch (request.getMethod()) { case Method.DEPRECATED_GET_OR_POST: // 当请求的postbody不为空时使用POST方式,否则使用GET方式 byte[] postBody = request.getPostBody(); if (postBody != null) { connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(postBody); out.close(); } break; case Method.GET: connection.setRequestMethod("GET"); break; case Method.DELETE: connection.setRequestMethod("DELETE"); break; case Method.POST: connection.setRequestMethod("POST"); addBodyIfExists(connection, request); // 添加body参数信息 break; case Method.PUT: connection.setRequestMethod("PUT"); addBodyIfExists(connection, request);// 添加body参数信息 break; case Method.HEAD: connection.setRequestMethod("HEAD"); break; case Method.OPTIONS: connection.setRequestMethod("OPTIONS"); break; case Method.TRACE: connection.setRequestMethod("TRACE"); break; case Method.PATCH: addBodyIfExists(connection, request); connection.setRequestMethod("PATCH"); break; default: throw new IllegalStateException("Unknown method type."); } } private static void addBodyIfExists(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { byte[] body = request.getBody(); if (body != null) { connection.setDoOutput(true); connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(body); out.close(); } } public byte[] getBody() throws AuthFailureError { Map<String, String> params = getParams(); // if (params != null && params.size() > 0) { return encodeParameters(params, getParamsEncoding()); } return null; }从 getBody() 可以看出,回调的就是 getParams() 函数!也就是我们对request设置参数的函数!
从代码看出HttpClientStack的performRequest()函数相对简单!
再回过头来分析,在BasicNetwork类中的performRequest()函数中,通过mHttpStack.performRequest(request, headers) 得到了HttpResponse对象,然后组装成 NetworkResponse 对象返回结果!
再回到NetworkDispatcher中, 看下面的代码:
Response<?> response = request.parseNetworkResponse(networkResponse);Request是个抽象类!不同的子类parseNetworkResponse()中的处理不尽相同!
下面来看Rquest及其子类的相关处理!
Request有很多个子类:
这里只简单分析StringRequest!
在NetworkDipatcher中
Response<?> response = request.parseNetworkResponse(networkResponse);如果request是StringRequest对象,则调用该类的parseNetworkResponse()函数!
@Override protected Response<String> parseNetworkResponse(NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); }通过 HttpHeaderParser.parseCacheHeaders() 函数将response 解析Cache.Entry对象!
最后封装成 Response 对象!
StringRequest中还重写了父类的deliverResponse()函数,函数内部就是直接调用回调函数的onResponse()方法相应!
@Override protected void deliverResponse(String response) { mListener.onResponse(response); }分析到这,就差一步了。就是对结果进行分发处理,不管是成功的还是失败的结果!
mDelivery.postResponse(request, response); mDelivery.postError(request, new VolleyError(e)); mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch (InterruptedException e) { // Not much we can do about this. } } });ResponseDelivery 是个接口:
只有一个实现子类!
那么这个子类什么时候创建的呢?
实际上,上面已经提到过,在构造RequestQueue的时候:
public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } @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)); }从这三个方法可以看出,不管成功还是失败的处理都调用了 mResponsePoster.execute 。mResponsePoster是个Executor对象!
它执行的对象都交给了主线程的handler进行处理!
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() { // 1. 判断请求是否被取消,如果被取消则不进行事件处理 if (mRequest.isCanceled()) { mRequest.finish("canceled-at-delivery"); return; } // 2. 判断相应是成功还是失败 if (mResponse.isSuccess()) { mRequest.deliverResponse(mResponse.result); } else { mRequest.deliverError(mResponse.error); } // 如果不是立即返回的,(表示从网络读取的)会调用finish()函数结束这个request if (mResponse.intermediate) { mRequest.addMarker("intermediate-response"); } else { mRequest.finish("done"); } // 执行postRun,(如果有) if (mRunnable != null) { mRunnable.run(); } } }OK,这里很明显就是回调了我们设置的成功或者失败的函数!!!
至此,从Volley初始化,到添加一个求Request,到处理一个请求(本地、网络),再到结果处理及分发,最后在主线程接口回调,这一系列过程就分析完毕了!!!
首先是,请求队列的创建,其中根据系统版本创建不同的 HttpStack 对象。接着即是启动队列!
启动队列就是创建一个CacheDispatcher和4个NetworkDispatcher,并全部启动!
然后就可以添加一个request请求进行处理了
添加一个请求时,首先判断是否可以被缓存,不能直接添加到网络请求队列中由NetworkDispatcher处理;如果可以则判断缓存中是否有或者是否已经过期或者需要刷新,进而由NetworkDispatcher 或者CacheDispatcher 处理!
不论是CacheDispatcher还是NetworkDispatcher都讲结果封装成Response 对象!进而由 ResponseDelivery 分发处理!
最后由 ResponseDelivery 回调成功或者失败函数返回结果!
附:
HTTP请求结果状态码!
/** * An <code>int</code> representing the three digit HTTP Status-Code. * <ul> * <li> 1xx: Informational * <li> 2xx: Success * <li> 3xx: Redirection * <li> 4xx: Client Error * <li> 5xx: Server Error * </ul> */ protected int responseCode = -1;