转载请注明出处:http://blog.csdn.net/ganklun/article/details/43372355
Google 于 2013 I/O上,发布了Volley。Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。Volley适合数据量不大但是通信频繁的场景。本篇文章将主要介绍volley的基本用法,以及经过封装修改后,实现一行代码搞定StringRequst、JsonObjectRequest、自定义Request的http请求,同时将介绍Volley如何对Cookie以及https的支持。下面就由本屌一步一步带大家领略volley的风采吧。
首先,我们需要创建一个volley队列,也就是一个RequestQueue对象。RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的,基本上在每一个需要和网络交互的Activity中创建一个RequestQueue对象就足够了。这里我们使用单例模式,由于本屌比较懒,所以当然使用懒汉模型啦,O(∩_∩)O哈哈~开个玩笑。代码如下所示:
private static RequestQueue getInstance(Context context) {
if (requestQueue == null) {
synchronized (VolleyUtil.class) {//这里的VolleyUtil.class就是我们封装好后的类。
if (requestQueue == null) {
requestQueue = Volley.newRequestQueue(context);
requestQueue.start();
}
}
}
return requestQueue;
}
事实上,这是第三个步骤了。实际情况是先产生request对象,然后将其加入队列。代码如下所示:
private static void addRequest(RequestQueue requestQueue,
Request request, Object tag) {
if (tag != null) {
request.setTag(tag);
}
request.setShouldCache(false);
request.setRetryPolicy(new DefaultRetryPolicy(TIME_OUT,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
requestQueue.add(request);
}
上面方法第一个参数就是我们第一步中产生的RequestQueue 队列,第二个参数就是接下来我们将要介绍的各种Request对象,第三个参数则是每一个Request
对象的标识,这个标识有什么作用,大家别急,过会也会提到。该方法就做了四件事,第一设置标识,第二设置是否缓存,第三设置超时情况,第四将Request
对象添加到RequestQueue队列。
一、StringRequest
由于传参方式的不同,这里会将StringRequest对象的情况分为POST与GET两种情况。先看POST,代码如下所示:
public static void sendStringRequestByPost(Context context, String url,
Object tag, final Map params, final Class clazz,
final HttpBackListener listener, final boolean isLogin,
final String cookieValue) {
StringRequest stringRequest = new StringRequest(Method.POST, url,
new Response.Listener() {
@Override
public void onResponse(String s) {
T t = JSON.parseObject(s, clazz);
listener.onSuccess(t);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
if (volleyError != null) {
Log.e("VolleyError", volleyError.getMessage());
listener.onFail(volleyError.networkResponse.statusCode);
}
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
if (!isLogin) {
headers.put(GlobConstant.COOKIE,
cookieValue);
return headers;
}
return super.getHeaders();
}
@Override
protected Map getParams() throws AuthFailureError {
return params == null ? super.getParams() : params;
}
};
addRequest(getInstance(context), stringRequest, tag);
}
该方法第一个参数不多说应用上下文,第二个参数就是你请求的httpUrl地址,第三个参数就是该request对象的tag标识,第四个参数就是我们需要提供的键值对参数,这里
是个Map
或者失败的情形,第七个参数其实是应用层面的参数,因为Android应用通常是在登录的时候去获取最新的header里的Cookie值,登录后为了维持与后台的会话连接,
需将最新的cookie值添加到请求头里,故重写getHeaders()方法,大家也可以根据自己的需要重写该方法。第八个参数就是我们添加的cookie值。接下来我们关注下重写
的getParams()方法,经过本屌测试该方法对于GET请求并不能讲参数顺利地提交到后台,POST请求没有问题,特此说明下,大家也可以去验证下是不是这样,欢迎前来
拍砖,(*^__^*) 嘻嘻……
接下来直接看GET请求的情况,代码如下所示:
public static void sendStringRequestByGet(Context context,
final String url, Object tag, final Map params,
final Class clazz, final HttpBackListener listener,
final boolean isLogin, final String cookieValue) {
StringRequest stringRequest = new StringRequest(Method.GET, url,
new Response.Listener() {
@Override
public void onResponse(String s) {
T t = JSON.parseObject(s, clazz);
listener.onSuccess(t);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
if (volleyError != null) {
Log.e("VolleyError", volleyError.getMessage());
listener.onFail(volleyError.networkResponse.statusCode);
}
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
if (!isLogin) {
headers.put(GlobConstant.COOKIE,
cookieValue);
return headers;
}
return super.getHeaders();
}
@Override
public String getUrl() {
String sParams = BaseUtil.mapToStringParams(params);
if (sParams.equals("")) {
return super.getUrl();
} else {
return url + "?" + sParams;
}
}
};
addRequest(getInstance(context), stringRequest, tag);
}
上面方法与POST的情况并没有太大区别,不同的是我们不再以重写getParams()的方式提交Map
二、JsonObjectRequest
JsonObjectRequest,顾名思义传递json咯, 直接上代码:
public static void sendJsonObject(Context context, int method,
String url, JSONObject jsonRequest, Object tag,
final Class clazz, final HttpBackListener listener,
final boolean isLogin, final String cookieValue) {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(method,
url, jsonRequest, new Response.Listener() {
@Override
public void onResponse(JSONObject jsonObject) {
T t = JSON.parseObject(jsonObject.toString(), clazz);
listener.onSuccess(t);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
if (volleyError != null) {
Log.e("VolleyError", volleyError.getMessage());
listener.onFail(volleyError.networkResponse.statusCode);
}
}
}) {
@Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
if (!isLogin) {
headers.put(GlobConstant.COOKIE,
cookieValue);
return headers;
}
return super.getHeaders();
}
};
addRequest(getInstance(context), jsonObjectRequest, tag);
}
这里我们需要传递JsonObject对象格式的参数,响应后public void onResponse(JSONObject jsonObject)得到的也是一个JsonObject对象,该类型就是Android自带的org.json包下的。
三、自定义Request
这里依然是传递json的情况,与上面JsonObjectRequest不同的是,这里我们重写Request,事实上,在volley里Request是所有类型的Request的最顶层父类,包括上面介绍的StringRequest,JsonObjectRequest。所以不难想象,我们自定义Request也难逃其中,自定义一个JsonRequest 去继承 Request类。代码如下:
public class JsonRequest extends Request {
private static final String PROTOCOL_CHARSET = "utf-8";
private static final String PROTOCOL_CONTENT_TYPE = String.format("application/json; charset=%s", PROTOCOL_CHARSET);
private String jsonStr;
private Class clazz;
private boolean isLogin;
private Listener listener;
private String cookieValue;
public JsonRequest(int method, String url, String jsonStr, Class clazz, boolean isLogin, String cookieValue, Listener listener,
ErrorListener errorListener) {
super(method, url, errorListener);
this.jsonStr = jsonStr;
this.clazz = clazz;
this.isLogin = isLogin;
this.cookieValue = cookieValue;
this.listener = listener;
}
@Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
if (!isLogin) {
headers.put(GlobConstant.COOKIE,cookieValue);
return headers;
}
return super.getHeaders();
}
@Override
protected void deliverResponse(T t) {
listener.onResponse(t);
}
@Override
public String getBodyContentType() {
return PROTOCOL_CONTENT_TYPE;
}
@Override
public byte[] getBody() throws AuthFailureError {
return jsonStr == null ? super.getBody() : jsonStr.getBytes();
}
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
if (isLogin) {
//解析请求返回的响应头
Header[] headers = response.apacheHeaders;
if (headers != null) {
for (Header header : headers) {
if (header.getName().equalsIgnoreCase(GlobConstant.SET_COOKIE)) {
//这里大家可以自由发挥,去解析自己需要的cookie.
}
}
}
}
String json = new String(response.data, PROTOCOL_CHARSET);
return Response.success(JSON.parseObject(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
}
首先,我们重写getBodyContentType()方法来说明我们传递的媒体类型,是application/json,字符编码是UTF-8,然后我们重写了getBody()方法,注意上面的jsonStr即将你自定义的Model类(比如User类)用fastJson或者Gson转成的json字符串。然后我们重点关注 parseNetworkResponse(NetworkResponse response)这个方法,这里可以通过response.apacheHeaders获取所有的响应头,然后根据自己的需要去解析相应的cookie。最后利用Json解析工具类将Json串映射成我们自定义的实体类T。这里我用的是fastJson。至于请求头的处理和之前的情况一样,这里不再赘述。自定义好了request后,发送原理就和上面的一样了,代码如下:
public static void sendJsonRequest(Context context, int method,
String url, String jsonStr, Object tag, Class clazz,
final HttpBackListener listener, final boolean isLogin,
final String cookieValue) {
JsonRequest jsonRequest = new JsonRequest(method, url, jsonStr,
clazz, isLogin, cookieValue, new Listener() {
@Override
public void onResponse(T t) {
listener.onSuccess(t);
}
}, new ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
if (volleyError != null) {
Log.e("VolleyError", volleyError.getMessage());
listener.onFail(volleyError.networkResponse.statusCode);
}
}
});
addRequest(getInstance(context), jsonRequest, tag);
}
Activity被终止之后,如果继续使用其中的Context,除了没必要的浪费CPU,电池,网络等资源,有可能还会导致程序crash,所以,我们要防止这种情况的发生。
使用volley,我们可以在Activity停止的时候,同时取消所有或部分未完成的网络请求。代码如下:
public static void cancelAllByTag(Object tag) {
if (null != requestQueue) {
if (tag != null) {
requestQueue.cancelAll(tag);
}
}
}
public static void cancelAll(Context context) {
if (null != requestQueue) {
requestQueue.cancelAll(context);
}
}
你可以在Activity的onStop方法里调用以上两个方法。Volley里所有的请求结果会返回给主线程,如果在主线程里取消了某些请求,则这些请求将不会被返回给主线程。
对于https的支持,大家可以参考这篇文章http://blog.csdn.net/llwdslal/article/details/18052723。本屌已经将其方法编译到最新的jar包里,大家无须编写任何代码。直接使用即可。
https://github.com/GankLun/VolleyUtils。
Volley在性能方面进行了大幅度的调整,它的适用场景就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。本来还想写一点关于volley网络图片加载的东东,说了本人比较懒,第一次写这样的技术博客,哎。。。还是留到下一次写吧。最后,大家是不是可以考虑在今后的网络程序编写中引入volley了呢?