参考文档
Android网络请求框架 Volley 你所要知道的一切 : http://blog.youkuaiyun.com/smartbetter/article/details/51497674
Android Volley完全解析,初识Volley的基本用法 : http://blog.youkuaiyun.com/sinyu890807/article/details/17482095
1. Volley的概述
Volley是2013年Google官方在 I/O大会上推出的Android平台的网络通信库,Volley适用于并发和对效率、性能要求比较高的场景。
Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载网络上的图片。Volley在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作。
2. Volley的特点
优点:
(1)使通信更快、更简单 、性能稳定
(2)高效的get、post网络请求以及网络图像的高效率异步处理请求
(3)能对网络请求进行访问排序、优先级处理
(4)网络图片的加载和三级缓存
(5)多级别取消请求,当有多个网络请求同时进行的时候,可以进行同时取消操作
(6)和Activity生命周期的联动,当Activity结束销毁时,可以同时取消网络请求操作
缺点:
(1)不适合数据 , 大文件的上传下载
(2)只支持http请求 , 不支持https请求
(3)图片加载性能一般(ImageLoader)
(4)使用的是httpclient,HttpURLConnection进行网络请求 , 内部对版本进行区分
3. Volley的网络请求
核心类 (见下面详解)
RequestQueue — 请求队列
RequestQueue mQueue = Volley.newRequestQueue(context);
Request — 网络请求
- StringRequet : 返回结果类型为String
- JsonObjectRequest : 返回结果类型为JsonObject对象
- JsonArrayRequest : 返回结果类型为JsonArray数组
- ImageRequet : 返回结果类型为Bitmap
网络请求三部曲
1.创建对应的请求对象(StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest)
2.创建请求队列:RequestQueue queue = Volley.newRequestQueue(context);
3.发起网络请求:queue.add(request)
4. RequestQueue的详解
在我们使用Volley时,要建立一个全局的请求队列,然后我们在建立一个请求,把请求加入到全局队列中,这样app的请求是通过队列管理的,方便我们进行取消某个请求或者取消全部请求。
RequestQueue可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。其内部的设计是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的,基本上创建一个全局的请求队列RequestQueue就足够了。
// 为应用程序建立全局请求队列
public class MyApplication extends Application {
public static RequestQueue queue;
@Override
public void onCreate() {
super.onCreate();
// 建立Volley全局请求队列
queue = Volley.newRequestQueue(getApplicationContext()); // 实例化RequestQueue对象
}
public static RequestQueue getHttpQueue() {
return queue;
}
}
注意:需要把MyApplication注册到AndroidManifest.xml中,并加上网络请求权限。
5. 四种Request的详解
- StringRequet : 返回结果类型不确定的情况使用 , 一般用于解析json数据
public void stringRequest(View v) {
//1.创建请求对象
//第2个参数:成功回调接口,第3个参数:失败回调接口
StringRequest request = new StringRequest(url, new Response.Listener<String>() {
//成功调用该方法
@Override
public void onResponse(String s) {
try {
showToast(s);
showLog(s);
JSONObject json = new JSONObject(s);
String name = json.getString("name");//正式开发不建议使用,当key不存在,空指针
String name1 = json.optString("name");//正式开发建议使用,当key不存在,返回默认值
int age = json.optInt("age");
showLog("age=" + age + ",name=" + name1);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
//错误时调用该方法
@Override
public void onErrorResponse(VolleyError volleyError) {
showLog(volleyError.getMessage());
}
});
//2.创建请求队列
RequestQueue queue = Volley.newRequestQueue(context);
//3.发起请求
queue.add(request);
}
- JsonObjectRequest : 用于请求一段JSON数据
//返回json对象
public void jsonObjectRequest(View v) {
//1.
JSONObject jsonObject = null;//调用接口提交的参数,正式开发不使用这种方式提交参数,重写一个方法getPostParams
// jsonObject.put("password", "123456");
JsonObjectRequest request = new JsonObjectRequest(url, jsonObject, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
showLog(jsonObject.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
showLog(volleyError.getMessage());
}
}) {
@Override
protected Map<String, String> getPostParams() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
params.put("username", "blood");
params.put("password", "123456");
// return super.getPostParams();
return params;
}
@Override
protected Map<String, String> getParams() throws AuthFailureError {
// return super.getParams();
HashMap<String, String> params = new HashMap<String, String>();
params.put("username", "blood");
params.put("password", "123456");
// return super.getPostParams();
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return super.getHeaders();
}
};
//2.
RequestQueue queue = Volley.newRequestQueue(context);
//3.
queue.add(request);
}
- JsonArrayRequest : 用于请求一段JSON数组
public void jsonArrayRequest(final View v) {
final String arrayUrl = "http://10.0.3.2:8080/json/students.json";
JsonArrayRequest request = new JsonArrayRequest(arrayUrl, new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray jsonArray) {
showLog(jsonArray.toString());
//解析json数组
for (int i = 0; i < jsonArray.length(); i++) {
//获取每一个数组元素
JSONObject json = jsonArray.optJSONObject(i);
String name = json.optString("name");
int age = json.optInt("age");
showLog("i=" + i + ",name=" + name + ",age=" + age);
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
showLog("" + volleyError.getMessage());
}
});
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(request);
}
- ImageRequet : 获取网络图片
//返回bitmap对象
public void imageRequest(View v){
String imgUrl = "http://10.0.3.2:8080/imgs/sunny.jpg";
int maxWidth = 120; //0表示默认大小
int maxHeight = 120; //0表示默认大小,大于0对图片大图压缩
Bitmap.Config decodeConfig = Bitmap.Config.RGB_565;// 一个像素点占16位,2byte
// Bitmap.Config decodeConfig = Bitmap.Config.ALPHA_8;//一个像素点占8位,1byte
// Bitmap.Config decodeConfig = Bitmap.Config.ARGB_4444;//一个像素点占16位,2byte
// Bitmap.Config decodeConfig = Bitmap.Config.ARGB_8888;//一个像素点占32位,4byte
/**
* 举例:有一张图片 480 * 800,请计算以下不同图片格式占用大小:
* RGB_565 :480 * 800 * 2 byte = 除以1024=kb
*/
//1.创建请求对象
ImageRequest request = new ImageRequest(imgUrl, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
mImage.setImageBitmap(bitmap);
}
}, maxWidth, maxHeight, decodeConfig, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
showLog(volleyError.getMessage());
}
});
//2.创建请求队列
RequestQueue queue = Volley.newRequestQueue(context);
//3.发起网络请求
queue.add(request);
}
6. Volley的get、post请求方式的使用
- Volley的get请求 , 以StringRequest和JsonObjectRequest为例
/**
* 返回结果类型不确定的情况下
*/
private void volleyGet(String url) {
/**
* int method 请求方式
* String url 请求接口地址
* Listener<String> listener 请求成功的回调
* ErrorListener errorListener 请求失败的回调
*/
StringRequest request = new StringRequest(Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String arg0) {
System.out.println("请求成功:" + arg0); // 输出请求成功的返回结果
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
System.out.println("请求失败:" + arg0);
}
});
request.setTag("homeGet"); // 这个Tag设置的要有意义
MyApplication.getHttpQueue().add(request); // 将请求加入到全局变量
}
/**
* 返回结果类型为JsonObject
* 注意:返回结果类型为JsonArray的请求方法和JsonObject类似
*/
private void volleyJOGet(String url) {
/**
* int method 请求方式
* String url 请求接口地址
* JSONObject jsonRequest Get为null,Post传递负载请求参数的Object
* Listener<JSONObject> listener 请求成功的回调
* ErrorListener errorListener 请求失败的回调
*/
JsonObjectRequest request = new JsonObjectRequest(Method.GET, url, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject arg0) {
System.out.println("请求成功:" + arg0.toString()); // 输出请求成功返回的JSONObject,这里可以直接进行json解析了
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
System.out.println("请求失败:" + arg0);
}
});
request.setTag("homeJoGet"); // 这个Tag设置的要有意义
MyApplication.getHttpQueue().add(request); // 将请求加入到全局变量
}
- Volley的post请求
首先我们用HashMap封装post请求的参数:
Map<String, String> hashmap = new HashMap<String, String>();
hashmap.put("zhangsan", "12"); // 将传递的参数添加进来
hashmap.put("key", "12");
然后就可以用封装好的参数向URL提交post请求了:
/**
* 返回结果类型不确定的情况下
*/
private void volleyPost(String url, final HashMap<String, String> hashmap) {
/**
* int method 请求方式
* String url 请求接口地址,使用post则请求参数不能写在URL里,而是使用参数化的传递
* 例如url:http://www.demo.com/login/get?
* 完整为:url:http://www.demo.com/login/get?zhangsan=12&key=12
* Listener<String> listener 请求成功的回调
* ErrorListener errorListener 请求失败的回调
*/
StringRequest request = new StringRequest(Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String arg0) {
System.out.println("请求成功:" + arg0); // 输出请求成功的返回结果
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
System.out.println("请求失败:" + arg0);
}
}){
// getParams()方法表示用户在volley中使用post方式请求数据中参数的传递
// volley会自动调用getParams()中传递过来的请求参数
@Override
protected Map<String, String> getParams() {
return hashmap;
}
};
request.setTag("homePost"); // 这个Tag设置的要有意义
MyApplication.getHttpQueue().add(request); // 将请求加入到全局变量
}
/**
* 返回结果类型为JsonObject
* 注意:返回结果类型为JsonArray的请求方法和JsonObject类似
*/
private void volleyJOPost(String url, final HashMap<String, String> hashmap) {
JSONObject object = new JSONObject(hashmap);
/**
* int method 请求方式
* String url 请求接口地址,使用post则请求参数不能写在URL里,而是使用参数化的传递
* 例如url:http://www.demo.com/login/get?
* 完整为:url:http://www.demo.com/login/get?zhangsan=12&key=12
* JSONObject jsonRequest 传递负载请求参数的Object
* Listener<JSONObject> listener 请求成功的回调
* ErrorListener errorListener 请求失败的回调
*/
JsonObjectRequest request = new JsonObjectRequest(Method.POST, url, object, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject arg0) {
System.out.println("请求成功:" + arg0.toString()); // 输出请求成功返回的JSONObject,这里可以直接进行json解析了
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
System.out.println("请求失败:" + arg0);
}
});
request.setTag("homeJoPost"); // 这个Tag设置的要有意义
MyApplication.getHttpQueue().add(request); // 将请求加入到全局变量
}
7. Volley与Activity生命周期的联动
Volley的生命周期和Activity的生命周期是关联在一起的,可以在Activity销毁时,同时关闭请求。
一般我们将Volley和Activity的生命周期关联的时候,都需要设置我们的Tag标签(通过Tag标签在全局队列中查找),然后在onStop()方法中执行取消请求。
@Override
protected void onStop() {
super.onStop();
MyApplication.getHttpQueue().cancelAll("homeGet"); // 通过给定的tag值,将指定的队列全部关闭
}
8. Volley使用ImageLoader缓存网络图片
ImageLoader也可以用于加载网络上的图片,并且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效,因为它不仅可以帮我们对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。
总结起来大致可以分为以下四步:
1. 创建一个RequestQueue对象。
2. 创建一个ImageLoader对象。
3. 获取一个ImageListener对象。
4. 调用ImageLoader的get()方法加载网络上的图片。
(1)创建ImageLoader
/**
* RequestQueue queue 请求队列
* ImageCache imageCache 缓存
*/
ImageLoader loader = new ImageLoader(MyApplication.getHttpQueue(), new BitmapCache());
这里需要使用到imageCache(创建了BitmapCache实例),ImageCache单独使用是起不到缓存作用的,所以还要结合LruCache使用,接下来自定义一个ImageCache缓存类:
public class BitmapCache implements ImageCache{
public LruCache<String, Bitmap> cache;
public int maxSize = 10*1024*1024; // 10M,定义LruCache最大缓存大小,当超过会启动自动回收
public BitmapCache(){
cache = new LruCache<String, Bitmap>(maxSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
};
}
// 从缓存中获取
@Override
public Bitmap getBitmap(String arg0) {
return cache.get(arg0);
}
// 存储到缓存中
@Override
public void putBitmap(String arg0, Bitmap arg1) {
cache.put(arg0, arg1);
}
}
(2)下来需要创建ImageListener来监听图片的加载过程:
public class MainActivity extends Activity {
private ImageView iv_img;
String url = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_img = (ImageView)findViewById(R.id.iv_img);
/**
* RequestQueue queue 请求队列
* ImageCache imageCache 缓存
*/
ImageLoader loader = new ImageLoader(MyApplication.getHttpQueue(), new BitmapCache());
/**
* ImageView view ImageView控件
* int defaultImageResId 默认的加载图片
* int errorImageResId 加载出错的时候显示的图片
*/
ImageListener listener = ImageLoader.getImageListener(iv_img,
R.drawable.ic_launcher, R.drawable.ic_launcher);
loader.get(url, listener); // 获取网络图片
}
}
运行一下程序并开始加载图片,你将看到ImageView中会先显示一张默认的图片,等到网络上的图片加载完成后,ImageView则会自动显示该图
9. Volley使用NetworkImageView加载网络图片
Volley还提供了第三种方式来加载网络图片,即使用NetworkImageView , 要搭配ImageLoader进行使用 。不同于以上两种方式,NetworkImageView是一个自定义控件,它是继承自ImageView的,具备ImageView控件的所有功能,并且在原生的基础之上加入了加载网络图片的功能。
大致可以分为以下五步:
1. 创建一个RequestQueue对象。
2. 创建一个ImageLoader对象。
3. 在布局文件中添加一个NetworkImageView控件。
4. 在代码中获取该控件的实例。
5. 设置要加载的图片地址。
public class MainActivity extends Activity {
private NetworkImageView niv_img;
String url = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
niv_img = (NetworkImageView)findViewById(R.id.niv_img);
/**
* RequestQueue queue 请求队列
* ImageCache imageCache 缓存
*/
ImageLoader loader = new ImageLoader(MyApplication.getHttpQueue(), new BitmapCache());
niv_img.setDefaultImageResId(R.drawable.ic_launcher); // 设置默认图片
niv_img.setErrorImageResId(R.drawable.ic_launcher); // 设置默认图片
niv_img.setImageUrl(url, loader); // 设置默认图片
}
}
其中,setImageUrl()方法接收两个参数,第一个参数用于指定图片的URL地址,第二个参数则是前面创建好的ImageLoader对象。
10. 是不是使用NetworkImageView来加载的图片都不会进行压缩呢?
这时有的朋友可能就会问了,使用ImageRequest和ImageLoader这两种方式来加载网络图片,都可以传入一个最大宽度和高度的参数来对图片进行压缩,而NetworkImageView中则完全没有提供设置最大宽度和高度的方法,那么是不是使用NetworkImageView来加载的图片都不会进行压缩呢?
其实并不是这样的,NetworkImageView并不需要提供任何设置最大宽高的方法也能够对加载的图片进行压缩。这是由于NetworkImageView是一个控件,在加载图片的时候它会自动获取自身的宽高,然后对比网络图片的宽度,再决定是否需要对图片进行压缩。也就是说,压缩过程是在内部完全自动化的,并不需要我们关心,NetworkImageView会始终呈现给我们一张大小刚刚好的网络图片,不会多占用任何一点内存,这也是NetworkImageView最简单好用的一点吧。
当然了,如果你不想对图片进行压缩的话,其实也很简单,只需要在布局文件中把NetworkImageView的layout_width和layout_height都设置成wrap_content就可以了,这样NetworkImageView就会将该图片的原始大小展示出来,不会进行任何压缩。