详细解读Volley(一)—— 基本Request对象 & RequestQueue&请求取消

  Volley它非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。 所以不建议用它去进行下载文件、加载大图的操作。有人可能会问,如果我服务器中的图片都挺大的,activity中listview要加载这些图片,是不 是不能用这个框架呢?其实这个问题本身就是错误的,你想如果你服务器的图片都是大图,你要在手机上用照片墙进行展示,下载图片都会慢的要死,这个本身就是 不可行的。所以在项目初期就应该建立好服务器端的小图,照片墙用加载小图,点击后再从网络上下载大图,这才是正确思路。这时你就可以用volley加载小 图了,如果是要加载大图,可以用别的算法,强烈建议手动完成大图清除的工作,否则很可能会出现OOM。Volley本身没有做什么回收算法,还是用最基本 的GC,实际使用中可以根据需要自定义一下。

零、准备工作

Git项目,添加为lib,申请权限

  1. git clone https://android.googlesource.com/platform/frameworks/volley
  1. <uses-permission android:name="android.permission.INTERNET" />

一、初始化请求对象——RequestQueue

  1. public class MyApplication extends Application {
  2.  
  3. public static RequestQueue requestQueue;
  4.  
  5. @Override
  6. public void onCreate() {
  7. super.onCreate();
  8. // 不必为每一次HTTP请求都创建一个RequestQueue对象,推荐在application中初始化
  9. requestQueue = Volley.newRequestQueue(this);
  10. }
  11. }

既然是Http操作,自然有请求和响应,RequestQueue是一个请求队列 对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。RequestQueue内部的设计就是非常合适高并发的,因此我们不必为 每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的。所以在这里我建立了一个application,然后用单例模式定义了 这个对象。当然,你可以选择在一个activity中定义一个RequestQueue对象,但这样可能会比较麻烦,而且还可能出现请求队列包含 activity强引用的问题,因此我还是推荐在application中定义。

二、使用StringRequest接收String类型的响应

前 面定义了请求对象,那么自然就有接收响应的对象了,这个框架中有多个响应对象,像StringRequest接受到的响应就是string类型 的;JsonRequest接收的响应就是Json类型对象。其实它们都是继承自Request<T>,然后根据不同的响应数据来进行特殊的 处理。

2.1 初始化

  1. /**
  2. * Creates a new request with the given method.
  3. *
  4. * @param method the request {@link Method} to use
  5. * @param url URL to fetch the string at
  6. * @param listener Listener to receive the String response
  7. * @param errorListener Error listener, or null to ignore errors
  8. */
  9. public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener)
  1. /**
  2. * Creates a new GET request.
  3. *
  4. * @param url URL to fetch the string at
  5. * @param listener Listener to receive the String response
  6. * @param errorListener Error listener, or null to ignore errors
  7. */
  8. public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
  9. this(Method.GET, url, listener, errorListener);
  10. }

这就是StringRequest的两个构造函数,不同之处是一个传入了一个method的参数,一个没有。其实看上面的源码就知道,如果你不传入method,默认会调用GET方式进行请求。当你传入了Method.POST就会用post来请求。

【参数解释】

url:请求的地址

listener:响应成功的监听器

errorListener:出错时的监听器

  1. StringRequest getStringRequest = new StringRequest("http://www.baidu.com", new ResponseListener(), new ResponseErrorListener());
  1. StringRequest postStringRequest = new StringRequest(Method.POST, "http://www.baidu.com", new ResponseListener(),null);

2.2 配置监听器

  1. /**
  2. * @author:Jack Tony
  3. * @description :设置响应结果监听器,因为用的是StringRequest,所以这里的结果我定义为string类型
  4. * @date :2015年1月24日
  5. */
  6. private class ResponseListener implements Response.Listener<String>{
  7.  
  8. @Override
  9. public void onResponse(String response) {
  10. // TODO 自动生成的方法存根
  11. Log.d("TAG", "-------------\n" + response);
  12. }
  13. }
  14.  
  15. /**
  16. * @author:Jack Tony
  17. * @description :访问出错时触发的监听器
  18. * @date :2015年1月28日
  19. */
  20. private class ResponseErrorListener implements Response.ErrorListener{
  21.  
  22. @Override
  23. public void onErrorResponse(VolleyError error) {
  24. // TODO 自动生成的方法存根
  25. Log.e("TAG", error.getMessage(), error);
  26. }
  27.  
  28. }

这两个监听器没啥可说的,因为是StringRequest调用的,所以成功时触发的监听器中得到的response就是String类型。如果访问出错,那么就打印出错信息。

2.3 执行GET请求

现在我们有了请求对象和响应对象,外加处理响应结果的监听器,那么就执行最后一步——发送请求。发送请求很简单,将响应对象添加到请求队列即可。

  1.      mQueue.add(getStringRequest);

完整代码:

  1. RequestQueue mQueue = MyApplication.requestQueue;
  2. StringRequest getStringRequest = new StringRequest("http://www.baidu.com", new ResponseListener(), new ResponseErrorListener());
  3. mQueue.add(getStringRequest);

通过简单的add()方法就直接发送了请求,如果服务器响应了请求就会触发我们的结果监听器,然后被打印出啦。现在请求的是百度,所以得到了网页的源码:

2.4 执行POST请求

POST和GET一样,仅仅是传入的方法不同。但一般我们的post都是要带一些参数的,volley没有提供附加参数的方法,所以我们必须要在StringRequest的匿名类中重写getParams()方法:

  1. StringRequest stringRequest = new StringRequest(Method.POST, url, listener, errorListener) {
  2. @Override
  3. protected Map<String, String> getParams() throws AuthFailureError {
  4. Map<String, String> map = new HashMap<String, String>();
  5. map.put("params1", "value1");
  6. map.put("params2", "value2");
  7. return map;
  8. }
  9. };

这 样就传入了value1和value2两个参数了。现在可能有人会问为啥这个框架不提供这个传参的方法,还非得让我们重写。我个人觉得这个框架本身的目的 就是执行频繁的网络请求,比如下载图片,解析json数据什么的,用GET就能很好的实现了,所以就没有提供传参的POST方法。为了简单起见,我重写了 Request类中的getParams(),添加了传参的方法,以后通过setParams()就可以传参数了。

重写的代码块:

  1. Map<String, String> mParams = null;
  2.  
  3. /**
  4. * Returns a Map of parameters to be used for a POST or PUT request. Can throw
  5. * {@link AuthFailureError} as authentication may be required to provide these values.
  6. *
  7. * <p>Note that you can directly override {@link #getBody()} for custom data.</p>
  8. *
  9. * @throws AuthFailureError in the event of auth failure
  10. */
  11. protected Map<String, String> getParams() throws AuthFailureError {
  12. return mParams;
  13. }
  14.  
  15. public void setParams(Map<String, String> params){
  16. mParams = params;
  17. }

完整代码:

  1. /*
  2. * Copyright (C) 2011 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16.  
  17. package com.android.volley;
  18.  
  19. import java.io.UnsupportedEncodingException;
  20. import java.net.URLEncoder;
  21. import java.util.Collections;
  22. import java.util.Map;
  23.  
  24. import android.net.TrafficStats;
  25. import android.net.Uri;
  26. import android.os.Handler;
  27. import android.os.Looper;
  28. import android.os.SystemClock;
  29. import android.text.TextUtils;
  30.  
  31. import com.android.volley.VolleyLog.MarkerLog;
  32.  
  33. /**
  34. * Base class for all network requests.
  35. *
  36. * @param <T> The type of parsed response this request expects.
  37. */
  38. public abstract class Request<T> implements Comparable<Request<T>> {
  39.  
  40. /**
  41. * Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}.
  42. */
  43. private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
  44.  
  45. /**
  46. * Supported request methods.
  47. */
  48. public interface Method {
  49. int DEPRECATED_GET_OR_POST = -1;
  50. int GET = 0;
  51. int POST = 1;
  52. int PUT = 2;
  53. int DELETE = 3;
  54. int HEAD = 4;
  55. int OPTIONS = 5;
  56. int TRACE = 6;
  57. int PATCH = 7;
  58. }
  59.  
  60. /** An event log tracing the lifetime of this request; for debugging. */
  61. private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
  62.  
  63. /**
  64. * Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
  65. * TRACE, and PATCH.
  66. */
  67. private final int mMethod;
  68.  
  69. /** URL of this request. */
  70. private final String mUrl;
  71.  
  72. /** Default tag for {@link TrafficStats}. */
  73. private final int mDefaultTrafficStatsTag;
  74.  
  75. /** Listener interface for errors. */
  76. private final Response.ErrorListener mErrorListener;
  77.  
  78. /** Sequence number of this request, used to enforce FIFO ordering. */
  79. private Integer mSequence;
  80.  
  81. /** The request queue this request is associated with. */
  82. private RequestQueue mRequestQueue;
  83.  
  84. /** Whether or not responses to this request should be cached. */
  85. private boolean mShouldCache = true;
  86.  
  87. /** Whether or not this request has been canceled. */
  88. private boolean mCanceled = false;
  89.  
  90. /** Whether or not a response has been delivered for this request yet. */
  91. private boolean mResponseDelivered = false;
  92.  
  93. // A cheap variant of request tracing used to dump slow requests.
  94. private long mRequestBirthTime = 0;
  95.  
  96. /** Threshold at which we should log the request (even when debug logging is not enabled). */
  97. private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;
  98.  
  99. /** The retry policy for this request. */
  100. private RetryPolicy mRetryPolicy;
  101.  
  102. /**
  103. * When a request can be retrieved from cache but must be refreshed from
  104. * the network, the cache entry will be stored here so that in the event of
  105. * a "Not Modified" response, we can be sure it hasn't been evicted from cache.
  106. */
  107. private Cache.Entry mCacheEntry = null;
  108.  
  109. /** An opaque token tagging this request; used for bulk cancellation. */
  110. private Object mTag;
  111.  
  112. /**
  113. * Creates a new request with the given URL and error listener. Note that
  114. * the normal response listener is not provided here as delivery of responses
  115. * is provided by subclasses, who have a better idea of how to deliver an
  116. * already-parsed response.
  117. *
  118. * @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
  119. */
  120. @Deprecated
  121. public Request(String url, Response.ErrorListener listener) {
  122. this(Method.DEPRECATED_GET_OR_POST, url, listener);
  123. }
  124.  
  125. /**
  126. * Creates a new request with the given method (one of the values from {@link Method}),
  127. * URL, and error listener. Note that the normal response listener is not provided here as
  128. * delivery of responses is provided by subclasses, who have a better idea of how to deliver
  129. * an already-parsed response.
  130. */
  131. public Request(int method, String url, Response.ErrorListener listener) {
  132. mMethod = method;
  133. mUrl = url;
  134. mErrorListener = listener;
  135. setRetryPolicy(new DefaultRetryPolicy());
  136.  
  137. mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
  138. }
  139.  
  140. /**
  141. * Return the method for this request. Can be one of the values in {@link Method}.
  142. */
  143. public int getMethod() {
  144. return mMethod;
  145. }
  146.  
  147. /**
  148. * Set a tag on this request. Can be used to cancel all requests with this
  149. * tag by {@link RequestQueue#cancelAll(Object)}.
  150. *
  151. * @return This Request object to allow for chaining.
  152. */
  153. public Request<?> setTag(Object tag) {
  154. mTag = tag;
  155. return this;
  156. }
  157.  
  158. /**
  159. * Returns this request's tag.
  160. * @see Request#setTag(Object)
  161. */
  162. public Object getTag() {
  163. return mTag;
  164. }
  165.  
  166. /**
  167. * @return this request's {@link com.android.volley.Response.ErrorListener}.
  168. */
  169. public Response.ErrorListener getErrorListener() {
  170. return mErrorListener;
  171. }
  172.  
  173. /**
  174. * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
  175. */
  176. public int getTrafficStatsTag() {
  177. return mDefaultTrafficStatsTag;
  178. }
  179.  
  180. /**
  181. * @return The hashcode of the URL's host component, or 0 if there is none.
  182. */
  183. private static int findDefaultTrafficStatsTag(String url) {
  184. if (!TextUtils.isEmpty(url)) {
  185. Uri uri = Uri.parse(url);
  186. if (uri != null) {
  187. String host = uri.getHost();
  188. if (host != null) {
  189. return host.hashCode();
  190. }
  191. }
  192. }
  193. return 0;
  194. }
  195.  
  196. /**
  197. * Sets the retry policy for this request.
  198. *
  199. * @return This Request object to allow for chaining.
  200. */
  201. public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
  202. mRetryPolicy = retryPolicy;
  203. return this;
  204. }
  205.  
  206. /**
  207. * Adds an event to this request's event log; for debugging.
  208. */
  209. public void addMarker(String tag) {
  210. if (MarkerLog.ENABLED) {
  211. mEventLog.add(tag, Thread.currentThread().getId());
  212. } else if (mRequestBirthTime == 0) {
  213. mRequestBirthTime = SystemClock.elapsedRealtime();
  214. }
  215. }
  216.  
  217. /**
  218. * Notifies the request queue that this request has finished (successfully or with error).
  219. *
  220. * <p>Also dumps all events from this request's event log; for debugging.</p>
  221. */
  222. void finish(final String tag) {
  223. if (mRequestQueue != null) {
  224. mRequestQueue.finish(this);
  225. }
  226. if (MarkerLog.ENABLED) {
  227. final long threadId = Thread.currentThread().getId();
  228. if (Looper.myLooper() != Looper.getMainLooper()) {
  229. // If we finish marking off of the main thread, we need to
  230. // actually do it on the main thread to ensure correct ordering.
  231. Handler mainThread = new Handler(Looper.getMainLooper());
  232. mainThread.post(new Runnable() {
  233. @Override
  234. public void run() {
  235. mEventLog.add(tag, threadId);
  236. mEventLog.finish(this.toString());
  237. }
  238. });
  239. return;
  240. }
  241.  
  242. mEventLog.add(tag, threadId);
  243. mEventLog.finish(this.toString());
  244. } else {
  245. long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
  246. if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
  247. VolleyLog.d("%d ms: %s", requestTime, this.toString());
  248. }
  249. }
  250. }
  251.  
  252. /**
  253. * Associates this request with the given queue. The request queue will be notified when this
  254. * request has finished.
  255. *
  256. * @return This Request object to allow for chaining.
  257. */
  258. public Request<?> setRequestQueue(RequestQueue requestQueue) {
  259. mRequestQueue = requestQueue;
  260. return this;
  261. }
  262.  
  263. /**
  264. * Sets the sequence number of this request. Used by {@link RequestQueue}.
  265. *
  266. * @return This Request object to allow for chaining.
  267. */
  268. public final Request<?> setSequence(int sequence) {
  269. mSequence = sequence;
  270. return this;
  271. }
  272.  
  273. /**
  274. * Returns the sequence number of this request.
  275. */
  276. public final int getSequence() {
  277. if (mSequence == null) {
  278. throw new IllegalStateException("getSequence called before setSequence");
  279. }
  280. return mSequence;
  281. }
  282.  
  283. /**
  284. * Returns the URL of this request.
  285. */
  286. public String getUrl() {
  287. return mUrl;
  288. }
  289.  
  290. /**
  291. * Returns the cache key for this request. By default, this is the URL.
  292. */
  293. public String getCacheKey() {
  294. return getUrl();
  295. }
  296.  
  297. /**
  298. * Annotates this request with an entry retrieved for it from cache.
  299. * Used for cache coherency support.
  300. *
  301. * @return This Request object to allow for chaining.
  302. */
  303. public Request<?> setCacheEntry(Cache.Entry entry) {
  304. mCacheEntry = entry;
  305. return this;
  306. }
  307.  
  308. /**
  309. * Returns the annotated cache entry, or null if there isn't one.
  310. */
  311. public Cache.Entry getCacheEntry() {
  312. return mCacheEntry;
  313. }
  314.  
  315. /**
  316. * Mark this request as canceled. No callback will be delivered.
  317. */
  318. public void cancel() {
  319. mCanceled = true;
  320. }
  321.  
  322. /**
  323. * Returns true if this request has been canceled.
  324. */
  325. public boolean isCanceled() {
  326. return mCanceled;
  327. }
  328.  
  329. /**
  330. * Returns a list of extra HTTP headers to go along with this request. Can
  331. * throw {@link AuthFailureError} as authentication may be required to
  332. * provide these values.
  333. * @throws AuthFailureError In the event of auth failure
  334. */
  335. public Map<String, String> getHeaders() throws AuthFailureError {
  336. return Collections.emptyMap();
  337. }
  338.  
  339. /**
  340. * Returns a Map of POST parameters to be used for this request, or null if
  341. * a simple GET should be used. Can throw {@link AuthFailureError} as
  342. * authentication may be required to provide these values.
  343. *
  344. * <p>Note that only one of getPostParams() and getPostBody() can return a non-null
  345. * value.</p>
  346. * @throws AuthFailureError In the event of auth failure
  347. *
  348. * @deprecated Use {@link #getParams()} instead.
  349. */
  350. @Deprecated
  351. protected Map<String, String> getPostParams() throws AuthFailureError {
  352. return getParams();
  353. }
  354.  
  355. /**
  356. * Returns which encoding should be used when converting POST parameters returned by
  357. * {@link #getPostParams()} into a raw POST body.
  358. *
  359. * <p>This controls both encodings:
  360. * <ol>
  361. * <li>The string encoding used when converting parameter names and values into bytes prior
  362. * to URL encoding them.</li>
  363. * <li>The string encoding used when converting the URL encoded parameters into a raw
  364. * byte array.</li>
  365. * </ol>
  366. *
  367. * @deprecated Use {@link #getParamsEncoding()} instead.
  368. */
  369. @Deprecated
  370. protected String getPostParamsEncoding() {
  371. return getParamsEncoding();
  372. }
  373.  
  374. /**
  375. * @deprecated Use {@link #getBodyContentType()} instead.
  376. */
  377. @Deprecated
  378. public String getPostBodyContentType() {
  379. return getBodyContentType();
  380. }
  381.  
  382. /**
  383. * Returns the raw POST body to be sent.
  384. *
  385. * @throws AuthFailureError In the event of auth failure
  386. *
  387. * @deprecated Use {@link #getBody()} instead.
  388. */
  389. @Deprecated
  390. public byte[] getPostBody() throws AuthFailureError {
  391. // Note: For compatibility with legacy clients of volley, this implementation must remain
  392. // here instead of simply calling the getBody() function because this function must
  393. // call getPostParams() and getPostParamsEncoding() since legacy clients would have
  394. // overridden these two member functions for POST requests.
  395. Map<String, String> postParams = getPostParams();
  396. if (postParams != null && postParams.size() > 0) {
  397. return encodeParameters(postParams, getPostParamsEncoding());
  398. }
  399. return null;
  400. }
  401.  
  402. Map<String, String> mParams = null;
  403.  
  404. /**
  405. * Returns a Map of parameters to be used for a POST or PUT request. Can throw
  406. * {@link AuthFailureError} as authentication may be required to provide these values.
  407. *
  408. * <p>Note that you can directly override {@link #getBody()} for custom data.</p>
  409. *
  410. * @throws AuthFailureError in the event of auth failure
  411. */
  412. protected Map<String, String> getParams() throws AuthFailureError {
  413. return mParams;
  414. }
  415.  
  416. public void setParams(Map<String, String> params){
  417. mParams = params;
  418. }
  419.  
  420. /**
  421. * Returns which encoding should be used when converting POST or PUT parameters returned by
  422. * {@link #getParams()} into a raw POST or PUT body.
  423. *
  424. * <p>This controls both encodings:
  425. * <ol>
  426. * <li>The string encoding used when converting parameter names and values into bytes prior
  427. * to URL encoding them.</li>
  428. * <li>The string encoding used when converting the URL encoded parameters into a raw
  429. * byte array.</li>
  430. * </ol>
  431. */
  432. protected String getParamsEncoding() {
  433. return DEFAULT_PARAMS_ENCODING;
  434. }
  435.  
  436. /**
  437. * Returns the content type of the POST or PUT body.
  438. */
  439. public String getBodyContentType() {
  440. return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
  441. }
  442.  
  443. /**
  444. * Returns the raw POST or PUT body to be sent.
  445. *
  446. * <p>By default, the body consists of the request parameters in
  447. * application/x-www-form-urlencoded format. When overriding this method, consider overriding
  448. * {@link #getBodyContentType()} as well to match the new body format.
  449. *
  450. * @throws AuthFailureError in the event of auth failure
  451. */
  452. public byte[] getBody() throws AuthFailureError {
  453. Map<String, String> params = getParams();
  454. if (params != null && params.size() > 0) {
  455. return encodeParameters(params, getParamsEncoding());
  456. }
  457. return null;
  458. }
  459.  
  460. /**
  461. * Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
  462. */
  463. private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
  464. StringBuilder encodedParams = new StringBuilder();
  465. try {
  466. for (Map.Entry<String, String> entry : params.entrySet()) {
  467. encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
  468. encodedParams.append('=');
  469. encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
  470. encodedParams.append('&');
  471. }
  472. return encodedParams.toString().getBytes(paramsEncoding);
  473. } catch (UnsupportedEncodingException uee) {
  474. throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
  475. }
  476. }
  477.  
  478. /**
  479. * Set whether or not responses to this request should be cached.
  480. *
  481. * @return This Request object to allow for chaining.
  482. */
  483. public final Request<?> setShouldCache(boolean shouldCache) {
  484. mShouldCache = shouldCache;
  485. return this;
  486. }
  487.  
  488. /**
  489. * Returns true if responses to this request should be cached.
  490. */
  491. public final boolean shouldCache() {
  492. return mShouldCache;
  493. }
  494.  
  495. /**
  496. * Priority values. Requests will be processed from higher priorities to
  497. * lower priorities, in FIFO order.
  498. */
  499. public enum Priority {
  500. LOW,
  501. NORMAL,
  502. HIGH,
  503. IMMEDIATE
  504. }
  505.  
  506. /**
  507. * Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default.
  508. */
  509. public Priority getPriority() {
  510. return Priority.NORMAL;
  511. }
  512.  
  513. /**
  514. * Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
  515. * per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
  516. * attempts remaining, this will cause delivery of a {@link TimeoutError} error.
  517. */
  518. public final int getTimeoutMs() {
  519. return mRetryPolicy.getCurrentTimeout();
  520. }
  521.  
  522. /**
  523. * Returns the retry policy that should be used for this request.
  524. */
  525. public RetryPolicy getRetryPolicy() {
  526. return mRetryPolicy;
  527. }
  528.  
  529. /**
  530. * Mark this request as having a response delivered on it. This can be used
  531. * later in the request's lifetime for suppressing identical responses.
  532. */
  533. public void markDelivered() {
  534. mResponseDelivered = true;
  535. }
  536.  
  537. /**
  538. * Returns true if this request has had a response delivered for it.
  539. */
  540. public boolean hasHadResponseDelivered() {
  541. return mResponseDelivered;
  542. }
  543.  
  544. /**
  545. * Subclasses must implement this to parse the raw network response
  546. * and return an appropriate response type. This method will be
  547. * called from a worker thread. The response will not be delivered
  548. * if you return null.
  549. * @param response Response from the network
  550. * @return The parsed response, or null in the case of an error
  551. */
  552. abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
  553.  
  554. /**
  555. * Subclasses can override this method to parse 'networkError' and return a more specific error.
  556. *
  557. * <p>The default implementation just returns the passed 'networkError'.</p>
  558. *
  559. * @param volleyError the error retrieved from the network
  560. * @return an NetworkError augmented with additional information
  561. */
  562. protected VolleyError parseNetworkError(VolleyError volleyError) {
  563. return volleyError;
  564. }
  565.  
  566. /**
  567. * Subclasses must implement this to perform delivery of the parsed
  568. * response to their listeners. The given response is guaranteed to
  569. * be non-null; responses that fail to parse are not delivered.
  570. * @param response The parsed response returned by
  571. * {@link #parseNetworkResponse(NetworkResponse)}
  572. */
  573. abstract protected void deliverResponse(T response);
  574.  
  575. /**
  576. * Delivers error message to the ErrorListener that the Request was
  577. * initialized with.
  578. *
  579. * @param error Error details
  580. */
  581. public void deliverError(VolleyError error) {
  582. if (mErrorListener != null) {
  583. mErrorListener.onErrorResponse(error);
  584. }
  585. }
  586.  
  587. /**
  588. * Our comparator sorts from high to low priority, and secondarily by
  589. * sequence number to provide FIFO ordering.
  590. */
  591. @Override
  592. public int compareTo(Request<T> other) {
  593. Priority left = this.getPriority();
  594. Priority right = other.getPriority();
  595.  
  596. // High-priority requests are "lesser" so they are sorted to the front.
  597. // Equal priorities are sorted by sequence number to provide FIFO ordering.
  598. return left == right ?
  599. this.mSequence - other.mSequence :
  600. right.ordinal() - left.ordinal();
  601. }
  602.  
  603. @Override
  604. public String toString() {
  605. String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
  606. return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
  607. + getPriority() + " " + mSequence;
  608. }
  609. }

使用示例:

  1. StringRequest postStringRequest = new StringRequest(Method.POST, "http://m.weather.com.cn/data/101010100.html",
  2. new ResponseListener(), null);
  3. Map<String, String> map = new HashMap<String, String>();
  4. map.put("params1", "value1");
  5. map.put("params2", "value2");
  6. postStringRequest.setParams(map);
  7. mQueue.add(postStringRequest);

结果:

三、使用JsonObjectRequest接收Json类型的响应

类似于StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。

3.1 构造函数

  1. /**
  2. * Creates a new request.
  3. * @param method the HTTP method to use
  4. * @param url URL to fetch the JSON from
  5. * @param jsonRequest A {@link JSONObject} to post with the request. Null is allowed and
  6. * indicates no parameters will be posted along with request.
  7. * @param listener Listener to receive the JSON response
  8. * @param errorListener Error listener, or null to ignore errors.
  9. */
  10. public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
         Listener<JSONObject> listener, ErrorListener errorListener) {
  11. super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
  12. errorListener);
  13. }
  14.  
  15. /**
  16. * Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is
  17. * <code>null</code>, <code>POST</code> otherwise.
  18. *
  19. * @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener)
  20. */
  21. public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,
  22. ErrorListener errorListener) {
  23. this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
  24. listener, errorListener);
  25. }

3.2 发送请求

和之前讲过的StringRequest一样,可以传入请求的类型,如果没传就默认是GET请求。参数也是如出一辙,就是泛型变了下。定义和使用的方式也完全一致,初始化对象后,添加到请求队列即可。

  1. JsonObjectRequest request = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html",
  2. null, new ResponseListener(), new ResponseErrorListener());
  3. mQueue.add(request);
  1. /**
  2. * @author:Jack Tony
  3. * @description :设置响应结果监听器,这里用的是JsonObjectRequest,所以返回的结果是JSONObject
  4. * @date :2015年1月24日
  5. */
  6. private class ResponseListener implements Response.Listener<JSONObject> {
  7.  
  8. @Override
  9. public void onResponse(JSONObject response) {
  10. // TODO 自动生成的方法存根
  11. Log.d("TAG", "-------------\n" + response.toString());
  12. }
  13.  
  14. }
  15.  
  16. /**
  17. * @author:Jack Tony
  18. * @description :访问出错时触发的监听器
  19. * @date :2015年1月28日
  20. */
  21. private class ResponseErrorListener implements Response.ErrorListener {
  22.  
  23. @Override
  24. public void onErrorResponse(VolleyError error) {
  25. Log.e("TAG", error.getMessage(), error);
  26. }
  27. }

结果:

你怎么查看解析是否成功了呢?服务器端的数据:

  1. {"weatherinfo":{"city":"北京","city_en":"beijing","date_y":"2014年3月4日","date":"","week":"星期二","fchh":"11","cityid":"101010100","temp1":"8℃~-3℃","temp2":"8℃~-3℃","temp3":"7℃~-3℃","temp4":"8℃~-1℃","temp5":"10℃~1℃","temp6":"10℃~2℃","tempF1":"46.4℉~26.6℉","tempF2":"46.4℉~26.6℉","tempF3":"44.6℉~26.6℉","tempF4":"46.4℉~30.2℉","tempF5":"50℉~33.8℉","tempF6":"50℉~35.6℉","weather1":"晴","weather2":"晴","weather3":"晴","weather4":"晴转多云","weather5":"多云","weather6":"多云","img1":"0","img2":"99","img3":"0","img4":"99","img5":"0","img6":"99","img7":"0","img8":"1","img9":"1","img10":"99","img11":"1","img12":"99","img_single":"0","img_title1":"晴","img_title2":"晴","img_title3":"晴","img_title4":"晴","img_title5":"晴","img_title6":"晴","img_title7":"晴","img_title8":"多云","img_title9":"多云","img_title10":"多云","img_title11":"多云","img_title12":"多云","img_title_single":"晴","wind1":"北风4-5级转微风","wind2":"微风","wind3":"微风","wind4":"微风","wind5":"微风","wind6":"微风","fx1":"北风","fx2":"微风","fl1":"4-5级转小于3级","fl2":"小于3级","fl3":"小于3级","fl4":"小于3级","fl5":"小于3级","fl6":"小于3级","index":"寒冷","index_d":"天气寒冷,建议着厚羽绒服、毛皮大衣加厚毛衣等隆冬服装。年老体弱者尤其要注意保暖防冻。","index48":"冷","index48_d":"天气冷,建议着棉服、羽绒服、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣、冬大衣或厚羽绒服。","index_uv":"中等","index48_uv":"中等","index_xc":"较适宜","index_tr":"一般","index_co":"较舒适","st1":"7","st2":"-3","st3":"8","st4":"0","st5":"7","st6":"-1","index_cl":"较不宜","index_ls":"基本适宜","index_ag":"易发"}}

如果解析错误,就会出现警告,这时错误监听器就会被触发:

如果解析成功,就不会出现错误,这就是泛型的好处,保证了程序的正确性。

最终我们就可以在Response.Listener<JSONObject>中得到JSONObject对象,通过这个对象就能进行下一步的处理了。

3.3 解析Json

比如要解析出上面Json数据中city的字段,就可以按照如下方式编码:

  1.      try {
  2. response = response.getJSONObject("weatherinfo");
  3. Log.i(TAG, "City = " + response.getString("city"));
  4. } catch (JSONException e) {
  5. // TODO 自动生成的 catch 块
  6. e.printStackTrace();
  7. }

完整监听器代码:

  1. private class ResponseListener implements Response.Listener<JSONObject> {
  2.  
  3. @Override
  4. public void onResponse(JSONObject response) {
  5. // TODO 自动生成的方法存根
  6. Log.d("TAG", "-------------\n" + response.toString());
  7.  
  8. try {
  9. response = response.getJSONObject("weatherinfo");
  10. Log.i(TAG, "City = " + response.getString("city"));
  11. } catch (JSONException e) {
  12. // TODO 自动生成的 catch 块
  13. e.printStackTrace();
  14. }
  15. }
  16.  
  17. }

结果:

四、JsonArrayRequest简介

除此之外,还有一个相关的响应对象叫做JsonArrayRequest,这个获得的就是一个Json序列,使用方式没有任何改变,这里就不做过多介绍了,因为剩下的就是Json的知识了,和Volley没有任何关系。

源码:

  1. /*
  2. * Copyright (C) 2011 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16.  
  17. package com.android.volley.toolbox;
  18.  
  19. import com.android.volley.NetworkResponse;
  20. import com.android.volley.ParseError;
  21. import com.android.volley.Response;
  22. import com.android.volley.Response.ErrorListener;
  23. import com.android.volley.Response.Listener;
  24.  
  25. import org.json.JSONArray;
  26. import org.json.JSONException;
  27.  
  28. import java.io.UnsupportedEncodingException;
  29.  
  30. /**
  31. * A request for retrieving a {@link JSONArray} response body at a given URL.
  32. */
  33. public class JsonArrayRequest extends JsonRequest<JSONArray> {
  34.  
  35. /**
  36. * Creates a new request.
  37. * @param url URL to fetch the JSON from
  38. * @param listener Listener to receive the JSON response
  39. * @param errorListener Error listener, or null to ignore errors.
  40. */
  41. public JsonArrayRequest(String url, Listener<JSONArray> listener, ErrorListener errorListener) {
  42. super(Method.GET, url, null, listener, errorListener);
  43. }
  44.  
  45. @Override
  46. protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
  47. try {
  48. String jsonString =
  49. new String(response.data, HttpHeaderParser.parseCharset(response.headers));
  50. return Response.success(new JSONArray(jsonString),
  51. HttpHeaderParser.parseCacheHeaders(response));
  52. } catch (UnsupportedEncodingException e) {
  53. return Response.error(new ParseError(e));
  54. } catch (JSONException je) {
  55. return Response.error(new ParseError(je));
  56. }
  57. }
  58. }

通过源码我们知道,这个响应对象发送的请求是Get,而且它是继承自JsonRequest,如果你想用POST来做,自行添加新的构造函数即可。

四、请求取消

      Activity里面启动了网络请求,而在这个网络请求还没返回结果的时候,Activity被结束了,此时如果继续使用其中的Context等,除了无辜的浪费CPU,电池,网络等资源,有可能还会导致程序crash,所以,我们需要处理这种一场情况。使用Volley的话,我们可以在Activity停止的时候,同时取消所有或部分未完成的网络请求。Volley里所有的请求结果会返回给主进程,如果在主进程里取消了某些请求,则这些请求将不会被返回给主线程。Volley支持多种request取消方式。
1)可以针对某些个request做取消操作:

  1. @Override
  2. public void onStop() {
  3. for (Request <?> req : mRequestQueue) {
  4. req.cancel();
  5. }
  6. }
 

2)取消这个队列里的所有请求:

  1. @Override
  2. protected void onStop() {
  3. // TODO Auto-generated method stub
  4. super.onStop();
  5. mRequestQueue.cancelAll(this);
  6. }

3)可以根据RequestFilter或者Tag来终止某些请求

给请求设置标签:

如果你想取消所有的请求,在onStop方法中添加如下代码:

  1. @Override
  2. protected void onStop() {
  3. super.onStop();
  4. mRequestQueue.cancelAll(new RequestQueue.RequestFilter() {
  5. @Override
  6. public boolean apply(Request<?> request) {
  7. // do I have to cancel this?
  8. return true; // -> always yes
  9. }
  10. });
  11. }

这样你就不必担心在onResponse被调用的时候用户已经销毁Activity。这种情况下会抛出NullPointerException 异。但是post请求则需要继续,即使用户已经改变了Activity。我们可以通过使用tag来做到,在构造GET请求的时候,添加一个tag给它。

  1. // after declaring your request
  2. request.setTag("My Tag");
  3. mRequestQueue.add(request);

如果要取消所有指定标记My Tag的请求,只需简单的添加下面的一行代码:

  1. mRequestQueue.cancelAll("My Tag");

这样你就只会取消My Tag请求,让其它请求不受影响。注意你必须手动在销毁的Activity中处理这种情况。

  1. @Override
  2. rotected void onStop() {
  3. // TODO Auto-generated method stub
  4. super.onStop();
  5.  
  6. mRequestQueue.cancelAll( new RequestFilter() {});
  7. or
  8. mRequestQueue.cancelAll(new Object());

 Volley源码下载:http://download.csdn.net/detail/shark0017/8404451

Volley(二)—— 基本Request对象 & RequestQueue&请求取消的更多相关文章

  1. request对象实现请求转发

    request对象实现请求转发,请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理.request对象提供了一个getRequestDispatcher方法,该方法返 ...

  2. 详细解读Volley(一)—— 基本Request对象 & RequestQueue

    Volley它非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕.所以不建议用它去进行下载文件.加载大图的操作.有人可能会问,如 ...

  3. 常用Request对象获取请求信息

    Request.ServerVariables(“REMOTE_ADDR”) ‘获取访问IPRequest.ServerVariables(“LOCAL_ADDR”) ‘同上Request.Serve ...

  4. Request对象实现请求转发区别

    servlet请求转发与重定向的区别: request.setAttribute("test","hello"); request.getRequestDisp ...

  5. 通过Request对象获取请求的IP地址

    /** * 标识要从哪些消息头中获取IP地址 */ private static final String[] getIpArray = {"HTTP_X_FORWARDED_FOR&quo ...

  6. java后端通过request对象获取请求的ip地址工具类

    package cn.zgjkw.battalion.util; import org.apache.log4j.Logger; import javax.servlet.http.HttpServl ...

  7. Django整理(五) - 请求与响应 - request对象

    请求对象 一.客户端传参的几种方式 1. 通过URL路径(path)传递,例如:http://127.0.0.1:8000/news/1/2,两个参数:id和page 2. 通过 query stri ...

  8. request 对象和 response 对象

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象 HttpServletResponse HttpServletR ...

  9. Asp.net内置对象之Request对象(概述及应用)

    Request对象主要用于获取来自客户端的数据,如用户填入表单的数据.保存在客户端的Cookie等,本文将围绕Request对象,讲解其的主要作用:读取窗体变量.读取查询字符串变量.取得Web服务器端 ...

随机推荐

  1. LayoutInflater的infalte()

    其中: resource:是布局文件ID root:是父ViewGroup对象, attachToRoot:是是否将“翻译”出来的View添加到上面的root中 root和attachToRoot是共 ...

  2. Android之手机向导以及设置中心模块的开发

    当我们使用的新的软件的时候,我们首先需要教用户如何使用我们的软件,当用户学习完使用教程,下次再登录的时候,我们应该直接跳到我们的功能界面,下面我来展示一下我学习视频做的效果图:手机防盗这个功能模块就是 ...

  3. Android Studio no debuggable applications解决方案

    android studio 默认是没有开启debuggable 功能的,在tools里打开该功能即可,Tools->Android->Enable ADB Integration. 刚设 ...

  4. 【C语言】外部函数和内部函数

    目录 [外部函数]  [内部函数] 1.外部函数  定义的函数能被本文件和其它文件访问(默认). 注:不允许有同名的外部函数. 2.内部函数  定义的函数只能被本文件访问,其它文件不能访问. 注:允许 ...

  5. 【代码笔记】iOS-判断textField里面是否有空

    一,效果图. 二,工程图. 三,代码. ViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional ...

  6. 使用 PHPMailer 发送邮件

    转载 http://blog.csdn.net/liruxing1715/article/details/7914974 PHPMailer 的官方网站:http://phpmailer.worxwa ...

  7. SQLServer安装正常服务启动正常却无法连接

    最近给服务器安装sql2008R2版本,发现一个抓狂的问题,我自己觉得,用sql多年,从2005版本到2012版本都从安装到使用都很熟练,竟然被2008版本难住了 给服务器正常安装,sql2008r2 ...

  8. [Linux 性能检测工具]VMSTAT

    VMSTAT NAME:          Vmstat: 报告虚拟内存统计 语法 :        vmstat [-a] [-n] [-t] [-S unit] [delay [ count]] ...

  9. C++/CLI——读书笔记《Visual C++/CLI从入门到精通》 第Ⅳ部分

    =================================版权声明================================= 版权声明:本文为博主原创文章 未经许可不得转载  请通过右 ...

  10. Java查询大文本

    但JAVA本身缺少相应的类库,需要硬编码才能实现结构化文件计算,代码复杂且可读性差,难以实现高效的并行处理. 使用免费的集算器可以弥补这一不足.集算器封装了丰富的结构化文件读写和游标计算函数,书写简单 ...