Android网络请求通信之Volley

一、Volley简介

Volley网络框架是Google公司在2013年发布的一款Android平台上的网络请求通信库。以下是对Volley的简单归纳。

Volley的优点

  1. 使网络通信更快、更简单、更健壮,用Volley开发的话,开发效率会得到很大提升,开发出来的网络模块的稳定性也会非常高
  2. Get、Post网络请求及网络图像的高效率异步处理请求,Volley帮我们实现了网络请求的异步化,而且它的Get和Post请求也是非常高效的
  3. 对网络请求进行排序、优先级处理
  4. 网络请求的缓存,当网络比较缓慢时或网络情况不太好的时候,Volley可以将我们上次请求的数据进行简单的缓存,提高用户体验
  5. Volley具有多级别取消请求,当我们有多个请求在同时进行的时候,可以做到同时取消这些请求的操作,非常的方便
  6. 和Activity生命周期的联动,当Activity结束、销毁的时候,可以同时取消网络请求的操作,避免APP在后台继续进行网络请求操作,导致APP性能和用户体验都非常差

Volley的缺点

  Volley不适合文件的上传和下载,当我们有上传和下载的需求的时候,可以考虑其他框架

为什么要使用Volley:

  1. 有高效的Get/Post方式的数据请求交互(效率非常高)
  2. 网络图片的加载和缓存(不使用Volley的情况下进行网络图片的处理会非常的麻烦,而且非常容易造成内存溢出,如果使用Volley,可以自动为图片进行缓存,不但节省流量,而且提高APP的性能)
  3. 是Google官方推出的针对Android平台的专用的网络通信库,Google的团队网络请求这部分优化的是非常好的,非常权威
  4. 性能很稳定,效率很强劲

使用Volley从服务器端获取数据的几种方式

  1. StringRequest:对请求返回的数据类型不确定的情况下使用,涵盖了后面的两种请求对象
  2. JsonObjectRequest:确定请求返回的数据类型是JsonObject时使用,解析效率比StringRequest高一些
  3. JsonArrayRequest:确定求求返回的数据类型是JsonArray时使用

本帖解决的有关Volley的五个问题

  1. Volley的Get和Post请求方式的使用
  2. Volley的网络请求队列的建立和取消队列请求
  3. Volley与Activity生命周期的联动 
  4. Volley的简单二次封装
  5. Volley获取网络图片

 

二、Volley的网络请求队列的建立和取消队列请求

  我们可以建立一个全局的请求队列,再在需要的时候建立一个请求,并将这个请求加入到请求队列中,这样一来整个APP的请求都可以通过这个队列来管理。因为Volley有全局请求队列这一功能,所以Volley更适合于并发的、对效率和性能要求非常好的场景。我们需要建立一个请求队列所在的全局类(继承自Application类),代码如下:

 1 public class MyApplication extends Application {
 2     private static RequestQueue queue; // Volley的全局请求队列
 3 
 4     @Override
 5     public void onCreate() {
 6         super.onCreate();
 7         // 创建Application的同时初始化Volley全局请求队列
 8         queue = Volley.newRequestQueue(getApplicationContext());
 9     }
10 
11     // 静态方法返回Volley全局请求队列
12     public static RequestQueue getRequestQueue() {
13         return queue;
14     }
15 }
请求队列所在的全局类MyApplication

  因为涉及到Application的关系,所以我们需要在AndroidMenifest文件的<application>标签中添加application的引用: android:name=".com.tools.MyApplication" 。另外,因为使用Volley需要使用网络,所以我们还需要为项目添加网络权限: <uses-permission android:name="android.permission.INTERNET" /> 。下面贴出AndroidMenifest文件中的所有代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.activity.volleyclient">
 4 
 5     <uses-permission android:name="android.permission.INTERNET" />
 6 
 7     <application
 8         android:name=".com.tools.MyApplication"
 9         android:allowBackup="true"
10         android:icon="@mipmap/ic_launcher"
11         android:label="@string/app_name"
12         android:supportsRtl="true"
13         android:theme="@android:style/Theme.NoTitleBar">
14         <activity
15             android:name=".MainActivity"
16             android:label="@string/app_name"
17             android:theme="@android:style/Theme.NoTitleBar">
18             <intent-filter>
19                 <action android:name="android.intent.action.MAIN" />
20 
21                 <category android:name="android.intent.category.LAUNCHER" />
22             </intent-filter>
23         </activity>
24     </application>
25 
26 </manifest>
AndroidMenifest文件中的代码

  到此为止,我们就已经创建好了请求队列。我们通过 MyApplication.getRequestQueue().add(stringRequestGet); 来向请求队列中添加请求(注意在添加之前必须为每个请求设置TAG标记,以便删除);用 MyApplication.getRequestQueue().cancelAll("tag"); 来删除请求队列中的请求。 

 

三、Volley的GET和POST请求方式的使用

  上面提到过,Volley获取服务端代码有三种方式:StringRequest、JsonObjectRequest和JsonArrayRequest。由于这三种方式的代码基本一样,而且后两种相比于第一种也有一定的局限性,所以这里就以StringRequest为例,贴出程序源码和结果。

 1 public class MainActivity extends Activity {
 2     // 基本的URL地址(GET和POST都用到这个地址,只不过GET需要在URL上添加参数,而POST不需要)
 3     private static final String BASE_URL = "http://192.168.1.102:8080/VolleyServer/JsonServlet";
 4     private TextView result;
 5 
 6     @Override
 7     protected void onCreate(Bundle savedInstanceState) {
 8         super.onCreate(savedInstanceState);
 9         setContentView(R.layout.content_main);
10         result = (TextView) findViewById(R.id.result);
11 
12         volleyGet();
13         volleyPost();
14     }
15 
16     // 使用GET方式从服务端获取到JSON数据并加以解析
17     private void volleyGet() {
18         // 使用StringRequest获取JSON数据
19         String url = BASE_URL + "?key=person";
20         StringRequest stringRequestGet = new StringRequest(Method.GET, url, new Response.Listener<String>() {
21             @Override
22             public void onResponse(String response) {
23                 try {
24                     addTextToResult("-->使用StringRequest用Get方式获取JSON数据\n");
25                     manageResponse(response);
26                 } catch (Exception e) {
27                     e.printStackTrace();
28                 }
29             }
30         }, new Response.ErrorListener() {
31             @Override
32             public void onErrorResponse(VolleyError volleyError) {
33                 addTextToResult(volleyError.toString());
34             }
35         });
36         // 给Volley的Request请求对象标注TAG,并加入到全局请求队列中
37         stringRequestGet.setTag("StringRequestGet");
38         MyApplication.getRequestQueue().add(stringRequestGet);
39     }
40 
41     // 使用POST方式从服务端获取到JSON数据并加以解析
42     private void volleyPost() {
43         // 使用StringRequest获取JSON数据
44         String url = BASE_URL;
45         StringRequest stringRequestPost = new StringRequest(Method.POST, url, new Response.Listener<String>() {
46             @Override
47             public void onResponse(String response) {
48                 try {
49                     addTextToResult("-->使用StringRequest用Post方式获取JSON数据\n");
50                     manageResponse(response);
51                 } catch (Exception e) {
52                     e.printStackTrace();
53                 }
54             }
55         }, new Response.ErrorListener() {
56             @Override
57             public void onErrorResponse(VolleyError volleyError) {
58                 addTextToResult(volleyError.toString());
59             }
60         }) {
61             @Override
62             protected Map<String, String> getParams() throws AuthFailureError {
63                 Map<String, String> hashMap = new HashMap<>();
64                 hashMap.put("key", "person");
65                 return hashMap;
66             }
67         };
68         stringRequestPost.setTag("StringRequestPost");
69         MyApplication.getRequestQueue().add(stringRequestPost);
70     }
71 
72     // 将参数中的文本添加到界面上的TextView中(在原文本的基础上添加)
73     private void addTextToResult(String text) {
74         String currentText = result.getText().toString();
75         currentText += text;
76         result.setText(currentText);
77     }
78 
79     // 将使用Volley获取到的服务端JSON数据进行解析后添加到结果TextView中
80     private void manageResponse(String response) {
81         try {
82             JSONObject jsonObject = new JSONObject(response).getJSONObject("person");
83             addTextToResult("姓名:" + jsonObject.getString("name") + "\n年龄:" + jsonObject.getInt("age") + "\n地址:" +
84                     jsonObject.getString("address") + "\n\n");
85         } catch (Exception e) {
86             e.printStackTrace();
87         }
88     }
89 
90     @Override
91     protected void onStop() {
92         // 结束Activity的同时销毁全局请求队列中的所有请求
93         super.onStop();
94         MyApplication.getRequestQueue().cancelAll("StringRequestGet");
95         MyApplication.getRequestQueue().cancelAll("StringRequestPost");
96     }
97 }
主界面MainActivity代码

运行结果如下:

 

四、Volley与Activity生命周期的联动

  所谓的和Activity生命周期的联动,就是在Activity被销毁的时候(调用Activity的onStop()方法的时候),同时销毁所有的Volley请求对象。代码已经包含在MainActivity的代码中,这里再贴一遍:

1 @Override
2 protected void onStop() {
3     // 结束Activity的同时销毁全局请求队列中的所有请求
4     super.onStop();
5     MyApplication.getRequestQueue().cancelAll("StringRequestGet");
6     MyApplication.getRequestQueue().cancelAll("StringRequestPost");
7 }
MainActivity的OnStop()方法

 

五、Volley的简单的二次封装

  所谓的“二次封装”,就是把上面我们所做的工作——包括实例化StringRequest、为Request添加TAG、将Request放入请求队列中灯操作——都封装成一个类或一个方法,以便以后调用。为了达到封装的目的,我们需要建立两个类:第一个类是VolleyInterface抽象类,用来定义请求成功和请求失败的接口,通过接口回调,让用户可以在主界面自定义实现操作;第二个类是VolleyRequest类,封装GET和POST两种请求方式的模糊代码,结合VolleyInterface类中的接口,完成封装。以下是这两个类的代码:

 1 public abstract class VolleyInterface {
 2     private Context context;
 3     public static Response.Listener<String> listener; // 请求成功的监听器
 4     public static Response.ErrorListener errorListener; // 请求失败的监听器
 5 
 6     public VolleyInterface(Context context, Response.Listener<String> listener, Response.ErrorListener errorListener) {
 7         this.context = context;
 8         this.listener = listener;
 9         this.errorListener = errorListener;
10     }
11 
12     // 提供给用户编写的请求成功后的操作的接口
13     public abstract void onSuccess(String response);
14 
15     // 提供给用户编写的请求失败后的操作的接口
16     public abstract void onError(VolleyError error);
17 
18     // 利用接口回调,封装StringRequest中的请求成功的接口
19     public Response.Listener<String> loadingListener() {
20         listener = new Response.Listener<String>() {
21             @Override
22             public void onResponse(String response) {
23                 onSuccess(response);
24             }
25         };
26         return listener;
27     }
28 
29     // 利用接口回调,封装StringRequest中的请求失败的接口
30     public Response.ErrorListener loadingErrorListener() {
31         errorListener = new Response.ErrorListener() {
32             @Override
33             public void onErrorResponse(VolleyError error) {
34                 onError(error);
35             }
36         };
37         return errorListener;
38     }
39 }
抽象类VolleyInterface中的代码
 1 public class VolleyRequest {
 2     private static StringRequest request;
 3     private static Context context;
 4 
 5     // 使用StringRequest用GET方式从服务端获取JSON数据的封装方法
 6     public static void RequestGet(Context context, String url, String tag, VolleyInterface vif) {
 7         MyApplication.getRequestQueue().cancelAll(tag);
 8         request = new StringRequest(Request.Method.GET, url, vif.loadingListener(), vif.loadingErrorListener());
 9         request.setTag(tag);
10         MyApplication.getRequestQueue().add(request);
11         MyApplication.getRequestQueue().start();
12     }
13 
14     // 使用StringRequest用POST方式从服务端获取JSON数据的封装方法
15     public static void RequestPost(Context context, String url, String tag, final Map<String, String> params, VolleyInterface vif) {
16         MyApplication.getRequestQueue().cancelAll(tag);
17         request = new StringRequest(Request.Method.POST, url, vif.loadingListener(), vif.loadingErrorListener()) {
18             @Override
19             protected Map<String, String> getParams() throws AuthFailureError {
20                 return params;
21             }
22         };
23         request.setTag(tag);
24         MyApplication.getRequestQueue().add(request);
25         MyApplication.getRequestQueue().start();
26     }
27 }
VolleyRequest类中的代码

以下是MainActivity类中的测试代码:

  1 public class MainActivity extends Activity {
  2     // 基本的URL地址(GET和POST都用到这个地址,只不过GET需要在URL上添加参数,而POST不需要)
  3     private static final String BASE_URL = "http://192.168.1.102:8080/VolleyServer/JsonServlet";
  4     private TextView result;
  5 
  6     @Override
  7     protected void onCreate(Bundle savedInstanceState) {
  8         super.onCreate(savedInstanceState);
  9         setContentView(R.layout.content_main);
 10         result = (TextView) findViewById(R.id.result);
 11 
 12         volleyGet();
 13         volleyPost();
 14         customGet();
 15         customPost();
 16     }
 17 
 18     // 使用GET方式从服务端获取到JSON数据并加以解析
 19     private void volleyGet() {
 20         // 使用StringRequest获取JSON数据
 21         String url = BASE_URL + "?key=person";
 22         StringRequest stringRequestGet = new StringRequest(Method.GET, url, new Response.Listener<String>() {
 23             @Override
 24             public void onResponse(String response) {
 25                 try {
 26                     addTextToResult("-->使用StringRequest用Get方式获取JSON数据\n");
 27                     manageResponse(response);
 28                 } catch (Exception e) {
 29                     e.printStackTrace();
 30                 }
 31             }
 32         }, new Response.ErrorListener() {
 33             @Override
 34             public void onErrorResponse(VolleyError volleyError) {
 35                 addTextToResult(volleyError.toString());
 36             }
 37         });
 38         // 给Volley的Request请求对象标注TAG,并加入到全局请求队列中
 39         stringRequestGet.setTag("StringRequestGet");
 40         MyApplication.getRequestQueue().add(stringRequestGet);
 41     }
 42 
 43     // 使用POST方式从服务端获取到JSON数据并加以解析
 44     private void volleyPost() {
 45         // 使用StringRequest获取JSON数据
 46         String url = BASE_URL;
 47         StringRequest stringRequestPost = new StringRequest(Method.POST, url, new Response.Listener<String>() {
 48             @Override
 49             public void onResponse(String response) {
 50                 try {
 51                     addTextToResult("-->使用StringRequest用Post方式获取JSON数据\n");
 52                     manageResponse(response);
 53                 } catch (Exception e) {
 54                     e.printStackTrace();
 55                 }
 56             }
 57         }, new Response.ErrorListener() {
 58             @Override
 59             public void onErrorResponse(VolleyError volleyError) {
 60                 addTextToResult(volleyError.toString());
 61             }
 62         }) {
 63             @Override
 64             protected Map<String, String> getParams() throws AuthFailureError {
 65                 Map<String, String> hashMap = new HashMap<>();
 66                 hashMap.put("key", "person");
 67                 return hashMap;
 68             }
 69         };
 70         stringRequestPost.setTag("StringRequestPost");
 71         MyApplication.getRequestQueue().add(stringRequestPost);
 72     }
 73 
 74     // 使用二次封装的方法进行GET请求
 75     private void customGet() {
 76         VolleyRequest.RequestGet(this, BASE_URL + "?key=person", "CustomRequestGet", new VolleyInterface(this, VolleyInterface.listener, VolleyInterface.errorListener) {
 77             @Override
 78             public void onSuccess(String response) {
 79                 addTextToResult("-->使用VolleyRequest用GET方式获取JSON数据\n");
 80                 manageResponse(response);
 81             }
 82 
 83             @Override
 84             public void onError(VolleyError error) {
 85                 addTextToResult(error.toString());
 86             }
 87         });
 88     }
 89 
 90     // 使用二次封装的方法进行POST请求
 91     private void customPost() {
 92         HashMap<String, String> hashMap = new HashMap<>();
 93         hashMap.put("key", "person");
 94         VolleyRequest.RequestPost(this, BASE_URL, "CustomRequestPost", hashMap, new VolleyInterface(this, VolleyInterface.listener, VolleyInterface.errorListener) {
 95             @Override
 96             public void onSuccess(String response) {
 97                 addTextToResult("-->使用VolleyRequest用POST方式获取JSON数据\n");
 98                 manageResponse(response);
 99             }
100 
101             @Override
102             public void onError(VolleyError error) {
103                 addTextToResult(error.toString());
104             }
105         });
106     }
107 
108     // 将参数中的文本添加到界面上的TextView中(在原文本的基础上添加)
109     private void addTextToResult(String text) {
110         String currentText = result.getText().toString();
111         currentText += text;
112         result.setText(currentText);
113     }
114 
115     // 将使用Volley获取到的服务端JSON数据进行解析后添加到结果TextView中
116     private void manageResponse(String response) {
117         try {
118             JSONObject jsonObject = new JSONObject(response).getJSONObject("person");
119             addTextToResult("姓名:" + jsonObject.getString("name") + "\n年龄:" + jsonObject.getInt("age") + "\n地址:" +
120                     jsonObject.getString("address") + "\n\n");
121         } catch (Exception e) {
122             e.printStackTrace();
123         }
124     }
125 
126     @Override
127     protected void onStop() {
128         // 结束Activity的同时销毁全局请求队列中的所有请求
129         super.onStop();
130         MyApplication.getRequestQueue().cancelAll("StringRequestGet");
131         MyApplication.getRequestQueue().cancelAll("StringRequestPost");
132         MyApplication.getRequestQueue().cancelAll("CustomRequestGet");
133         MyApplication.getRequestQueue().cancelAll("CustomRequestPost");
134     }
135 }
第二次测试的代码

运行结果如下图所示:

 

六、Volley获取网络图片

  Volley获取网络图片有三种方式。第一种是使用ImageRequest获取网络图片,加载到Android自带的ImageView中;第二种是使用ImageLoader结合图片缓存获取网络图片,加载到Android自带的ImageView中;第三种是使用ImageLoader结合图片缓存获取网络图片,加载到Volley中提供的NetworkImageView中。下面是将这三种方法在同一个Activity中演示的代码:

 1 public class MainActivity extends Activity {
 2     private static final String IMAGE_URL = "http://www.baidu.com/img/bdlogo.png";
 3     private ImageView image, image2;
 4     private NetworkImageView image3;
 5 
 6     @Override
 7     protected void onCreate(Bundle savedInstanceState) {
 8         super.onCreate(savedInstanceState);
 9         setContentView(R.layout.content_main);
10         image = (ImageView) findViewById(R.id.image);
11         image2 = (ImageView) findViewById(R.id.image2);
12         image3 = (NetworkImageView) findViewById(R.id.image3);
13 
14         // Volley加载网络图片
15         volleyImageLoad();
16         volleyImageCache();
17         volleyNetworkImageLoad();
18     }
19 
20     // 使用ImageRequest获取网络图片,加载到Android自带的ImageView中
21     private void volleyImageLoad() {
22         ImageRequest request = new ImageRequest(IMAGE_URL, new Response.Listener<Bitmap>() {
23             @Override
24             public void onResponse(Bitmap bitmap) {
25                 image.setImageBitmap(bitmap);
26             }
27         }, 100, 100, Bitmap.Config.RGB_565, new Response.ErrorListener() {
28             @Override
29             public void onErrorResponse(VolleyError volleyError) {
30                 image.setImageResource(R.mipmap.ic_launcher);
31             }
32         });
33         request.setTag("ImageRequest");
34         MyApplication.getRequestQueue().add(request);
35     }
36 
37     // 使用ImageLoader结合图片缓存获取网络图片,加载到Android自带的ImageView中
38     private void volleyImageCache() {
39         ImageLoader imageLoader = new ImageLoader(MyApplication.getRequestQueue(), new BitmapCache());
40         ImageLoader.ImageListener listener = imageLoader.getImageListener(image2, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
41         imageLoader.get(IMAGE_URL, listener);
42     }
43 
44     // 使用ImageLoader结合图片缓存获取网络图片,加载到Volley中提供的NetworkImageView中
45     private void volleyNetworkImageLoad() {
46         ImageLoader imageLoader = new ImageLoader(MyApplication.getRequestQueue(), new BitmapCache());
47         image3.setDefaultImageResId(R.mipmap.ic_launcher);
48         image3.setErrorImageResId(R.mipmap.ic_launcher);
49         image3.setImageUrl(IMAGE_URL, imageLoader);
50     }
51 
52     @Override
53     protected void onStop() {
54         // 结束Activity的同时销毁全局请求队列中的所有请求
55         super.onStop();
56         MyApplication.getRequestQueue().cancelAll("ImageRequest");
57     }
58 }
MainActivity的代码
 1 public class BitmapCache implements ImageLoader.ImageCache {
 2     public LruCache<String, Bitmap> cache;
 3     public int maxCacheLength = 10 * 1024 * 1024;
 4 
 5     @SuppressLint("NewApi")
 6     public BitmapCache() {
 7         cache = new LruCache<String, Bitmap>(maxCacheLength) {
 8             @Override
 9             protected int sizeOf(String key, Bitmap value) {
10                 return value.getRowBytes() * value.getHeight();
11             }
12         };
13     }
14 
15     @SuppressLint("NewApi")
16     @Override
17     public Bitmap getBitmap(String key) {
18         return cache.get(key);
19     }
20 
21     @SuppressLint("NewApi")
22     @Override
23     public void putBitmap(String key, Bitmap value) {
24         cache.put(key, value);
25     }
26 }
图片缓存类BitmapCache

运行结果如下图所示:

注:第一张图是模糊的,可见用ImageRequest获取到的图片质量不如后两种方法。

 

最后附上  Volley用到的JAR包

posted @ 2016-02-18 10:17  小挂坠  阅读(428)  评论(0编辑  收藏  举报