1. 什么是Volley
我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据。Android
系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高。
不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码。于是乎,一些Android网络通信框架也就应运而生,比如说AsyncHttpClient,它把HTTP所有的通信细节全部封装在了内部,我们只需要简单调用几行代码就可以完成通信操作了。再比如Universal-Image-Loader,它使得在界面上显示网络图片的操作变得极度简单,开发者不用关心如何从网络上获取图片,也不用关心开启线程、回收图片资源等细节,Universal-Image-Loader已经把一切都做好了。
Android开发团队也是意识到了有必要将HTTP的通信操作再进行简单化,于是在2013年Google
I/O大会上推出了一个新的网络通信框架——Volley。Volley可是说是把AsyncHttpClient和Universal-Image-
Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-
Loader一样轻松加载网络上的图片。除了简单易用之外,Volley在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。
下图所示的这些应用都是属于数据量不大,但网络通信频繁的,因此非常适合使用Volley。
2. 下载Volley
介绍了这么多理论的东西,下面我们就准备开始进行实战了,首先需要将Volley的jar包准备好,如果你的电脑上装有Git,可以使用如下命令下载Volley的源码:
git clone https://android.googlesource.com/platform/frameworks/volley
下载完成后将它导入到你的Eclipse工程里,然后再导出一个jar包就可以了。如果你的电脑上没有Git,那么也可以直接使用我导出好的jar包,下载地址是:http://www.kwstu.com/ResourcesView/kwstu_201441183330928
。
新建一个Android项目,将volley.jar文件复制到libs目录下,这样准备工作就算是做好了。
3. StringRequest的用法
前面已经说过,Volley的用法非常简单,那么我们就从最基本的HTTP通信开始学习吧,即发起一条HTTP请求,然后接收HTTP响应。首先需要获取到一个RequestQueue对象,可以调用如下方法获取到:
RequestQueue mQueue = Volley.newRequestQueue(context);
注意这里拿到的RequestQueue是一个请求队列对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。
RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的,基本上在每一个需要和网络交互的Activity中创建一个RequestQueue对象就足够了。
接下来为了要发出一条HTTP请求,我们还需要创建一个StringRequest对象,如下所示:
StringRequest stringRequest = 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); } });
可以看到,这里new出了一个StringRequest对象,StringRequest的构造函数需要传入三个参数,第一个参数就是目标服务器的URL
地址,第二个参数是服务器响应成功的回调,第三个参数是服务器响应失败的回调。其中,目标服务器地址我们填写的是百度的首页,然后在响应成功的回调里打印出服务器返回的内容,在响应失败的回调里打印出失败的详细信息。
最后,将这个StringRequest对象添加到RequestQueue里面就可以了,如下所示:
mQueue.add(stringRequest);
另外,由于Volley是要访问网络的,因此不要忘记在你的AndroidManifest.xml中添加如下权限:
好了,就是这么简单,如果你现在运行一下程序,并发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据。
没错,百度返回给我们的就是这样一长串的HTML代码,虽然我们看起来会有些吃力,但是浏览器却可以轻松地对这段HTML代码进行解析,然后将百度的首页展现出来。
这样的话,一个最基本的HTTP发送与响应的功能就完成了。你会发现根本还没写几行代码就轻易实现了这个功能,主要就是进行了以下三步操作:
1. 创建一个RequestQueue对象。
2. 创建一个StringRequest对象。
3. 将StringRequest对象添加到RequestQueue里面。
不过大家都知道,HTTP的请求类型通常有两种,GET和POST,刚才我们使用的明显是一个GET请求,那么如果想要发出一条POST请求应该怎么做呢?StringRequest中还提供了另外一种四个参数的构造函数,其中第一个参数就是指定请求类型的,我们可以使用如下方式进行指定:
StringRequest stringRequest = new StringRequest(Method.POST, url, listener,
errorListener);
可是这只是指定了HTTP请求方式是POST,那么我们要提交给服务器的参数又该怎么设置呢?很遗憾,StringRequest中并没有提供设置POST
参数的方法,但是当发出POST请求的时候,Volley会尝试调用StringRequest的父类——Request中的getParams()方法来获取POST参数,那么解决方法自然也就有了,我们只需要在StringRequest的匿名类中重写getParams()方法,在这里设置POST
参数就可以了,代码如下所示:
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; } };
你可能会说,每次都这样用起来岂不是很累?连个设置POST参数的方法都没有。但是不要忘记,Volley是开源的,只要你愿意,你可以自由地在里面添加和修改任何的方法,轻松就能定制出一个属于你自己的Volley版本。
4. JsonRequest的用法
学完了最基本的StringRequest的用法,我们再来进阶学习一下JsonRequest的用法。类似于
StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和
JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。
至于它们的用法也基本上没有什么特殊之处,先new出一个JsonObjectRequest对象,如下所示:
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html", 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); } });
可以看到,这里我们填写的URL地址是http://m.weather.com.cn/data/101010100.html,这是中国天气网提供的一个查询天气信息的接口,响应的数据就是以JSON格式返回的,然后我们在onResponse()方法中将返回的数据打印出来。
最后再将这个JsonObjectRequest对象添加到RequestQueue里就可以了,如下所示:
mQueue.add(jsonObjectRequest);
这样当HTTP通信完成之后,服务器响应的天气信息就会回调到onResponse()方法中,并打印出来。现在运行一下程序,发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据。
由此可以看出,服务器返回给我们的数据确实是JSON格式的,并且onResponse()方法中携带的参数也正是一个JSONObject对象,之后只需要从JSONObject对象取出我们想要得到的那部分数据就可以了。
你应该发现了吧,JsonObjectRequest的用法和StringRequest的用法基本上是完全一样的,Volley的易用之处也在这里体现出来了,会了一种就可以让你举一反三,因此关于JsonArrayRequest的用法相信已经不需要我再去讲解了吧。
Android Volley网络框架使用
在Android中,网络请求无非就这两种:HttpURLConnection和HttpClient( Apache),我们在使用时一般都会对它们进行一系列的封装,但是这过程不免有些繁琐,所以,Google官方也考虑到了这点,在2013年Google I/O大会上就推出了一个新的网络请求框架——Volley,它将各种网络请求都简单化,并且把AsyncHttpClient和Universal-Image-Loader两大框架的优点集一身,Volley用在数据量不大的网络请求操作时它的性能表现的非常出色,但是Volley如果在进行数据量大的网络操作时(下载文件等),那么Volley将表现的比较糟糕。
Volley有这么几大功能:
1、普通数据、JSON、图片的异步加载
2、网络请求优先级处理
3、自带硬盘缓存(普通数据、图片、JSON),另外我们在加载图片时候通过ImageLoader还可加入LruCache
4、取消请求
5、与Activity生命周期联动(Activity退出时同时取消所有的请求)
可见,Volley框架是非常强大的,下面我就一一介绍怎么使用Volley框架。
Volley框架的原理:它内部是通过一个请求队列(RequestQueue)来维护所有请求,我们新创建一个请求(request)后通过RequestQueue.add()方法将请求添加置请求队列中,然后调用RequestQueue.start()方法执行请求队列中的方法
Volley中包含这么几种类型的请求:
StringRequest - 返回字符串数据
JsonObjectRequest - 返回JSONArray数据
JsonArrayRequest - 返回JSONObject数据
ImageRequest - 返回Bitmap类型数据
当然使用前我们必须导入Volley.jar包(可以去网上下载),或者通过git下载
git clone https://android.googlesource.com/platform/frameworks/volley
这里给出我上传的jar包下载地址:Volley.jar
创建RequestQueue请求队列
RequestQueue是通过Volley的静态方法newRequestQueue来创建的:
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
一般我们会继承自Application在自定义的MyApplication中创建一个全局的请求队列,用来维护app中的网络请求。
StringRequest
这里主要讲最常用的GET和POST请求方式:
这里我用聚合网上查询手机号码归属地的数据为例子,我们创建一个StringRequest请求,然后给该请求设置一个Tag,用来标记这个请求,取消请求时候我们可以通过这个Tag来取消某个或者所有请求,再把该请求加入请求队列,最后执行请求队列中的请求。
StringRequest的构造方法为:
/**
* @method 请求方式(GET、POST等)
* @url 请求url
* @listener 请求成功回调的接口
* @errorListener 请求失败回调的接口
*/
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener)
GET
一个完整的StringRequest的GET请求如下:
String url = "http://apis.juhe.cn/mobile/get?phone=18270837821&key=9a4329bdf84fa69d193ce601c22b949d";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());//创建一个请求队列
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
}
});
request.setTag("zxy");
mRequestQueue.add(request);
mRequestQueue.start();
POST
一个完整的StringRequest的POST请求如下:
String url = "http://apis.juhe.cn/mobile/get";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
}
}){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> map =new HashMap<>();
map.put("phone","18270837821");
map.put("key","9a4329bdf84fa69d193ce601c22b949d");
return map;
}
};
request.setTag("zxy");
mRequestQueue.add(request);
mRequestQueue.start();
其中,因为是以POST方式请求数据的,所以我们必须实现StringRequest的getParams()方法,该方法返回的是Map<String, String>类型的集合,也就是用<key,value>的形式把数据通过POST传入服务器
JsonObjectRequest
JsonObjectRequest构造方法为:
/**
* @method 请求方式(GET、POST等)
* @url 请求url
* @jsonRequest 请求传入的json数据
* @listener 请求成功回调的接口
* @errorListener 请求失败回调的接口
*/
public JsonObjectRequest(int method, String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener)
GET
一个完整的JsonObjectRequest的GET请求如下:
因为用的是GET请求方式,参数是在url中传入,所以JSONObject对象传入null
String url = "http://apis.juhe.cn/mobile/get?phone=18270837821&key=9a4329bdf84fa69d193ce601c22b949d";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
Toast.makeText(getApplicationContext(),jsonObject.toString(),Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
}
});
request.setTag("zxy");
mRequestQueue.add(request);
mRequestQueue.start();
POST
一个完整的JsonObjectRequest的POST请求如下:
String url = "http://apis.juhe.cn/mobile/get";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("phone", "18270837821");
jsonObject.put("key", "9a4329bdf84fa69d193ce601c22b949d");
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, url, jsonObject, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
Toast.makeText(getApplicationContext(),jsonObject.toString(),Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
}
});
request.setTag("zxy");
mRequestQueue.add(request);
mRequestQueue.start();
用以上StringRequest和JSONObjectRequest请求我们都获取到了数据,如图
虽然这两种方式都可以返回我们请求的数据,但是JSONObjectRequest请求在处理json对象的返回结果时候效率更高,所以确定返回结果是json类型时候可以使用JSONObjectRequest
ImageRequest
ImageRequest的构造方法为:
/**
* @url 请求url
* @listener 请求成功回调的接口
* @maxWidth 图片最大的宽度(如果超过则Volley会对图片进行压缩,如果为0则不压缩)
* @maxHeight 图片最大的高度
* @decodeConfig 图片的配置
* @errorListener 请求失败回调的接口
*/
public ImageRequest(String url, Listener<Bitmap> listener, int maxWidth, int maxHeight, Config decodeConfig, ErrorListener errorListener)
普通的加载方式(ImageView显示)
一个标准的普通请求网络图片的方法:
String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}, 0, 0, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
}
});
request.setTag("zxy");
mRequestQueue.add(request);
mRequestQueue.start();
}
这种方式只适合在图片数量不多的情况下使用,否则则需要考虑使用下面两种,同样,因为Volley内部本身就有硬盘缓存机制,在没网的情况下则会加载缓存中的图片
带LruCache缓存的加载方式(ImageView+ImageLoader+ImageCache)
我们上一种方法就是使用Volley中的ImageRequest请求的加载网络图片,这种方法没有LruCache和显示效果不好,下面我们来使用Volley中的ImageLoader+ImageCache的加载方式,这种方式可以在网络差的情况下和加载出错的情况下给出一个默认的提示图片,而且使用了LruCache缓存可以避免OOM。
首先我们创建一个ImageLoader对象,ImageLoader构造方法
public ImageLoader(RequestQueue queue, ImageLoader.ImageCache imageCache)
需要传入请求队列对象和ImageCache对象,我们再来看看ImageCache对象
public interface ImageCache{...}
发现它是ImageLoader内部的一个接口,所以我们得实现这个接口然后传入,于是我们创建一个BimapCache实现ImageCache接口:
public class BitmapCache implements ImageLoader.ImageCache {
private LruCache<String, Bitmap> mBitmapLruCache;
public BitmapCache() {
int maxCache = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxCache / 8;
mBitmapLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
}
@Override
public Bitmap getBitmap(String key) {
return mBitmapLruCache.get(key);
}
@Override
public void putBitmap(String key, Bitmap bitmap) {
mBitmapLruCache.put(key, bitmap);
}
}
其实这就是创建一个图片内存缓存对象。
之后我们再使用ImageLoader.get()方法加载网络图片,我们来看看get()方法的参数:
/**
* @requestUrl - 请求url
* @listener - ImageLoader.ImageListener的监听对象
* @maxWidth - 图片的最大高度,如果超过则会压缩,为0则不压缩
* @maxHeight
*/
public ImageLoader.ImageContainer get(String requestUrl, ImageLoader.ImageListener listener, int maxWidth, int maxHeight)
第一个参数好办,那么第二个参数我们可以通过ImageLoader.getImageListener()来得到,我们来看看它的参数:
/**
* @view - 表示将图片设置到哪个控件对象上
* @defaultImageResId- 默认时显示的图片的资源id
* @errorImageResId- 加载出错时显示的图片的资源id
*/
public static ImageLoader.ImageListener getImageListener(final ImageView view, final int defaultImageResId, final int errorImageResId)
好了一切都好办了,一个带LruCache缓存的加载图片的标准请求:
String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
ImageLoader mImageLoader = new ImageLoader(mRequestQueue,new BitmapCache());
ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(mImageView, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
mImageLoader.get(url,imageListener);
效果图:
【注意】:mImageLoader.get(url,imageListener)加载图片的原理是什么呢?其实从源码中很容易看出,它首先会得到url对应的key,然后判断硬盘缓存中是否含有该key的图片,如果有则取出,没有则通过网络请求重新加载图片,所以使用这种双缓存的方式加载网络图片,可以有效的防止OOM
使用NetworkImageView+ImageLoader+ImageCache
Volley框架中对图片的请求做的特别好,其中还为我们提供了一个专门用于显示图片的控件:NetworkImageView,该控件继承自ImageView,除了拥有ImageView控件的功能之外,还多了三个方法:
public void setImageUrl(String url, ImageLoader imageLoader) {
this.mUrl = url;
this.mImageLoader = imageLoader;
this.loadImageIfNecessary(false);
}
public void setDefaultImageResId(int defaultImage) {
this.mDefaultImageId = defaultImage;
}
public void setErrorImageResId(int errorImage) {
this.mErrorImageId = errorImage;
}
setDefaultImageResId - 设置该控件默认时显示的图片
setErrorImageResId - 设置加载网络图片失败时显示的图片
setImageUrl - 从网络上加载图片
使用NetworkImageView需要在layout中替换掉ImageView:
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/net_img"
android:layout_width="match_parent"
android:layout_height="match_parent" />
同样这里也需要使用ImageLoader和ImageCache,一个标准使用NetworkImageView的例子为:
String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
ImageLoader mImageLoader = new ImageLoader(mRequestQueue,new BitmapCache());
mNetworkImageView.setDefaultImageResId(R.mipmap.ic_launcher);//设置默认显示的图片
mNetworkImageView.setErrorImageResId(R.mipmap.ic_launcher);//设置加载出错时显示的图片
mNetworkImageView.setImageUrl(url,mImageLoader);
效果为:
可以看到用NetworkImageView和第二种方式效果是一样的,它同样会先检查硬盘缓存中有没有该图片,如果没有,再通过网络加载得到该图片,如果有则直接设置。既然和第二种一样,那为什么还推出NetworkImageView这个控件呢?答案就是NetworkImageView这个控件在你的Activity退出时候会自动取消网络请求,即完全不需要我们担心网络请求生命周期的问题。
【注意】:在上述两种使用了LruCache缓存加载图片的方法,当图片量较大时推荐使用,否则当你只有几张图片则使用第一种比较好,因为你使用LruCache时需要为它分配一定的内存空间,而图片量不大时候也使用LruCache缓存,那这块空间则一直是作为缓存图片用的,占着这块内存空间,相反反而会得不偿失。
Volley与Activity生命周期联动与取消请求
其实就是在Activity退出时候或销毁时候,取消对应的网络请求,避免网络请求在后台浪费资源,所以,我们一般在onStop()方法中通过之前设置的Tag取消网络请求:
@Override
protected void onStop() {
super.onStop();
mRequestQueue.cancelAll("zxy");
}
RequestQueue.cancelAll()方法会过滤出Tag为“zxy”的所有请求都一并取消。
或者通过Request.cancel()取消请求,把当前Activity的请求放入一个List集合中,关闭Activity时分别取消。
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索网络
, 数据
, 对象
, 缓存
, 参数
new
volley框架、android volley框架、volley框架下载、volley框架的使用、volley框架上传图片,以便于您获取更多的相关知识。