Volley
的中文翻译为“齐射、并发”,是在2013
年Google I/O
大会上推出的一个新的网络通信框架,具有网络请求处理、小图片异步加载和缓存等功能,能够帮助APP
更方便地执行网络数据操作,而且更快速高效。Volley
可以说是把HttpClient
和Universal-Image-Loader
的优点集于了一身,既可以像HttpClient
一样非常简单地进行Http
通信,也可以像Universal-Image-Loader
一样轻松加载网络上的图片。除了简单易用之外,Volley
在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大但通信频繁的网络操作,而对于大数据量的网络操作比如说上传、下载文件等Volley
的表现就会非常糟糕。
Volley
的Http
请求在Android 2.3
及以上版本中使用的是HttpURLConnection
,而在Android 2.2
及以下版本中使用的是HttpClient
。在Android 2.2
版本之前HttpClient
拥有较少的Bug
,因此使用它是最好的选择;而在Android 2.3
版本及以后HttpURLConnection
则是最佳的选择。它的API
简单、体积较小,非常适用于Android
项目开发。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。
大多数的Android
应用程序都会使用Http
协议来发送和接收网络数据,在Android
中主要提供了两种方式来进行Http
操作,分别是HttpURLConnection
和HttpClient
。这两种方式都支持Https
协议、以流的形式进行上传和下载、配置超时时间、IPv6
以及连接池等功能。
HttpClient:
DefaultHttpClient
和AndroidHttpClient
都是HttpClient
具体的实现类,它们都拥有众多的API
,而且实现比较稳定,Bug
数量也很少。但同时也由于HttpClient
的API
数量过多,使得我们很难在不破坏兼容性的情况下对它进行升级和扩展,所以目前Android
团队在提升和优化HttpClient
方面的工作态度并不积极。
HttpURLConnection:
HttpURLConnection
是一种多用途、轻量极的Http
客户端,使用它来进行Http
操作可以适用于大多数的应用程序。虽然HttpURLConnection
的API
提供的比较简单,但是这也使得我们可以更加容易地去使用和扩展它。不过在Android 2.2
版本之前,HttpURLConnection
一直存在着一些令人厌烦的Bug
,比如说对一个可读的InputStream
调用close()
方法时,就有可能会导致连接池的失效。
Get
、Post
网络请求和图片加载Request
请求如Gson
、Xml
请求等<uses-permission android:name="android.permission.INTERNET" />
RequestQueue
请求队列对象xxxRequest
请求对象(xxx
代表String
、JsonObject
、JsonArray
等)xxxRequest
请求对象到RequestQueue
请求队列中Volley
主要提供了以下几种类型的异步网络请求:
StringRequest
用来接收和发送String
类型的数据JsonObjectRequest
用来接收和发送JsonObject
类型的数据JsonArrayRequest
用来接收和发送JsonArray
类型的数据请求队列:
public static RequestQueue requestQueue;//请求队列对象
/**
* @Description 用于返回RequestQueue对象,如果为空则创建它
*/
public static RequestQueue getRequestQueue() {
if (requestQueue == null) {
synchronized (HttpRequestUtil.class) {
if (requestQueue == null) {
// 建立Volley的Http请求队列
requestQueue = Volley.newRequestQueue(getContext());
}
}
}
return requestQueue;
}
StringRequest用法:
/**
* get 请求
*
* @param url 访问服务器地址
* @param listener 自定义Http请求回调
*/
public static void getString(String url, HttpListener listener) {
// 创建当前请求对象
StringRequest request = new StringRequest(url, getListener(listener), getErrorListener(listener));
// 第一个代表超时时间:即超过20s认为超时,第三个参数代表最大重试次数,这里设置为1.0f代表如果超时则不重试
request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
// 将请求添加到队列中
getRequestQueue().add(request);
}
/**
* Http请求成功回调类
*
* @param listener 自定义Http请求回调
* @return
*/
public static Response.Listener getListener(final HttpListener listener) {
httpListener = listener;
Response.Listener mListener = new Response.Listener() {
@Override
public void onResponse(String s) {
httpListener.onSuccess(getReturnData(s));
}
};
return mListener;
}
/**
* Http请求失败回调类
*
* @param listener 自定义Http请求回调
* @return
*/
public static Response.ErrorListener getErrorListener(final HttpListener listener) {
httpListener = listener;
Response.ErrorListener mErrorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
httpListener.onFailure(volleyError);
}
};
return mErrorListener;
}
JsonObjectRequest用法:
/**
* JsonObjectRequest 请求
*
* @param method 请求方式
* @param url 访问服务器地址
* @param params 请求参数
*/
public static void requestJsonObject(int method, String url, final Map params) {
// 创建当前请求对象
JsonObjectRequest request = new JsonObjectRequest(method, url, params == null ? null : new JSONObject(params), new Response.Listener() {
@Override
public void onResponse(JSONObject jsonObject) {
//成功响应时回调此函数
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
//失败响应时回调此函数
}
});
// 第一个代表超时时间:即超过20s认为超时,第三个参数代表最大重试次数,这里设置为1.0f代表如果超时则不重试
request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
// 将请求添加到队列中
getRequestQueue().add(request);
}
JsonArrayRequest请求:
/**
* JsonArrayRequest 请求
*
* @param url 访问服务器地址
*/
public static void requestJsonArray(String url) {
// 创建当前请求对象
JsonArrayRequest request = new JsonArrayRequest(url, new Response.Listener() {
@Override
public void onResponse(JSONArray jsonArray) {
//成功响应时回调此函数
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
//失败响应时回调此函数
}
});
// 第一个代表超时时间:即超过20s认为超时,第三个参数代表最大重试次数,这里设置为1.0f代表如果超时则不重试
request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
// 将请求添加到队列中
getRequestQueue().add(request);
}
请求设置和取消:
// 设置请求标签
request.setTag("tag");
/**
* @Description 取消网络请求
*/
public static void cancelAll(Object tag) {
if (tag != null) {
getRequestQueue().cancelAll(tag);
}
}
请求重连和超时:
// 第一个代表超时时间:即超过20s认为超时,第三个参数代表最大重试次数,这里设置为1.0f代表如果超时则不重试
request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
请求优先级:
// 覆写getPriority()方法
@Override
public Priority getPriority() {
return Request.Priority.NORMAL;
}
请求头部(Http头部):
// 覆写getHeaders()方法
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put("cookie", "your cookie");
return headers;
}
请求队列重启:
/**
* @Description 重启当前请求队列
*/
public static void start() {
getRequestQueue().start();
}
Volley
有着强大的缓存机制用来维护请求到的缓存,这节省了不必要的网络消耗和等待时间,下面是一些关于缓存的常用方法:
从缓存中读取请求: 即先从缓存读取看是否有缓存数据,如果没有则请求网络数据
Cache cache = getRequestQueue().getCache();
Cache.Entry entry = cache.get(url);
if (entry != null) {
try {
String data = new String(entry.data, "Utf-8");
//处理data数据,将其转化为JSON,XML,Bitmap等等
} catch (Exception e) {
e.printStackTrace();
}
} else {
//缓存中不存在,做网络请求
}
缓存失效: 它并不意味着删除缓存,Volley
仍将使用缓存对象,直到服务器返回新数据,一旦接收到新数据将覆盖原来的缓存
getRequestQueue().getCache().invalidate(url, true);
关闭缓存: 禁用特定Url
的缓存
getRequestQueue().getCache().remove(url);
删除来自特定Url的缓存
getRequestQueue().getCache().remove(url);
删除所有缓存
getRequestQueue().getCache().clear();
Volley
主要提供了ImageRequest
、ImageLoader
、NetworkImageView
三种图片加载方式。
ImageRequest用法:
RequestQueue
对象ImageRequest
对象Request
对象添加到RequestQueue
里面 /**
* 通过ImageRequest来显示网络图片
*
* @param url 请求图片的地址
* @param imageView 图片的容器ImageView
*/
public static void setImageRequest(String url, final ImageView imageView) {
//参数1:图片的URL地址
//参数2:图片请求成功的回调,这里可以把返回的Bitmap参数设置到ImageView中
//参数3和4:允许图片最大宽度和高度,如果指定网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大都不会进行压缩
//参数5:图片的颜色属性,Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小
//参数6:图片请求失败的回调,这里可以设置当请求失败时在ImageView中显示一张默认图片
ImageRequest imageRequest = new ImageRequest(url, new Response.Listener() {
@Override
public void onResponse(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
}, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
imageView.setBackgroundResource(R.drawable.image_error);
}
});
getRequestQueue().add(imageRequest);
}
ImageLoader用法:
RequestQueue
对象ImageLoader
对象ImageListener
对象ImageLoader
的get()
方法加载网络上的图片特点:内部是使用ImageRequest
来实现的,它不仅可以对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。
/**
* 通过ImageLoader来显示网络图片
*
* @param url 请求图片的地址
* @param imageView 图片的容器ImageView
*/
public static void setImageLoader(String url, ImageView imageView) {
//参数1:RequestQueue对象
//参数2:ImageCache对象
ImageLoader loader = new ImageLoader(getRequestQueue(), new BitmapCache());
//参数1:显示图片的ImageView控件
//参数2:默认显示的图片
//参数3:请求失败时显示的图片
ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(imageView, R.drawable.image_default, R.drawable.image_error);
//参数1:图片的URL地址
//参数2:ImageListener对象
loader.get(url, imageListener);
//参数1:图片的URL地址
//参数2:ImageListener对象
//参数3和4:图片允许的最大宽度和高度
//loader.get(url, imageListener, 200, 200);
}
NetworkImageView用法:
RequestQueue
对象ImageLoader
对象NetworkImageView
控件特点:NetworkImageView
是一个自定义控件,它继承自ImageView
,具备ImageView
控件的所有功能,并且在原生的基础之上加入了加载网络图片的功能。
图片压缩:NetworkImageView
是一个控件,在加载图片的时候它会自动获取自身的宽高,然后对比网络图片的宽高,再决定是否需要对图片进行压缩。也就是说,压缩过程是在内部完全自动化的,并不需要我们关心,NetworkImageView
会始终呈现给我们一张大小刚刚好的网络图片,不会多占用任何一点内存。
/**
* 通过NetWorkImageView来显示网络图片
*
* @param url 请求图片的地址
* @param netWorkImageView 图片的容器NetworkImageView
*/
public static void setNetWorkImageView(String url, NetworkImageView netWorkImageView) {
ImageLoader loader = new ImageLoader(getRequestQueue(), new BitmapCache());
netWorkImageView.setDefaultImageResId(R.drawable.image_default);
netWorkImageView.setErrorImageResId(R.drawable.image_error);
netWorkImageView.setImageUrl(url, loader);
}
Volley
提供了非常好的扩展机制,使得我们可以很轻松地自定义任意类型的Request
来满足不同的数据格式解析。
自定义XMLRequest:
package com.wiggins.volley.http;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
/**
* @Description 自定义XMLRequest用于请求XML格式的数据
* @Author 一花一世界
*/
public class XMLRequest extends Request {
private final Listener mListener;
public XMLRequest(int method, String url, Listener listener, ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
public XMLRequest(String url, Listener listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
protected void deliverResponse(XmlPullParser response) {
mListener.onResponse(response);
}
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String xmlString = new String(response.data, "UTF-8");
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlString));
return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (XmlPullParserException e) {
return Response.error(new ParseError(e));
}
}
}
自定义GsonRequest:
package com.wiggins.volley.http;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import java.io.UnsupportedEncodingException;
/**
* @Description 自定义GsonRequest用于使用GSON来解析数据
* @Author 一花一世界
*/
public class GsonRequest<T> extends Request<T> {
private final Listener mListener;
private Gson mGson;
private Class mClass;
public GsonRequest(int method, String url, Class clazz, Listener listener, ErrorListener errorListener) {
super(method, url, errorListener);
mGson = new Gson();
mClass = clazz;
mListener = listener;
}
public GsonRequest(String url, Class clazz, Listener listener, ErrorListener errorListener) {
this(Method.GET, url, clazz, listener, errorListener);
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data, "UTF-8");
return Response.success(mGson.fromJson(jsonString, mClass),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
}
项目地址 ☞ 传送门