通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文【安卓网络请求开源框架Volley源码解析系列】初识Volley及其基本用法。如StringRequest用来请求一段文本信息,JsonRequest(JsonObjectRequest、JsonArrayRequest)用于请求一条JSON格式的数据,ImageRequest则是用于请求网络上的图片资源。但是我们知道在网络上传输的轻量级数据还包括另外一种即XML。但是Volley中目前还没提供用于请求XML格式的类,那我们如果我们要请求的是一段XML格式数据该怎们办?

答案很简单,自己动手写,还记得在初识Volley的博客中讲到的Volley的基本特点之一吗:可扩展性强。Volley提供了非常强的扩展机制,使得我们可以很轻松地定制出任意类型的Request。

一定制自己的Request请求

定制XMLRequest:去扩展不是自己开发的框架中的某些功能不是一件很容易的事,因为你很可能不知道从哪个地方作为切入点,因此我们首先要做的就是把谷歌官方写的一些与自己要定制的类功能相似的类好好研究一下,看能否看懂然后自己模仿。那么自然首先想到去研究一下StringRequest类的源码,代码如下:

  1. /**
  2. * A canned request for retrieving the response body at a given URL as a String.
  3. */
  4. public class StringRequest extends Request<String> {
  5. private final Listener<String> mListener;
  6.  
  7. /**
  8. * Creates a new request with the given method.
  9. *
  10. * @param method the request {@link Method} to use
  11. * @param url URL to fetch the string at
  12. * @param listener Listener to receive the String response
  13. * @param errorListener Error listener, or null to ignore errors
  14. */
  15. public StringRequest(int method, String url, Listener<String> listener,
  16. ErrorListener errorListener) {
  17. super(method, url, errorListener);
  18. mListener = listener;
  19. }
  20.  
  21. /**
  22. * Creates a new GET request.
  23. *
  24. * @param url URL to fetch the string at
  25. * @param listener Listener to receive the String response
  26. * @param errorListener Error listener, or null to ignore errors
  27. */
  28. public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
  29. this(Method.GET, url, listener, errorListener);
  30. }
  31.  
  32. @Override
  33. protected void deliverResponse(String response) {
  34. mListener.onResponse(response);
  35. }
  36.  
  37. @Override
  38. protected Response<String> parseNetworkResponse(NetworkResponse response) {
  39. String parsed;
  40. try {
  41. parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
  42. } catch (UnsupportedEncodingException e) {
  43. parsed = new String(response.data);
  44. }
  45. return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
  46. }
  47. }

可以看到即使加上注释代码也没多少,我们首先从类的定义开始分析:

  1. public class StringRequest extends Request<String>

可以看到StringRequest继承自Request,而且提供了一个泛型参数String用来表示该类主要用来处理String类型的数据。这也是为何StringRequest中的代码如此简单的原因,因为复杂的网络请求的绝大部分公共功能都在抽象类Request类中实现了,我们只需要继承自它然后重写一些属于自己的特定的方法即可。因此我们知道如果我们要定制一个XMLRequest通过样也需要继承自Request,然后指定泛型参数为XML解析器即可。

接下来是其构造器,这个在初识Volley的博客中已经讲过,因此不再赘述。

然后是两个重写方法,这也是我们关注的重点:

  1. @Override
  2. protected void deliverResponse(String response) {
  3. mListener.onResponse(response);
  4. }
  5.  
  6. @Override
  7. protected Response<String> parseNetworkResponse(NetworkResponse response) {
  8. String parsed;
  9. try {
  10. parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
  11. } catch (UnsupportedEncodingException e) {
  12. parsed = new String(response.data);
  13. }
  14. return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
  15. }

第一个为protected void deliverResponse(String response) ,它是在其父类Request中定义的一个抽象方法,即所有继承自Request的子类必须重写它,其定义如下:

  1. abstract protected void deliverResponse(T response);

可以看到参数为传入的泛型参数,该函数的功能即为当从服务器端获取数据成功时会自动回调该方法。采用的是观察者模式,即先定义一个泛型的Listener对象,然后再该方法中调用该对象的onResponse(response)方法。

第二个为 protected Response<String> parseNetworkResponse(NetworkResponse response),它同样是在其父类Request中定义的一个抽象方法,即所有继承自Request的子类必须重写它,其定义如下:

  1. abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

可以看到参数为传入的泛型参数,顾名思义该函数的功能即为将从服务器端获取到的数据进行解析,是整个类中最核心的部分,也是不同功能的Request必须定制重写的一个方法,服务器端返回数据用NetworkResponse这个类来表示,其定义如下:

  1. public class NetworkResponse {
  2. /**
  3. * Creates a new network response.
  4. * @param statusCode the HTTP status code
  5. * @param data Response body
  6. * @param headers Headers returned with this response, or null for none
  7. * @param notModified True if the server returned a 304 and the data was already in cache
  8. */
  9. public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
  10. boolean notModified) {
  11. this.statusCode = statusCode;
  12. this.data = data;
  13. this.headers = headers;
  14. this.notModified = notModified;
  15. }
  16.  
  17. public NetworkResponse(byte[] data) {
  18. this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
  19. }
  20.  
  21. public NetworkResponse(byte[] data, Map<String, String> headers) {
  22. this(HttpStatus.SC_OK, data, headers, false);
  23. }
  24.  
  25. /** The HTTP status code. */
  26. public final int statusCode;
  27.  
  28. /** Raw data from this response. */
  29. public final byte[] data;
  30.  
  31. /** Response headers. */
  32. public final Map<String, String> headers;
  33.  
  34. /** True if the server returned a 304 (Not Modified). */
  35. public final boolean notModified;
  36. }

重点关注byte[] data这个成员属性, 它是用来存储从服务器端获取的数据,/** Raw data from this response. */,即从服务器端获取到数据将以字节的形式存放在data字节数组中。因此我们需要获取怎样类型的数据就只需要对该数组中的内容进行相应操作即可,如在上述的StringRequest中因为要获取的是String类型的数据,因此我们需要将其以字符串的形式表示, parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));然后调用Response.success()方法将其返回return
Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));。其中success返回的是代泛型参数的Response对象,其定义如下:

  1. /** Returns a successful response containing the parsed result. */
  2. public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
  3. return new Response<T>(result, cacheEntry);
  4. }

讲到这我们基本上就从源码的角度彻底理解了StringRequest类,那么接下来我们就按照上述我为大家分析的步骤模仿写一个XMLRequest,代码如下:

  1. public class XMLRequest extends Request<XmlPullParser> {//继承自Request类,指定泛型参数为XML解析器
  2.  
  3. private final Listener<XmlPullParser> mListener;//指定监听器的泛型参数为XML解析器
  4.  
  5. public XMLRequest(int method, String url, Listener<XmlPullParser> listener,
  6. ErrorListener errorListener) {
  7. super(method, url, errorListener);
  8. mListener = listener;
  9. }
  10.  
  11. public XMLRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorListener) {
  12. this(Method.GET, url, listener, errorListener);
  13. }
  14.  
  15. @Override
  16. protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
  17. try {
  18. String xmlString = new String(response.data,
  19. HttpHeaderParser.parseCharset(response.headers));
  20. XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
  21. XmlPullParser xmlPullParser = factory.newPullParser();
  22. xmlPullParser.setInput(new StringReader(xmlString));
  23. return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));//返回包含该xml解析器的Response对象
  24. } catch (UnsupportedEncodingException e) {
  25. return Response.error(new ParseError(e));
  26. } catch (XmlPullParserException e) {
  27. return Response.error(new ParseError(e));
  28. }
  29. }
  30.  
  31. @Override
  32. protected void deliverResponse(XmlPullParser response) {
  33. mListener.onResponse(response);
  34. }
  35.  
  36. }

如果你了解XML解析的话那么上述代码对你而言将会相当的简单。下面我们就来使用这个我们自定义的XMLRequest。

  1. XMLRequest xmlRequest = new XMLRequest(
  2. "http://flash.weather.com.cn/wmaps/xml/china.xml",
  3. new Response.Listener<XmlPullParser>() {
  4. @Override
  5. public void onResponse(XmlPullParser response) {
  6. try {
  7. int eventType = response.getEventType();
  8. while (eventType != XmlPullParser.END_DOCUMENT) {
  9. switch (eventType) {
  10. case XmlPullParser.START_TAG:
  11. String nodeName = response.getName();
  12. if ("city".equals(nodeName)) {
  13. String pName = response.getAttributeValue(0);
  14. Log.d("TAG", "pName is " + quName);
  15. }
  16. break;
  17. }
  18. eventType = response.next();
  19. }
  20. } catch (XmlPullParserException e) {
  21. e.printStackTrace();
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }, new Response.ErrorListener() {
  27. @Override
  28. public void onErrorResponse(VolleyError error) {
  29. Log.e("TAG", error.getMessage(), error);
  30. }
  31. });
  32. mQueue.add(xmlRequest);

运行结果如下:



讲到这基本上就把Volley的用法及其扩展全部讲完了,相信大家对Volley使用已经相当熟练了,但这还不够,接下来我们不再是从单个类的源码角度来解析Volley,而是从整个框架体系角度来解析Volley,让你彻底了解Volley的整个框架体系。

二Volley框架源码剖析

首先我们来看一下谷歌官方文档中Volley的框架图:



从这个图可以看到整个框架体系包括至少三个线程:主线程(main thread),缓存调度线程(cache thread),网络调度线程(network thread)。那么我们首先从主线程入手。

可以看到在主线程中会根据优先级将请求添加到缓存队列。所谓缓存队列就是前面我们每次使用Volley时都要创建的RequestQueue对象。那么我们首先来看一下其源代码:

  1. public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
  2. File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
  3.  
  4. String userAgent = "volley/0";
  5. try {
  6. String packageName = context.getPackageName();
  7. PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
  8. userAgent = packageName + "/" + info.versionCode;
  9. } catch (NameNotFoundException e) {
  10. }
  11.  
  12. if (stack == null) {
  13. if (Build.VERSION.SDK_INT >= 9) {
  14. stack = new HurlStack();
  15. } else {
  16. // Prior to Gingerbread, HttpUrlConnection was unreliable.
  17. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
  18. stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
  19. }
  20. }
  21.  
  22. Network network = new BasicNetwork(stack);
  23.  
  24. RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
  25. queue.start();
  26.  
  27. return queue;
  28. }
  29.  
  30. /**
  31. * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
  32. *
  33. * @param context A {@link Context} to use for creating the cache dir.
  34. * @return A started {@link RequestQueue} instance.
  35. */
  36. public static RequestQueue newRequestQueue(Context context) {
  37. return newRequestQueue(context, null);
  38. }

其中第二个即为我们每次使用Volley时调用的即Volley.newRequestQueue(context)。可以看到在该方法中事实上是调用的带两个参数的重载方法,即第一个方法。可以看到首先指定了硬盘缓存的位置为data/data/package_name/cache/volley/...,然后判断SDK的版本号,如果大于等于9则会创建一个HurlStack(),否则创建一个HttpClientStack(),而实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的,至于为何这么选择在我的博文【安卓网络请求开源框架Volley源码解析系列】初识Volley及其基本用法已介绍过,在此不再赘述。创建好了HttpStack之后,接下来又创建了一个Network对象(具体实现类是BasicNetwork),它是用于根据传入的HttpStack对象来处理网络请求的,然后创建一个请求队列RequestQueue调用其start()启动,最后返回该请求队列。

那么最核心的当然是start()方法,我们来看一下其源码:

  1. public void start() {
  2. stop(); // Make sure any currently running dispatchers are stopped.
  3. // Create the cache dispatcher and start it.
  4. mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
  5. mCacheDispatcher.start();
  6.  
  7. // Create network dispatchers (and corresponding threads) up to the pool size.
  8. for (int i = 0; i < mDispatchers.length; i++) {
  9. NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
  10. mCache, mDelivery);
  11. mDispatchers[i] = networkDispatcher;
  12. networkDispatcher.start();
  13. }
  14. }

可以看到,该方法代码逻辑非常简单,创建了一个CacheDispatcher和4个NetworkDispatcher个对象,然后调用start(),这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。可以看到在CacheDispatcher的构造器中传入了缓存请求队列(mCacheQueue),网络请求队列(mNetworkQueue),硬盘缓存对象(DiskBasedCache),结果分发器(mDelivery)。之所以也传入网络请求队列是因为一部分缓存请求可能已经过期了,这时候需要重新从网络获取。NetworkDispatcher除了缓存请求队列没有传入,其他跟CacheDispatcher一样。到这里RequestQueue的任务就完成了,以后有请求都会交由这些dispatcher线程处理。

创建了请求队列之后我们只需要构建出相应的Request,然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作,那么接下来我们来看一下请求添加

即add()的源码:

  1. /**
  2. * Adds a Request to the dispatch queue.
  3. * @param request The request to service
  4. * @return The passed-in request
  5. */
  6. public Request add(Request request) {
  7. // Tag the request as belonging to this queue and add it to the set of current requests.
  8. request.setRequestQueue(this);
  9. synchronized (mCurrentRequests) {
  10. mCurrentRequests.add(request);
  11. }
  12.  
  13. // Process requests in the order they are added.
  14. request.setSequence(getSequenceNumber());
  15. request.addMarker("add-to-queue");
  16.  
  17. // If the request is uncacheable, skip the cache queue and go straight to the network.
  18. if (!request.shouldCache()) {
  19. mNetworkQueue.add(request);
  20. return request;
  21. }
  22.  
  23. // Insert request into stage if there's already a request with the same cache key in flight.
  24. synchronized (mWaitingRequests) {
  25. String cacheKey = request.getCacheKey();
  26. if (mWaitingRequests.containsKey(cacheKey)) {
  27. // There is already a request in flight. Queue up.
  28. Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
  29. if (stagedRequests == null) {
  30. stagedRequests = new LinkedList<Request>();
  31. }
  32. stagedRequests.add(request);
  33. mWaitingRequests.put(cacheKey, stagedRequests);
  34. if (VolleyLog.DEBUG) {
  35. VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
  36. }
  37. } else {
  38. // Insert 'null' queue for this cacheKey, indicating there is now a request in
  39. // flight.
  40. mWaitingRequests.put(cacheKey, null);
  41. mCacheQueue.add(request);
  42. }
  43. return request;
  44. }
  45. }

可以看到add的大致逻辑如下:

1.将请求加入mCurrentRequests集合(注意此行为是同步的)

2.为请求添加序列号

3.判断是否应该缓存请求,如果不需要,加入网络请求队列

4.如果有相同请求正在被处理,加入到相同请求等待队列中,否则加入缓存请求队列。

通过上述分析我们可以知道add方法仅仅只起到添加请求的作用,而请求的处理是CacheDispatcher和NetworkDispatcher来完成的。这也正好与上述给的Volley的框架流程图相符。我们来看一下其源码:

  1. public class CacheDispatcher extends Thread {
  2.  
  3. private static final boolean DEBUG = VolleyLog.DEBUG;
  4.  
  5. /** The queue of requests coming in for triage. */
  6. private final BlockingQueue<Request> mCacheQueue;
  7.  
  8. /** The queue of requests going out to the network. */
  9. private final BlockingQueue<Request> mNetworkQueue;
  10.  
  11. /** The cache to read from. */
  12. private final Cache mCache;
  13.  
  14. /** For posting responses. */
  15. private final ResponseDelivery mDelivery;
  16.  
  17. /** Used for telling us to die. */
  18. private volatile boolean mQuit = false;
  19.  
  20. /**
  21. * Creates a new cache triage dispatcher thread. You must call {@link #start()}
  22. * in order to begin processing.
  23. *
  24. * @param cacheQueue Queue of incoming requests for triage
  25. * @param networkQueue Queue to post requests that require network to
  26. * @param cache Cache interface to use for resolution
  27. * @param delivery Delivery interface to use for posting responses
  28. */
  29. public CacheDispatcher(
  30. BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue,
  31. Cache cache, ResponseDelivery delivery) {
  32. mCacheQueue = cacheQueue;
  33. mNetworkQueue = networkQueue;
  34. mCache = cache;
  35. mDelivery = delivery;
  36. }
  37.  
  38. /**
  39. * Forces this dispatcher to quit immediately. If any requests are still in
  40. * the queue, they are not guaranteed to be processed.
  41. */
  42. public void quit() {
  43. mQuit = true;
  44. interrupt();
  45. }
  46.  
  47. @Override
  48. public void run() {
  49. if (DEBUG) VolleyLog.v("start new dispatcher");
  50. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  51.  
  52. // Make a blocking call to initialize the cache.
  53. mCache.initialize();
  54.  
  55. while (true) {
  56. try {
  57. // Get a request from the cache triage queue, blocking until
  58. // at least one is available.
  59. final Request request = mCacheQueue.take();
  60. request.addMarker("cache-queue-take");
  61.  
  62. // If the request has been canceled, don't bother dispatching it.
  63. if (request.isCanceled()) {
  64. request.finish("cache-discard-canceled");
  65. continue;
  66. }
  67.  
  68. // Attempt to retrieve this item from cache.
  69. Cache.Entry entry = mCache.get(request.getCacheKey());
  70. if (entry == null) {
  71. request.addMarker("cache-miss");
  72. // Cache miss; send off to the network dispatcher.
  73. mNetworkQueue.put(request);
  74. continue;
  75. }
  76.  
  77. // If it is completely expired, just send it to the network.
  78. if (entry.isExpired()) {
  79. request.addMarker("cache-hit-expired");
  80. request.setCacheEntry(entry);
  81. mNetworkQueue.put(request);
  82. continue;
  83. }
  84.  
  85. // We have a cache hit; parse its data for delivery back to the request.
  86. request.addMarker("cache-hit");
  87. Response<?> response = request.parseNetworkResponse(
  88. new NetworkResponse(entry.data, entry.responseHeaders));
  89. request.addMarker("cache-hit-parsed");
  90.  
  91. if (!entry.refreshNeeded()) {
  92. // Completely unexpired cache hit. Just deliver the response.
  93. mDelivery.postResponse(request, response);
  94. } else {
  95. // Soft-expired cache hit. We can deliver the cached response,
  96. // but we need to also send the request to the network for
  97. // refreshing.
  98. request.addMarker("cache-hit-refresh-needed");
  99. request.setCacheEntry(entry);
  100.  
  101. // Mark the response as intermediate.
  102. response.intermediate = true;
  103.  
  104. // Post the intermediate response back to the user and have
  105. // the delivery then forward the request along to the network.
  106. mDelivery.postResponse(request, response, new Runnable() {
  107. @Override
  108. public void run() {
  109. try {
  110. mNetworkQueue.put(request);
  111. } catch (InterruptedException e) {
  112. // Not much we can do about this.
  113. }
  114. }
  115. });
  116. }
  117.  
  118. } catch (InterruptedException e) {
  119. // We may have been interrupted because it was time to quit.
  120. if (mQuit) {
  121. return;
  122. }
  123. continue;
  124. }
  125. }
  126. }
  127. }

重点关注run()方法,因为CacheDispatcher继承自Thread,其重要的逻辑处理都是在run()方法中完成的。可以看到在run()方法中存在一个while(true)死循环,即

缓存线程是一直在运行的,在该while循环中的处理过程大致如下:

1首先从缓存中取出一个请求,如果结果为空,则把该请求加入到网络请求中,否则对缓存结果进行过期判断。

2如果过期了,则加入网络请求队列。如果没有过期,那么通过request.parseNetworkResponse方法将硬盘缓存中的数据构造为Response对象(Request的parseNetworkResponse是抽象的,需要子类重写)。

在来看看网络请求线程的源码:

  1. public class NetworkDispatcher extends Thread {
  2. /** The queue of requests to service. */
  3. private final BlockingQueue<Request> mQueue;
  4. /** The network interface for processing requests. */
  5. private final Network mNetwork;
  6. /** The cache to write to. */
  7. private final Cache mCache;
  8. /** For posting responses and errors. */
  9. private final ResponseDelivery mDelivery;
  10. /** Used for telling us to die. */
  11. private volatile boolean mQuit = false;
  12.  
  13. /**
  14. * Creates a new network dispatcher thread. You must call {@link #start()}
  15. * in order to begin processing.
  16. *
  17. * @param queue Queue of incoming requests for triage
  18. * @param network Network interface to use for performing requests
  19. * @param cache Cache interface to use for writing responses to cache
  20. * @param delivery Delivery interface to use for posting responses
  21. */
  22. public NetworkDispatcher(BlockingQueue<Request> queue,
  23. Network network, Cache cache,
  24. ResponseDelivery delivery) {
  25. mQueue = queue;
  26. mNetwork = network;
  27. mCache = cache;
  28. mDelivery = delivery;
  29. }
  30.  
  31. /**
  32. * Forces this dispatcher to quit immediately. If any requests are still in
  33. * the queue, they are not guaranteed to be processed.
  34. */
  35. public void quit() {
  36. mQuit = true;
  37. interrupt();
  38. }
  39.  
  40. @Override
  41. public void run() {
  42. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  43. Request request;
  44. while (true) {
  45. try {
  46. // Take a request from the queue.
  47. request = mQueue.take();
  48. } catch (InterruptedException e) {
  49. // We may have been interrupted because it was time to quit.
  50. if (mQuit) {
  51. return;
  52. }
  53. continue;
  54. }
  55.  
  56. try {
  57. request.addMarker("network-queue-take");
  58.  
  59. // If the request was cancelled already, do not perform the
  60. // network request.
  61. if (request.isCanceled()) {
  62. request.finish("network-discard-cancelled");
  63. continue;
  64. }
  65.  
  66. // Tag the request (if API >= 14)
  67. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  68. TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
  69. }
  70.  
  71. // Perform the network request.
  72. NetworkResponse networkResponse = mNetwork.performRequest(request);
  73. request.addMarker("network-http-complete");
  74.  
  75. // If the server returned 304 AND we delivered a response already,
  76. // we're done -- don't deliver a second identical response.
  77. if (networkResponse.notModified && request.hasHadResponseDelivered()) {
  78. request.finish("not-modified");
  79. continue;
  80. }
  81.  
  82. // Parse the response here on the worker thread.
  83. Response<?> response = request.parseNetworkResponse(networkResponse);
  84. request.addMarker("network-parse-complete");
  85.  
  86. // Write to cache if applicable.
  87. // TODO: Only update cache metadata instead of entire record for 304s.
  88. if (request.shouldCache() && response.cacheEntry != null) {
  89. mCache.put(request.getCacheKey(), response.cacheEntry);
  90. request.addMarker("network-cache-written");
  91. }
  92.  
  93. // Post the response back.
  94. request.markDelivered();
  95. mDelivery.postResponse(request, response);
  96. } catch (VolleyError volleyError) {
  97. parseAndDeliverNetworkError(request, volleyError);
  98. } catch (Exception e) {
  99. VolleyLog.e(e, "Unhandled exception %s", e.toString());
  100. mDelivery.postError(request, new VolleyError(e));
  101. }
  102. }
  103. }
  104.  
  105. private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
  106. error = request.parseNetworkError(error);
  107. mDelivery.postError(request, error);
  108. }
  109. }

同样我们也只需要关注run()方法中的代码,可以看到在该方法中同样存在一个while(true)死循环,即网络请求线程也是一直在运行的,在该while循环中的处理过程大致和缓存线程相同,也是构造Response对象,然后交由ResponseDelivery处理,但是这里的Response对象是通过NetworkResponse转化的,而这个NetworkResponse是从网络获取的,这里最核心的一行代码就是:

  1. // Perform the network request.
  2. NetworkResponse networkResponse = mNetwork.performRequest(request);

即执行网络请求,在这个perfromRequest中会调用HttpClient或HttpUrlConnection去执行网络请求,然后将从服务器端获取到的数据构造成一个NetWorkResponse对象返回。

4请求结果的分发与处理:

在NetworkDispatcher中收到了NetworkResponse这个返回值后又会调用Request的parseNetworkResponse()方法来解析NetworkResponse中的数据,以及将数据写入到缓存,这个方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯定不同。还记得我们上述自定义Request的方式吗?其中parseNetworkResponse()这个方法就是必须要重写的。

在解析完了NetworkResponse中的数据之后,又会调用ExecutorDelivery的postResponse()方法来回调解析出的数据,代码如下

  1. @Override
  2. public void postResponse(Request<?> request, Response<?> response) {
  3. postResponse(request, response, null);
  4. }
  5.  
  6. @Override
  7. public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
  8. request.markDelivered();
  9. request.addMarker("post-response");
  10. mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
  11. }

可以看到,在mResponsePoster的execute()方法中传入了一个ResponseDeliveryRunnable对象,这样做是为了保证该对象中的run()方法是在主线程当中运行的了,我们来看一下其源码:

  1. private class ResponseDeliveryRunnable implements Runnable {
  2. private final Request mRequest;
  3. private final Response mResponse;
  4. private final Runnable mRunnable;
  5.  
  6. public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
  7. mRequest = request;
  8. mResponse = response;
  9. mRunnable = runnable;
  10. }
  11.  
  12. @SuppressWarnings("unchecked")
  13. @Override
  14. public void run() {
  15. // If this request has canceled, finish it and don't deliver.
  16. if (mRequest.isCanceled()) {
  17. mRequest.finish("canceled-at-delivery");
  18. return;
  19. }
  20.  
  21. // Deliver a normal response or error, depending.
  22. if (mResponse.isSuccess()) {
  23. mRequest.deliverResponse(mResponse.result);
  24. } else {
  25. mRequest.deliverError(mResponse.error);
  26. }
  27.  
  28. // If this is an intermediate response, add a marker, otherwise we're done
  29. // and the request can be finished.
  30. if (mResponse.intermediate) {
  31. mRequest.addMarker("intermediate-response");
  32. } else {
  33. mRequest.finish("done");
  34. }
  35.  
  36. // If we have been provided a post-delivery runnable, run it.
  37. if (mRunnable != null) {
  38. mRunnable.run();
  39. }
  40. }
  41. }
  42. }

同样重点关注run方法中的代码,我们可以看到如果请求成功会调用即

  1. if (mResponse.isSuccess()) {
  2. mRequest.deliverResponse(mResponse.result);}

而deliverResponse(mResponse.result)这个方法就是我们在自定义Request时需要重写的另外一个方法,每一条网络请求的响应都会回调到这个方法中,所以我们需要重写该方法,在这个方法中将响应的数据回调到Response.Listener的onResponse()方法中就可以了。

至此Volley的整个框架体系就解析完了,我们来总结一下:

1首先在主线程中创建一个请求队列:Volley.newRequestQueue(context)然后通过add()添加一条网络请求。

2这条请求会被先添加到缓存队列,如果在缓存队列中找到该请求则读取缓存解析,然后将解析结果返回给主线程

3如果没找到该请求则将该请求加入网络请求队列,发送HTTP请求,获取服务器端返回的结果,解析之后写入缓存,然后将解析结果返回主线程。

如果你再仔细看一下上述Volley框架体系结构图,你会看到上述三个步骤正好完全对应谷歌官方文档的架构体系流程图。





一个关于Volley框架源码解析的博客

【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. [源码解析] PyTorch 分布式(17) --- 结合DDP和分布式 RPC 框架

    [源码解析] PyTorch 分布式(17) --- 结合DDP和分布式 RPC 框架 目录 [源码解析] PyTorch 分布式(17) --- 结合DDP和分布式 RPC 框架 0x00 摘要 0 ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  5. Spring源码解析系列汇总

    相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的Spring源码解析系列文章的汇总,总共包含以下专题.喜欢的同学可以收藏起来以备不时之需 SpringIOC源码解析(上) 本篇文章搭建了IOC源 ...

  6. TiKV 源码解析系列文章(三)Prometheus(上)

    本文为 TiKV 源码解析系列的第三篇,继续为大家介绍 TiKV 依赖的周边库 rust-prometheus,本篇主要介绍基础知识以及最基本的几个指标的内部工作机制,下篇会介绍一些高级功能的实现原理 ...

  7. TiKV 源码解析系列 - Raft 的优化

    本系列文章主要面向 TiKV 社区开发者,重点介绍 TiKV 的系统架构,源码结构,流程解析.目的是使得开发者阅读之后,能对 TiKV 项目有一个初步了解,更好的参与进入 TiKV 的开发中.本文是本 ...

  8. Android源码解析系列

    转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...

  9. Cwinux源码解析系列

      Cwinux源码解析系列

随机推荐

  1. bzoj3211花神游历各国 线段树

    3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 4252  Solved: 1547[Submit][Status][Discu ...

  2. spring boot新建项目问题总结

    1.启动报:Unregistering JMX-exposed beans on shutdown 原因:以来的Tomcat没有启动 解决办法:在pom.xml加入依赖 <dependency& ...

  3. 根据构建类型自动修改依赖库的BuildConfig.DEBUG的值

    app模块引用了library,在library模块中控制日志输出使用的是 if (BuildConfig.DEBUG) { logger.d("print %s", msg); ...

  4. ubuntu上的附件-终端和用快捷键ctrl+alt+f1 有啥区别

    ctrl +alt +Fn 打开的是模拟终端,简单说来,linux系统一开机会自动打开6个模拟终端,然后自动切换到其中一个(一般来说是切换到图形界面的那个也就是说窗口管理器是在这6个模拟终端中运行的) ...

  5. 反向Ajax之Socket.io

    1.什么是反向ajax? 传统的ajax的困惑? 新需求--当服务器端数据发生变化时,客户端(浏览器端)如何即时得到通知呢? 找一些实际的案例:客服系统.在线聊天 这类应用,有一个显著的特点: 数据并 ...

  6. cookie读取、写入、删除

    需求:用户访问页面之后出现弹框,点击关闭之后24小时内不会再出现.实现:cookie首先温习一点cookie的知识,明确以下几点:什么是cookie?cookie 是存储于访问者的计算机中的变量.每当 ...

  7. javascript装饰器模式

    装饰器模式 什么是装饰器 原名decorator 被翻译为装饰器 可以理解为装饰 修饰 包装等意 现实中的作用 一间房子通过装饰可以变得更华丽,功能更多 类似一部手机可以单独使用 但是很多人都愿意家个 ...

  8. HashMap和ConcurrentHashMap实现原理及源码分析

    HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...

  9. Java常量初始化后不会再去重新获取

    Java虚拟机编译机制:更改常量部分 最近一个Java项目中需要修改一个静态常量的值,本地修改编译以后调试正常,然后把对应的entity类的class文件上传到服务器对应的目录以后系统依旧我行我素,各 ...

  10. Microsoft Visual Studio 2017 编译最新版 libuv 1.x 并且生成 LIB 和 DLL 两种模式

    以为昨天晚上编译通过就可以了,哪知道,早上编译DLL的一车的报错 今天开始逐个解决,终于把引用的问题一亿解决了,具体步骤如下 1 在 Windows 平台下编译出错,显示导出未定义,打开 uv-win ...