0. 前言
android系统中主要提供了httpurlconnection和httpclient进行网络通信,但是如果不对其进行封装就很容易就会写出重复代码。因此一些android网络通信框架应运而生, volley就是其中的佼佼者,volley不仅可以进行http通信,也可以轻松加载网络上的图片。volley设计的初衷就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,volley的表现就会非常糟糕。原因总结如下:
(1)volley的网络请求线程池默认为4。因此只能并发进行4个请求(多了排队),容易被4个较大文件的下载任务阻塞其余请求。
(2)request的parsenetworkresponse()方法返回byte[]类型,需要把传输到的数据读到内存中。如果文件过大,容易引发oom。
1. volley的基本使用
1.1 http get请求
首先在as中导入volley的jar包。
requestqueue mqueue = volley.newrequestqueue(getapplicationcontext());
stringrequest stringrequest_get = new stringrequest("http://www.baidu.com",
new response.listener<string>() {
@override
public void onresponse(string response) {
log.d("tag", response);
}
}, new response.errorlistener() {
@override
public void onerrorresponse(volleyerror error) {
log.e("tag", error.getmessage(), error);
}
});
mqueue.add(stringrequest);
stringrequest的构造函数需要传入三个参数,第一个是url地址,第二/三个参数是服务器响应成功/失败的回调。若成功则将返回的html代码转为string打印出log。
1.2 http post请求
requestqueue mqueue = volley.newrequestqueue(getapplicationcontext());
stringrequest stringrequest = new stringrequest(method.post, url, listener, errorlistener) {
@override
protected map<string, string> getparams() throws authfailureerror {
map<string, string> map = new hashmap<string, string>();
map.put("params1", "value1");
map.put("params2", "value2");
return map;
}
};
mqueue.add(stringrequest);
当发出post请求的时候,volley会尝试调用stringrequest的父类中的getparams()方法来获取post参数,因此我们需要在stringrequest中重写getparams()方法,设置post参数即可。
1.3 jsonrequest
和stringrequest一样,jsonrequest也是继承自request类的, jsonrequest是一个抽象类,有两个子类jsonobjectrequest和jsonarrayrequest,前者用于请求一段json数据的,后者用于请求一段json数组。下面是使用前者进行一段json请求的范例代码。
//队列初始化以及request加入队列略
jsonobjectrequest jsonobjectrequest = new jsonobjectrequest(url, null,
new response.listener<jsonobject>() {
@override
public void onresponse(jsonobject response) {
log.d("tag", response.tostring());
}
}, new response.errorlistener() {
@override
public void onerrorresponse(volleyerror error) {
log.e("tag", error.getmessage(), error);
}
});
1.4 imagerequest
volley支持对图片的加载,因为imagerequest也是继承自request类,因此用法也大同小异。下面直接传入图片url,返回数据后内部解析为bitmap,最后设置给imageview。否则设置默认图片。
imagerequest imagerequest = new imagerequest(url_image,
new response.listener<bitmap>() {
@override
public void onresponse(bitmap response) {
imageview.setimagebitmap(response);
}
}, 0, 0, config.rgb_565, new response.errorlistener() {
@override
public void onerrorresponse(volleyerror error) {
imageview.setimageresource(r.drawable.default_image);
}
});
需要注意的是,第三/四个参数用于指定允许图片最大的宽/高,若网络图片的实际宽高大于该设定值,则会对图片进行压缩,指定成0的话就表示不进行压缩。第五个参数用于指定图片的颜色属性,bitmap.config下的几个常量都可以在这里使用,其中argb_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而rgb_565则表示每个图片像素占据2个字节大小。不过这种加载图片的方式并不被推荐,因为下面有更好的。
1.5 imageloader
imageloader基于imagerequest实现,并且更加智能,多出了帮图片缓存的功能,还可以过滤掉重复的请求链接。但是imageloader已经不再继承自request类。
//mqueue初始化略
imageloader imageloader = new imageloader(mqueue, new bitmapcache());
imagelistener listener = imageloader.getimagelistener(imageview, r.drawable.default_image,
r.drawable.failed_image);
imageloader.get(url_image, listener, 200, 200); //限制最大宽高
public class bitmapcache implements imagecache {
//内部使用lru实现
private lrucache<string, bitmap> mcache;
public bitmapcache() {
//缓存图片的大小设置为10m
int maxsize = 10 * 1024 * 1024;
mcache = new lrucache<string, bitmap>(maxsize) {
@override
protected int sizeof(string key, bitmap bitmap) {
return bitmap.getrowbytes() * bitmap.getheight();
}
};
}
@override
public bitmap getbitmap(string url) {
return mcache.get(url);
}
@override
public void putbitmap(string url, bitmap bitmap) {
mcache.put(url, bitmap);
}
}
第二行构造一个imageloader对象,其中第二个参数是一个imagecache对象,参数二为自定义的用户缓存的类bitmapcache,该类继承了imagecache。第三行获取一个imagelistener对象,传入参数比较简单,看名字就知道了。第四行调用imageloader的get()方法来加载图片。
1.6 networkimageview
这是第三种加载图片的方式,相对来说也是被用的比较多的方式。networkimageview继承自imageview的,在原生的基础之上加入了加载网络图片的功能。用法仍旧是先创建一个requestqueue对象和一个imageloader对象。接下来是在xml中定义我们的networkimageview,宽高表示裁剪到此宽高,wrap_content表示不裁剪。
<com.android.volley.toolbox.networkimageview
android:id="@+id/network_image_view"
android:layout_width="100dp"
android:layout_height="100dp"/>
在activity中获取到networkimageview实例后,就可以调用它的setdefaultimageresid()方法、seterrorimageresid()方法和setimageurl()方法来分别设置加载时显示的图片,加载失败时显示的图片,以及目标图片的url地址。
networkimageview.setdefaultimageresid(r.drawable.default_image);
networkimageview.seterrorimageresid(r.drawable.failed_image);
networkimageview.setimageurl(url_image,imageloader);
2. 自定义request
在网络上传输的数据常用到xml和json格式,那么如果想要请求一条xml/json格式的数据就需要拓展我们的volley。
2. 1 xmlrequest
public class stringrequest extends request<string> {
private final listener<string> mlistener;
public stringrequest(int method, string url, listener<string> listener, errorlistener errorlistener) {
super(method, url, errorlistener);
mlistener = listener;
}
public stringrequest(string url, listener<string> listener, errorlistener errorlistener) {
this(method.get, url, listener, errorlistener);
}
@override
protected void deliverresponse(string response) {
mlistener.onresponse(response);
}
@override
protected response<string> parsenetworkresponse(networkresponse response) {
string parsed;
try {
parsed = new string(response.data, httpheaderparser.parsecharset(response.headers));
} catch (unsupportedencodingexception e) {
parsed = new string(response.data);
}
return response.success(parsed, httpheaderparser.parsecacheheaders(response));
}
}
拓展之前先要看一下原有的request的子类是如何实现的,上面以stringrequest为例。stringrequest中提供了两个有参的构造函数,参数包括请求类型,请求地址,以及响应回调等,在构造函数中一定要调用super()方法将这几个参数传给父类,因为http的请求和响应都是在父类中处理的。
由于request类中的deliverresponse()和parsenetworkresponse()是两个抽象方法,因此stringrequest中对这两个方法进行了实现。前者调用了mlistener中的onresponse()方法,并将response内容传入即完成了将服务器响应的数据进行回调。后者对服务器响应的数据进行解析,其中数据是以字节的形式存放在networkresponse的data变量中(前言中在volley为什么不适合大文件下载就讲到了),这里将数据取出然后组装成一个string,并传入response的success()方法中。
在了解了stringrequest之后,我们自定义实现我们的xmlrequest。
public class xmlrequest extends request<xmlpullparser> {
private final listener<xmlpullparser> mlistener;
public xmlrequest(int method, string url, listener<xmlpullparser> listener, errorlistener errorlistener) {
super(method, url, errorlistener);
mlistener = listener;
}
public xmlrequest(string url, listener<xmlpullparser> listener, errorlistener errorlistener) {
this(method.get, url, listener, errorlistener);
}
@override
protected void deliverresponse(xmlpullparser response) {
mlistener.onresponse(response);
}
@override
protected response<xmlpullparser> parsenetworkresponse(networkresponse response) {
try {
//先转为字符串
string xmlstring = new string(response.data, httpheaderparser.parsecharset(response.headers));
//重点在于解析xml
xmlpullparserfactory factory = xmlpullparserfactory.newinstance();
xmlpullparser xmlpullparser = factory.newpullparser();
xmlpullparser.setinput(new stringreader(xmlstring));
//返回xmlpullparser实例
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));
}
}
}
//使用我们的xmlrequest
xmlrequest xmlrequest = new xmlrequest( url_xml, new response.listener<xmlpullparser>() {
@override
public void onresponse(xmlpullparser response) {
try {
int eventtype = response.geteventtype();
while (eventtype != xmlpullparser.end_document) {
switch (eventtype) {
case xmlpullparser.start_tag:
string nodename = response.getname();
if (wanted_tag.equals(nodename)) {
string pname = response.getattributevalue(0);
log.d("tag", "pname is " + pname);
}
break;
}
eventtype = response.next();
}
} catch (xmlpullparserexception e) {
e.printstacktrace();
} catch (ioexception e) {
e.printstacktrace();
}
}
}, new response.errorlistener() {
@override
public void onerrorresponse(volleyerror error) {
log.e("tag", error.getmessage(), error);
}
});
2. 2 gsonrequest
用过gson的都知道gson解析有多方便,这里可以把volley和gson结合在一起吗?当然可以。重写代码基本上大同小异。前提是先导入gson的jar包。
public class gsonrequest<t> extends request<t> {
private final listener<t> mlistener;
private gson mgson;
private class<t> mclass;
public gsonrequest(int method, string url, class<t> clazz, listener<t> listener, errorlistener errorlistener) {
super(method, url, errorlistener);
mgson = new gson();
mclass = clazz;
mlistener = listener;
}
public gsonrequest(string url, class<t> clazz, listener<t> listener, errorlistener errorlistener) {
this(method.get, url, clazz, listener, errorlistener);
}
@override
protected response<t> parsenetworkresponse(networkresponse response) {
try {
string jsonstring = new string(response.data, httpheaderparser.parsecharset(response.headers));
//结合gson解析
return response.success(mgson.fromjson(jsonstring, mclass), httpheaderparser.parsecacheheaders(response));
} catch (unsupportedencodingexception e) {
return response.error(new parseerror(e));
}
}
@override
protected void deliverresponse(t response) {
mlistener.onresponse(response);
}
}
//使用我们的gsonrequest
gsonrequest<weather> gsonrequest = new gsonrequest<weather>(url_json,yourclass.class,
new response.listener<weather>() {
@override
public void onresponse(yourclass obj) {
//这里获得obj对应的json中的数据
}
}, new response.errorlistener() {
@override
public void onerrorresponse(volleyerror error) {
log.e("tag", error.getmessage(), error);
}
});
以上就是android开发—volley具体的使用详解的详细内容。