Volley是Android开发者新的瑞士军刀,它提供了优美的框架,使得Android应用程序网络访问更容易和更快。Volley抽象实现了底层的HTTP Client库,让你不关注HTTP Client细节,专注于写出更加漂亮、干净的RESTful HTTP请求。另外,Volley请求会异步执行,不阻挡主线程。

Volley提供的功能

简单的讲,提供了如下主要的功能:

1、封装了的异步的RESTful 请求API;

2、一个优雅和稳健的请求队列;

3、一个可扩展的架构,它使开发人员能够实现自定义的请求和响应处理机制;

4、能够使用外部HTTP Client库;

5、缓存策略;

6、自定义的网络图像加载视图(NetworkImageView,ImageLoader等);

为什么使用异步HTTP请求?

Android中要求HTTP请求异步执行,如果在主线程执行HTTP请求,可能会抛出android.os.NetworkOnMainThreadException  异常。阻塞主线程有一些严重的后果,它阻碍UI渲染,用户体验不流畅,它可能会导致可怕的ANR(Application Not Responding)。要避免这些陷阱,作为一个开发者,应该始终确保HTTP请求是在一个不同的线程。

怎样使用Volley

这篇博客将会详细的介绍在应用程程中怎么使用volley,它将包括一下几方面:

1、安装和使用Volley库

2、使用请求队列

3、异步的JSON、String请求

4、取消请求

5、重试失败的请求,自定义请求超时

6、设置请求头(HTTP headers)

7、使用Cookies

8、错误处理

安装和使用Volley库

引入Volley非常简单,首先,从git库先克隆一个下来:

  1. git clone https://android.googlesource.com/platform/frameworks/volley

然后编译为jar包,再把jar包放到自己的工程的libs目录。

使用请求队列

Volley的所有请求都放在一个队列,然后进行处理,这里是你如何将创建一个请求队列:

  1. RequestQueue mRequestQueue = Volley.newRequestQueue(this); // 'this' is Context

理想的情况是把请求队列集中放到一个地方,最好是初始化应用程序类中初始化请求队列,下面类做到了这一点:

  1. public class ApplicationController extends Application {
  2.  
  3. /**
  4. * Log or request TAG
  5. */
  6. public static final String TAG = "VolleyPatterns";
  7.  
  8. /**
  9. * Global request queue for Volley
  10. */
  11. private RequestQueue mRequestQueue;
  12.  
  13. /**
  14. * A singleton instance of the application class for easy access in other places
  15. */
  16. private static ApplicationController sInstance;
  17.  
  18. @Override
  19. public void onCreate() {
  20. super.onCreate();
  21.  
  22. // initialize the singleton
  23. sInstance = this;
  24. }
  25.  
  26. /**
  27. * @return ApplicationController singleton instance
  28. */
  29. public static synchronized ApplicationController getInstance() {
  30. return sInstance;
  31. }
  32.  
  33. /**
  34. * @return The Volley Request queue, the queue will be created if it is null
  35. */
  36. public RequestQueue getRequestQueue() {
  37. // lazy initialize the request queue, the queue instance will be
  38. // created when it is accessed for the first time
  39. if (mRequestQueue == null) {
  40. mRequestQueue = Volley.newRequestQueue(getApplicationContext());
  41. }
  42.  
  43. return mRequestQueue;
  44. }
  45.  
  46. /**
  47. * Adds the specified request to the global queue, if tag is specified
  48. * then it is used else Default TAG is used.
  49. *
  50. * @param req
  51. * @param tag
  52. */
  53. public <T> void addToRequestQueue(Request<T> req, String tag) {
  54. // set the default tag if tag is empty
  55. req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
  56.  
  57. VolleyLog.d("Adding request to queue: %s", req.getUrl());
  58.  
  59. getRequestQueue().add(req);
  60. }
  61.  
  62. /**
  63. * Adds the specified request to the global queue using the Default TAG.
  64. *
  65. * @param req
  66. * @param tag
  67. */
  68. public <T> void addToRequestQueue(Request<T> req) {
  69. // set the default tag if tag is empty
  70. req.setTag(TAG);
  71.  
  72. getRequestQueue().add(req);
  73. }
  74.  
  75. /**
  76. * Cancels all pending requests by the specified TAG, it is important
  77. * to specify a TAG so that the pending/ongoing requests can be cancelled.
  78. *
  79. * @param tag
  80. */
  81. public void cancelPendingRequests(Object tag) {
  82. if (mRequestQueue != null) {
  83. mRequestQueue.cancelAll(tag);
  84. }
  85. }
  86. }

异步的JSON、String请求

Volley提供了以下的实用工具类进行异步HTTP请求:

  • JsonObjectRequest — To send and receive JSON Object from the Server
  • JsonArrayRequest — To receive JSON Array from the Server
  • StringRequest — To retrieve response body as String (ideally if you intend to parse the response by yourself)

JsonObjectRequest

这个类可以用来发送和接收JSON对象。这个类的一个重载构造函数允许设置适当的请求方法(DELETE,GET,POST和PUT)。如果您正在使用一个RESTful服务端,可以使用这个类。下面的示例显示如何使GET和POST请求。

GET请求:

  1. final String URL = "/volley/resource/12";
  2. // pass second argument as "null" for GET requests
  3. JsonObjectRequest req = new JsonObjectRequest(URL, null,
  4. new Response.Listener<JSONObject>() {
  5. @Override
  6. public void onResponse(JSONObject response) {
  7. try {
  8. VolleyLog.v("Response:%n %s", response.toString(4));
  9. } catch (JSONException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. }, new Response.ErrorListener() {
  14. @Override
  15. public void onErrorResponse(VolleyError error) {
  16. VolleyLog.e("Error: ", error.getMessage());
  17. }
  18. });
  19.  
  20. // add the request object to the queue to be executed
  21. ApplicationController.getInstance().addToRequestQueue(req);

POST请求:

  1. final String URL = "/volley/resource/12";
  2. // Post params to be sent to the server
  3. HashMap<String, String> params = new HashMap<String, String>();
  4. params.put("token", "AbCdEfGh123456");
  5.  
  6. JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
  7. new Response.Listener<JSONObject>() {
  8. @Override
  9. public void onResponse(JSONObject response) {
  10. try {
  11. VolleyLog.v("Response:%n %s", response.toString(4));
  12. } catch (JSONException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }, new Response.ErrorListener() {
  17. @Override
  18. public void onErrorResponse(VolleyError error) {
  19. VolleyLog.e("Error: ", error.getMessage());
  20. }
  21. });
  22.  
  23. // add the request object to the queue to be executed
  24. ApplicationController.getInstance().addToRequestQueue(req);

JsonArrayRequest

这个类可以用来接受 JSON Arrary,不支持JSON Object。这个类现在只支持 HTTP GET。由于支持GET,你可以在URL的后面加上请求参数。类的构造函数不支持请求参数。

  1. final String URL = "/volley/resource/all?count=20";
  2. JsonArrayRequest req = new JsonArrayRequest(URL, new Response.Listener<JSONArray> () {
  3. @Override
  4. public void onResponse(JSONArray response) {
  5. try {
  6. VolleyLog.v("Response:%n %s", response.toString(4));
  7. } catch (JSONException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }, new Response.ErrorListener() {
  12. @Override
  13. public void onErrorResponse(VolleyError error) {
  14. VolleyLog.e("Error: ", error.getMessage());
  15. }
  16. });
  17.  
  18. // add the request object to the queue to be executed
  19. ApplicationController.getInstance().addToRequestQueue(req);

StringRequest

这个类可以用来从服务器获取String,如果想自己解析请求响应可以使用这个类,例如返回xml数据。它还可以使用重载的构造函数定制请求。

  1. final String URL = "/volley/resource/recent.xml";
  2. StringRequest req = new StringRequest(URL, new Response.Listener<String>() {
  3. @Override
  4. public void onResponse(String response) {
  5. VolleyLog.v("Response:%n %s", response);
  6. }
  7. }, new Response.ErrorListener() {
  8. @Override
  9. public void onErrorResponse(VolleyError error) {
  10. VolleyLog.e("Error: ", error.getMessage());
  11. }
  12. });
  13.  
  14. // add the request object to the queue to be executed
  15. ApplicationController.getInstance().addToRequestQueue(req);

取消请求

Volley提供了强大的API取消未处理或正在处理的请求。取消请求最简单的方法是调用请求队列cancelAll(tag)的方法,前提是你在添加请求时设置了标记。这样就能使标签标记的请求挂起。

给请求设置标签:

  1. request.setTag("My Tag");

使用ApplicationController添加使用了标签的请求到队列中:

  1. ApplicationController.getInstance().addToRequestQueue(request, "My Tag");

取消所有指定标记的请求:

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

重试失败的请求,自定义请求超时

Volley中没有指定的方法来设置请求超时时间,可以设置RetryPolicy 来变通实现。DefaultRetryPolicy类有个initialTimeout参数,可以设置超时时间。要确保最大重试次数为1,以保证超时后不重新请求。

Setting Request Timeout

  1. 1
  1. request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
 

如果你想失败后重新请求(因超时),您可以指定使用上面的代码,增加重试次数。注意最后一个参数,它允许你指定一个退避乘数可以用来实现“指数退避”来从RESTful服务器请求数据。

设置请求头(HTTP headers)

有时候需要给HTTP请求添加额外的头信息,一个常用的例子是添加 “Authorization”到HTTP 请求的头信息。Volley请求类提供了一个 getHeaers()的方法,重载这个方法可以自定义HTTP 的头信息。

添加头信息:

  1. JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
  2. new Response.Listener<JSONObject>() {
  3. @Override
  4. public void onResponse(JSONObject response) {
  5. // handle response
  6. }
  7. }, new Response.ErrorListener() {
  8. @Override
  9. public void onErrorResponse(VolleyError error) {
  10. // handle error
  11. }
  12. }) {
  13.  
  14. @Override
  15. public Map<String, String> getHeaders() throws AuthFailureError {
  16. HashMap<String, String> headers = new HashMap<String, String>();
  17. headers.put("CUSTOM_HEADER", "Yahoo");
  18. headers.put("ANOTHER_CUSTOM_HEADER", "Google");
  19. return headers;
  20. }
  21. };

使用Cookies

Volley中没有直接的API来设置cookies,Volley的设计理念就是提供干净、简洁的API来实现RESTful HTTP请求,不提供设置cookies是合理的。

下面是修改后的ApplicationController类,这个类修改了getRequestQueue()方法,包含了 设置cookie方法,这些修改还是有些粗糙。

  1. // http client instance
  2. private DefaultHttpClient mHttpClient;
  3. public RequestQueue getRequestQueue() {
  4. // lazy initialize the request queue, the queue instance will be
  5. // created when it is accessed for the first time
  6. if (mRequestQueue == null) {
  7. // Create an instance of the Http client.
  8. // We need this in order to access the cookie store
  9. mHttpClient = new DefaultHttpClient();
  10. // create the request queue
  11. mRequestQueue = Volley.newRequestQueue(this, new HttpClientStack(mHttpClient));
  12. }
  13. return mRequestQueue;
  14. }
  15.  
  16. /**
  17. * Method to set a cookie
  18. */
  19. public void setCookie() {
  20. CookieStore cs = mHttpClient.getCookieStore();
  21. // create a cookie
  22. cs.addCookie(new BasicClientCookie2("cookie", "spooky"));
  23. }
  24.  
  25. // add the cookie before adding the request to the queue
  26. setCookie();
  27.  
  28. // add the request to the queue
  29. mRequestQueue.add(request);

错误处理

正如前面代码看到的,在创建一个请求时,需要添加一个错误监听onErrorResponse。如果请求发生异常,会返回一个VolleyError实例。

以下是Volley的异常列表:

AuthFailureError:如果在做一个HTTP的身份验证,可能会发生这个错误。

NetworkError:Socket关闭,服务器宕机,DNS错误都会产生这个错误。

NoConnectionError:和NetworkError类似,这个是客户端没有网络连接。

ParseError:在使用JsonObjectRequest或JsonArrayRequest时,如果接收到的JSON是畸形,会产生异常。

SERVERERROR:服务器的响应的一个错误,最有可能的4xx或5xx HTTP状态代码。

TimeoutError:Socket超时,服务器太忙或网络延迟会产生这个异常。默认情况下,Volley的超时时间为2.5秒。如果得到这个错误可以使用RetryPolicy。

可以使用一个简单的Help类根据这些异常提示相应的信息:

  1. public class VolleyErrorHelper {
  2. /**
  3. * Returns appropriate message which is to be displayed to the user
  4. * against the specified error object.
  5. *
  6. * @param error
  7. * @param context
  8. * @return
  9. */
  10. public static String getMessage(Object error, Context context) {
  11. if (error instanceof TimeoutError) {
  12. return context.getResources().getString(R.string.generic_server_down);
  13. }
  14. else if (isServerProblem(error)) {
  15. return handleServerError(error, context);
  16. }
  17. else if (isNetworkProblem(error)) {
  18. return context.getResources().getString(R.string.no_internet);
  19. }
  20. return context.getResources().getString(R.string.generic_error);
  21. }
  22.  
  23. /**
  24. * Determines whether the error is related to network
  25. * @param error
  26. * @return
  27. */
  28. private static boolean isNetworkProblem(Object error) {
  29. return (error instanceof NetworkError) || (error instanceof NoConnectionError);
  30. }
  31. /**
  32. * Determines whether the error is related to server
  33. * @param error
  34. * @return
  35. */
  36. private static boolean isServerProblem(Object error) {
  37. return (error instanceof ServerError) || (error instanceof AuthFailureError);
  38. }
  39. /**
  40. * Handles the server error, tries to determine whether to show a stock message or to
  41. * show a message retrieved from the server.
  42. *
  43. * @param err
  44. * @param context
  45. * @return
  46. */
  47. private static String handleServerError(Object err, Context context) {
  48. VolleyError error = (VolleyError) err;
  49.  
  50. NetworkResponse response = error.networkResponse;
  51.  
  52. if (response != null) {
  53. switch (response.statusCode) {
  54. case 404:
  55. case 422:
  56. case 401:
  57. try {
  58. // server might return error like this { "error": "Some error occured" }
  59. // Use "Gson" to parse the result
  60. HashMap<String, String> result = new Gson().fromJson(new String(response.data),
  61. new TypeToken<Map<String, String>>() {
  62. }.getType());
  63.  
  64. if (result != null && result.containsKey("error")) {
  65. return result.get("error");
  66. }
  67.  
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. }
  71. // invalid request
  72. return error.getMessage();
  73.  
  74. default:
  75. return context.getResources().getString(R.string.generic_server_down);
  76. }
  77. }
  78. return context.getResources().getString(R.string.generic_error);
  79. }
  80. }

总结:

Volley是一个非常好的库,你可以尝试使用一下,它会帮助你简化网络请求,带来更多的益处。

我也希望更加全面的介绍Volley,以后可能会介绍使用volley加载图像的内容,欢迎关注。

谢谢你的阅读,希望你能喜欢。

参考:

/**
* @author 张兴业
*  iOS入门群:83702688
*  android开发进阶群:241395671
*  我的新浪微博:@张兴业TBOW
*/

原文:

http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/

Asynchronous HTTP Requests in Android Using Volley的更多相关文章

  1. Android - 使用Volley请求网络数据

    Android - 使用Volley请求网络数据 Android L : Android Studio 14 个人使用volley的小记,简述使用方法,不涉及volley源码 准备工作 导入Volle ...

  2. How to make asynchronous HTTP requests in PHP 4种PHP异步执行的常用方式

    [是否等待返回就执行下一步] How to make asynchronous HTTP requests in PHP - Stack Overflow https://stackoverflow. ...

  3. Android网络通信Volley框架源代码浅析(三)

    尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析.相信大家对于Volley有了初步的认识,可是假设想更深入的理解 ...

  4. Android网络通信Volley框架源代码浅析(一)

    尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天開始,我打算为大家呈现关于Volley框架的源代码分析的文章,Volley ...

  5. android之volley学习

    Volley是android的平台通信库,一个新的网络通信框架.Volley 的特点:适合数据量小,通信频繁的网络操作. 获取Volley git 工具使用:git clone https://and ...

  6. android之Volley实现瀑布流

    1.首先我们来看下主布局文件activity_main.xml. <RelativeLayout xmlns:android="http://schemas.android.com/a ...

  7. 【Android】Volley做网络请求的几种用法

    前言: 最近在将自己写的烂代码重构,以前使用的网络请求全是基于apache的HttpClient,简单使用还好,使用多了发现重复代码太多,而且每次使用都很繁琐,因此在网上找了半天网络请求的相关类库,最 ...

  8. Android库Volley的使用介绍

    Android Volley 是Google开发的一个网络lib,可以让你更加简单并且快速的访问网络数据.Volley库的网络请求都是异步的,你不必担心异步处理问题. Volley的优点: 请求队列和 ...

  9. Android 使Volley完美支持自定义证书的Https

    其实在最早的版本里,Volley甚至是不支持https协议的,只能跑http,当然你也可以自己修改他的源码让他支持,如今volley的代码经过一些改进以后, 已经可以完美支持https协议了,无论是在 ...

随机推荐

  1. 新建 jsp异常,The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path

    新项目,新建jsp页面的时候报异常: Multiple annotations found at this line: - The superclass "javax.servlet.htt ...

  2. 2013夏,iDempiere来了 - v1.0c Installers (Devina LTS Release) 2013-06-27

    怀揣着为中小企业量身定做一整套开源软件解决方案的梦想开始了一个网站的搭建.http://osssme.org/ iDempiere来了 - v1.0c Installers (Devina LTS R ...

  3. Node.js搭建本地web服务(转)

    http://www.cnblogs.com/wangfupeng1988/p/4143996.html https://github.com/finderL/webserver

  4. worker

    <script id="worker" type="app/worker"> addEventListener('message', functio ...

  5. Springboot client 常用配置详解

    Property name Description Default value spring.boot.admin.client.enabled Enables the Spring Boot Adm ...

  6. 点击按钮,实现两个td值互换

    <body> <table id="table1"> <tr> <td>第一个单元格</td> <td>第二 ...

  7. 使用7za压缩zip包的命令,当中屏蔽部分文件夹内容

    7za.exe a -r  "D:\paages\prduct\produtConfig.zip" "E:/tm\packaes\poduct" -xr!doc ...

  8. myeclipse之完全破解

    并不是所有的破解都是成功的,就如并不是所有的战争都会胜利一样,我们在做事情的时候,总会遇到些问题,比如Activate不成功,需要手动激活. 激活不成功就是不成功,来回的破解.卸载.重装,都还是不可能 ...

  9. eclipse不自动弹出提示的解决办法(eclipse alt+/快捷键失效)centos 6.7

    1.次方法用于没有一点提示的情况:依次打开eclipse上面的windows ——preferences ——java ——editor —— content assist ,在右上方有一行“sele ...

  10. Unix系统编程(五)read系统调用

    read系统调用从文件描述符fd所指代的打开文件中读取数据. ssize_t read(int fd, void *buffer, size_t count); count参数指定最多能读取的字节数, ...