Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架。在 Google I/O 2013 大会上发布。
Volley 的主要特点
1. 适合数据量小,通信频繁的网络操作
2. 扩展性强。Volley 中大多是基于接口的设计,可配置性强。
3. 一定程度符合 Http 规范,包括返回 ResponseCode(2xx、3xx、4xx、5xx)的处理,请求头的处理,缓存机制的支持等。并支持重试及优先级定义。
4. 默认 Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现。
5. 提供简便的图片加载工具。
首先创建一个请求队列,然后创建一个请求,将请求添加到请求队列中,Volley对队列中的请求进行处理。
图中的概念:
Volley:
Volley 对外暴露的 API,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue。
Request:
表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest都是它的子类,表示某种类型的请求。
RequestQueue:
表示请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 函数启动时会启动CacheDispatcher和NetworkDispatchers。
CacheDispatcher:
一个线程,用于调度处理走缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。
NetworkDispatcher:
一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
ResponseDelivery:
返回结果分发接口,目前只有基于ExecutorDelivery的在入参 handler 对应线程内进行分发。
HttpStack:
处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection 的HttpURLStack和 基于 Apache HttpClient 的HttpClientStack。
Network:
调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
Cache:
缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。NetworkDispatcher得到请求结果后判断是否需要存储在 Cache,CacheDispatcher会从 Cache 中取缓存结果。
首先当有Request被添加到请求队列中请求网络时,Volley会先去Cache中查看是否有请求相同的URL地址的缓存,如果不久前请求过,则会调用CacheDispatcher线程处理获得缓存的返回结果;如果之前没有请求过,则会调用NetworkDispatcher线程处理请求,查看请求队列中有没有空闲的线程,如果有,则调用该线程访问URL,如果没有空闲的线程,则等待线程空闲下来。
在前面我们已经讲了Volley基本使用,现在我们来看代码的实现:
1. 新建一个消息队列RequestQueue的对象,通过调用Volley.newRequestQueue(getApplicationContext())获得该对象。
RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
2. 创建字符串请求消息队列的StringRequest 的请求。传入四个参数,第一个是请求的方法,第二个是请求的url,第三个参数是请求成功回调的方法,第四个是请求失败回调的方法。
GET方式请求:
StringRequest request = new StringRequest(Request.Method.GET, "http://192.168.0.44:8080/MyServiceTest/MyTestServlet",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
POST方式请求:
StringRequest request = new StringRequest(Request.Method.POST, "http://192.168.0.44:8080/MyServiceTest/MyTestServlet",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}){
//重写了StringRequest中的getParams()方法。
@Override
protected Map<String, String> getParams() throws AuthFailureError {
HashMap<String, String > map = new HashMap<>();
map.put("username","zhangsan");
return map;
}
};
3. 请求队列调用add方法,将请求添加到请求队列中
queue.add(request);
我们要知道,如果我们按照上面的方式实现,那么每次调用都会产生一个新的请求队列,这样每次发出请求都会添加到一个新的请求队列中。而我们想要的结果是每次的请求添加到同一个请求队列中,这样我们就需要使每次产生的请求队列是同一个,这就用到了“单例设计模式”。
实现代码如下:
public class MySingleton{
private String lock = "lock";
private static MySingleton mInstance;
private RequestQueue mRequestQueue;
private static Context mCtx;
private MySingleton(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
}
public static MySingleton getInstance(Context context){
if(mInstance == null){
synchronized (lock){
if(mInstance == null){
mInstance = new MySingleton(context);
}
}
}
return mInstance;
}
public RequestQueue getRequestQueue(){
if(mRequestQueue == null){
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
//将请求添加到队列中
public void addToRequestQueue(Request req){
getRequestQueue().add(req);
}
}
Activity中我们通过按钮的点击事件连接网络,然后将返回数据显示在TextView中:
public class VolleyBaseActivity extends Activity implements OnClickListener {
private Button mButtonVolleyGet;
private TextView mTextViewContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_volley_base);
mButtonVolleyGet = (Button) findViewById(R.id.button_volleyget);
mTextViewContent = (TextView) findViewById(R.id.textview_content);
mButtonVolleyGet.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_volleyget:
//创建请求
StringRequest request = new StringRequest(Request.Method.POST, "http://192.168.0.44:8080/MyServiceTest/MyTestServlet",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
mTextViewContent.setText(response);//将连接成功后,返回的信息输出到TextView中。
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextViewContent.setText("网络连接错误");//如果连接失败,在TextView出显示信息"网络连接失败"。
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
HashMap<String, String> map = new HashMap<>();
map.put("username", "zhangsan");
return map;
}
};
//创建请求队列并将请求添加到请求队列中
MySingleton.getInstance(getApplicationContext()).addToRequestQueue(request);
break;
default:
break;
}
}
}
图片加载请求是使用ImageRequest的,但是ImageRequest的使用比较繁琐,Android中就将其进行了封装,使用ImageLoader。我们将ImageLoader也采用单例设计模式。
public class MySingleton{
private static MySingleton mInstance;
private RequestQueue mRequestQueue;//消息队列
private ImageLoader mImageLoader;//ImageLoader对象
private static Context mCtx;
private MySingleton(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
mImageLoader = getImageLoader();
}
public static synchronized MySingleton getInstance(Context context){
if(mInstance == null){
mInstance = new MySingleton(context);
}
return mInstance;
}
public RequestQueue getRequestQueue(){
if(mRequestQueue == null){
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
public ImageLoader getImageLoader(){
if(mImageLoader==null){
mImageLoader = new ImageLoader(getRequestQueue(), new ImageLoader.ImageCache(){
private final LruCache<String,Bitmap> cache = new LruCache<String,Bitmap>(20);//设置图片缓存
@Override
public Bitmap getBitmap(String url) {
return null;
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
}
});
}
return mImageLoader;
}
//将请求添加到队列中
public void addToRequestQueue(Request req){
getRequestQueue().add(req);
}
}
Activity中我们通过点击按钮将图片显示出来。这里我们先显示网络图片使用NetworkImageView,因为NetworkImageView加载网络图片比较简单,只需要调用setImageUrl(URL url, ImageLoader loader)方法即可,方法传入两个参数,第一个是图片的URL地址,第二个是ImageLoader对象。
public class ImageRequestActivity extends Activity implements View.OnClickListener {
private Button mButtonGetImage;
private NetworkImageView mNetworkImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_request);
mButtonGetImage = (Button) findViewById(R.id.button_get_image);
mNetworkImageView = (NetworkImageView) findViewById(R.id.imageview);
mButtonGetImage.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button_get_image:
//获得ImageLoader的对象。
ImageLoader loader = MySingleton.getInstance(getApplicationContext()).getImageLoader();
//调用setImageUrl方法
mNetworkImageView.setImageUrl("http://pic.nipic.com/2007-11-09/2007119122519868_2.jpg", loader);
break;
default:
break;
}
}
}
JsonRequest的使用与StringRequest的使用是相同的,只不过在后期对数据的处理不同。在此不在列举JsonRequest的使用。