Volley是一个http库,它能够帮助Android Apps更方便的执行网络操作,更重要的是它更快速高效。
优点:
自动执行网络请求
高并发网络连接
通过标准的http的cache coherence(高速缓存一致性)使得磁盘与内存缓存不可见
支持指定请求的优先级
支持取消已经发出的请求,可以取消单个请求或者指定取消请求队列中的一个区域
框架容易定制,如:定制重试或回退功能
强大的指令可以使得异步加载网络数据并显示到UI的操作更加简单
Volley不适合用来下载大的数据文件,因为Volley在解析过程中会把所有的响应数据保存在内存中。
1.发送一个简单的网络请求
Volley的使用方式:创建一个RequestQueue并通过add方法传递Request对象给它。RequestQueue管理工作线程用来执行网络操作,从Cache中读取和写入数据,以及解析http的响应内容。Request执行raw response的解析,Volley会把响应的数据分发给主线程。
执行add方法时,Volley触发执行一个缓存处理线程以及一系列的网络处理线程,具体流程:
当添加请求到队列时,它将被缓存线程捕获并触发:如果这个请求可以被缓存处理,那么会在缓存线程中执行响应数据的解析并返回到主线程。如果请求不能被缓存处理,它会被放到网络队列中。网络线程池中第一个可用的网络线程会从队列中获取到这个请求并执行http操作,解析响应数据,把数据写到缓存中,之后再把解析之后的数据返回到主线程。
取消请求
为了取消一个请求,需要对请求对象执行cancel方法。一旦取消,Volley会确保该请求的响应Handler不会被执行。这意味着在实际操作中可以在Activity的onStop方法中取消所有pending在队列中的请求。不需要通过检测getActivity() == null来丢弃响应handler,其他类似onSaveInstanceState()等保护性的方法里面也都不需要检测。
为了利用这种优势,应该跟踪所有已经发送的请求,以便在需要的时候,可以取消它们。有一个简便的方法:可以为每一个请求对象都绑定一个tag对象。可以使用这个tag来提供取消的范围。同样的,可以为ViewPager中的所有请求缩略图Request对象分别打上对应Tab的tag。并在滑动时取消这些请求,用来确保新生成的tab不会被前面tab的请求任务所卡到。
public class MainActivity extends Activity {
public static final String TAG = "MyTag";
private RequestQueue mRequestQueue;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textView);
mRequestQueue = Volley.newRequestQueue(this);
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
new Response.Listener() {
@Override
public void onResponse(String response) {
mTextView.setText(response.substring(0, 30));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText("error");
}
});
// 给请求设置tag
stringRequest.setTag(TAG);
// 添加tag到请求队列
mRequestQueue.add(stringRequest);
}
@Override
protected void onStop() {
super.onStop();
if (mRequestQueue != null) {
mRequestQueue.cancelAll(TAG);
}
}
}
HTTP的请求类型通常有两种GET和POST,上面是一个GET请求,想要发出一条POST请求需要用:
// POST请求
StringRequest stringRequest = new StringRequest(Method.POST, url, listener, errorListener);
// 带参数的POST请求
StringRequest stringRequest = new StringRequest(Method.POST, url,
listener, errorListener) {
@Override
protected Map getParams() throws AuthFailureError {
HashMap map = new HashMap();
map.put("params1", "value1");
map.put("params2", "value2");
return map;
};
};
2.建立请求队列
如果仅仅是想做一个单次的请求并且不想要线程池一直保留,则可以通过上面提到的Volley.newRequestQueue()方法在任何需要的时候创建RequestQueue。如果APP需要持续的使用网络,更加高效的方式是建立一个RequestQueue的单例,这样它能够持续保持在APP的生命周期中。
一个RequestQueue需要两部分来支持它的工作:一部分是网络操作用来执行请求的数据传输,另外一个是用来处理缓存操作的Cache。在Volley的工具箱中包含了标准的实现方式:DiskBasedCache提供了每个文件与对应响应数据一一映射的缓存实现。BasicNetwork提供了一个网络传输的实现,连接方式可以是HttpClient或者HttpURLConnection。
BasicNetwork是Volley默认的网络操作实现方式。一个BasicNetwork必须使用Http Client进行初始化。这个Client通常是HttpClient或者HttpURLConnection:对于API低于9(Gingerbread)的使用HttpClient。对于API是9及以上的使用HttpURLConnection。
推荐的方式是实现一个单例类,里面封装了RequestQueue对象与其他Volley的方法。
下面是一个单例类,提供了RequestQueue与ImageLoader的功能:
public class RequestQueueSingleton {
private static RequestQueueSingleton mInstance;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static Context mContext;
public static synchronized RequestQueueSingleton getInstance(Context context) {
if (mInstance == null) {
mInstance = new RequestQueueSingleton(context);
}
return mInstance;
}
private RequestQueueSingleton(Context context) {
mContext = context;
mRequestQueue = getRequestQueue();
mImageLoader = new ImageLoader(mRequestQueue,
new ImageLoader.ImageCache() {
private final LruCache cache = new LruCache(
20);
@Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
@Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
});
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(mContext
.getApplicationContext());
}
return mRequestQueue;
}
public void addToRequestQueue(Request request) {
getRequestQueue().add(request);
}
public ImageLoader getImageLoader() {
return mImageLoader;
}
}
利用单例类来执行RequestQueue的操作:
// 获取RequestQueue
RequestQueue queue = RequestQueueSingleton.getInstance(this).getRequestQueue();
// 向RequestQueue中添加请求
RequestQueueSingleton.getInstance(this).addToRequestQueue(stringRequest);
3.创建标准的请求
Volley支持的请求类型:
StringRequest:指定一个URL并在相应回调中接受一个原始的raw string数据。
ImageRequest:指定一个URL并在相应回调中接受一个image。
JsonObjectRequest与JsonArrayRequest(均为JsonRequest的子类):指定一个URL并在相应回调中获取到一个JSON对象或者JSON数组。
3.1Request an Image
Volley为请求图片提供了如下的类,这些类依次有着依赖关系,用来支持在不同的层级进行图片处理:
ImageRequest:一个封装好的,用来处理URL请求图片并且返回一张decode好的bitmap的类。它同样提供了一些简便的接口方法,如指定一个大小进行重新裁剪。它的主要好处是Volley会确保类似decode,resize等耗时的操作执行在工作线程中。
ImageLoader:一个用来处理加载与缓存从网络上获取到的图片的帮助类。ImageLoader是管理协调大量的ImageRequest的类。如在ListView中需要显示大量缩略图的时候。ImageLoader为通常的Volley cache提供了更加前瞻的内存缓存,这个缓存对于防止图片抖动非常有用。这还使得能够在避免阻挡或者延迟主线程的前提下在缓存中能够被Hit到。ImageLoader还能够实现响应联合(Coalescing),每一个响应里面都可以设置bitmap到view上面。联合使得能够同时提交多个响应,这提升了性能。
NetworkImageView:在ImageLoader的基础上建立,替换ImageView进行使用。对于需要对ImageView设置网络图片的情况下使用很有效。
NetworkImageView同样可以在view被detached的时候取消pending的请求。
ImageRequest的例子,获取指定URL的image并显示在APP上:
mImageView = (ImageView) findViewById(R.id.iamgeView);
String url = "http://img3.imgtn.bdimg.com/it/u=3460487607,3411547186&fm=21&gp=0.jpg";
ImageRequest imageRequest = new ImageRequest(url,
new Response.Listener() {
@Override
public void onResponse(Bitmap response) {
mImageView.setImageBitmap(response);
}
}, 0, 0, Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mImageView.setImageResource(R.drawable.ic_launcher);
}
});
RequestQueueSingleton.getInstance(this).addToRequestQueue(imageRequest);
ImageRequest中第3/4个参数分别用于指定允许图片最大的宽度和高度,如果网络图片的宽度或高度大于这个值,则会对图片进行压缩,设置为0则不会压缩。
ImageLoader和NetworkImageView的例子,可以使用它们来处理类似ListView等大量显示图片的情况,在layout xml文件中,可以使用NetworkImageView来替代通常的ImageView,如:
可以使用ImageLoader来显示一张图片:
ImageLoader imageLoader = RequestQueueSingleton.getInstance(this)
.getImageLoader();
imageLoader.get(url, ImageLoader.getImageListener(mImageView,
R.drawable.def_img, R.drawable.err_img));
使用NetworkImageView的实现:
// NetworkImageView的使用
NetworkImageView networkImageView = (NetworkImageView) findViewById(R.id.networkImageView);
networkImageView.setImageUrl(url, imageLoader);
3.2Request JSON
Volley提供了以下的类用来执行JSON请求:
JsonArrayRequest:一个为了获取JSONArray返回数据的请求。
JsonObjectRequest:一个为了获取JSONObject返回数据的请求。允许把一个JSONObject作为请求参数。
这两个类都是继承自JsonRequest的。可以使用类似的方法来处理两种类型的请求。如:
// JsonObjectRequest的使用
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(
"http://my-json-feed", null,
new Response.Listener() {
@Override
public void onResponse(JSONObject response) {
System.out.println(response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
System.out.println(error.getMessage());
}
});
RequestQueueSingleton.getInstance(this).addToRequestQueue(
jsonObjectRequest);
4.实现自定义的请求
如果请求返回的数值是一个string、image或者JSON,则不需要去实现请求类。对于那些需要自定义的请求类型,需要做的步骤是:继承Request
实现抽象方法parseNetworkResponse()与deliverResponse()方法。
parseNetworkResponse:对解析后的结果进行封装,运行在工作线程中。
deliverResponse:将返回的数据带到主线程的回调中。
// 自定义XMLRequest的使用
XMLRequest xmlRequest = new XMLRequest(
"http://flash.weather.com.cn/wmaps/xml/china.xml",
new Response.Listener() {
@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 (nodeName.equals("city")) {
System.out.println("city:"
+ response.getAttributeValue(0));
}
break;
}
eventType = response.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
System.out.println(error.getMessage());
}
});
RequestQueueSingleton.getInstance(this).addToRequestQueue(xmlRequest);