Volley 是 2013年Google I/O大会上被提出的网络请求框架。它使得Android应用网络操作更方便更快捷;抽象了底层 Http Client 等实现的细节,让开发者更专注与产生 RESTful Request。另外,Volley 在不同的线程上异步执行所有请求而避免了阻塞主线程。
Volley 适合小而频繁的通信,它的使用方法很简单,只有两步:
1. 创建 Request
2. Request 传入 RequestQueue 即可。
代码如下:
StringRequest request = new StringRequest(URL, new Response.Listener() {
@Override
public void onResponse(String s) {
System.out.println("onResponse:"+s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
System.out.println("onErrorResponse: " +volleyError);
}
});
request.setTag(toString());
RequestQueue queue = Volley.newRequestQueue(this);
queue.add(request);
除了上面的 StringRequest,Volley 还提供其他三种请求对象:JsonObjectRequest, JsonArrayRequest, ImageRequest。分别用来解析不同格式的数据。
学会了基本使用,是不够的。为了更加方便,日常开发中还需要对其进行封装。Volley 提供的对象有:
1. ImageLoader 图片加载器
2. ImageCache 图片缓存对象
3. RequestQueue 请求队列
为了重用这些对象,提供性能,我们将其封装在单例中。
VolleyTools.java
public class VolleyTools {
private volatile static VolleyTools mInstance = null;
private RequestQueue mQueue;
private MyImageCache mImageCache;
private ImageLoader mImageLoader;
private VolleyTools() {
mQueue = Volley.newRequestQueue(DefaultApplication.getContext());
mImageCache = new MyImageCache();
mImageLoader = new ImageLoader(mQueue, mImageCache);
}
public static VolleyTools getSingletonInstance() {
if (mInstance == null) {
synchronized(VolleyTools.class){
if (mInstance == null){
mInstance = new VolleyTools();
}
}
}
return mInstance;
}
public RequestQueue getRequestQueue(){
return mQueue;
}
public void performRequest(Request request){
getRequestQueue().add(request);
}
public MyImageCache getImageCache(){
return mImageCache;
}
public ImageLoader getImageLoader(){
return mImageLoader;
}
public static RequestQueue newRequestQueue(){
return getSingletonInstance().getRequestQueue();
}
public static void executeRequest( Request request){
getSingletonInstance().performRequest(request);
}
public static MyImageCache newImageCache(){
return getSingletonInstance().getImageCache();
}
public static ImageLoader newImageLoader(){
return getSingletonInstance().getImageLoader();
}
public static void cancelTag(String tag){
getSingletonInstance().getRequestQueue().cancelAll(tag);
}
private class MyImageCache implements ImageLoader.ImageCache {
private LruCache mCache = null;
public MyImageCache() {
int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
mCache = new LruCache(maxSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
};
}
@Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
}
}
VolleyTools 有如下特点:
1. 单例了所有和请求相关的对象
2. 强制使用的 Application 的 Context,防止内存泄露
3. 提供静态的执行方法,加载图片不需要关心ImageLoader ImageCache等
4. 提供全局取消的方法
使用示例1 - 普通请求:
@Override
public void onClick(View v) {
StringRequest request = new StringRequest(URL, new Response.Listener() {
@Override
public void onResponse(String s) {
System.out.println("onResponse:"+s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
System.out.println("onErrorResponse: " +volleyError);
}
});
request.setTag(toString());
VolleyTools.executeRequest(request);
}
@Override
protected void onStop() {
super.onStop();
// 取消请求
VolleyTools.cancelTag(toString());
}
使用示例2 - 请求图片
@Override
public void onClick(View v) {
ImageRequest request = new ImageRequest(URL, new Response.Listener() {
@Override
public void onResponse(Bitmap bitmap) {
mIvTest.setImageBitmap(bitmap);
}
}, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
System.out.println("load error!" + volleyError);
}
});
request.setTag(toString());
VolleyTools.executeRequest(request);
}
下图已经将 Volley 整个流程描述的淋漓尽致。RequestQueue 会维护一个缓存调度线程(cache线程)和一个网络调度线程池(net线程),当一个Request被加到队列中的时候,cache线程会把这个请求进行筛选:如果这个请求的内容可以在缓存中找到,cache线程会亲自解析相应内容,并分发到主线程(UI)。如果缓存中没有,这个request就会被加入到另一个NetworkQueue,所有真正准备进行网络通信的request都在这里,第一个可用的net线程会从NetworkQueue中拿出一个request扔向服务器。当响应数据到的时候,这个net线程会解析原始响应数据,写入缓存,并把解析后的结果返回给主线程。
newRequestQueue :
1 . 根据包名,生成 userAgent
2 . 根据 SDK 版本,选择底层的 Http 访问器(HurlStack or HttpClientStack)
3 . 创建 Network 执行 UrlStack 的 performRequest。performRequest 是网络访问的细节,解析header键值对,解析参数,构建请求对象,发送请求,拿到响应,处理异常等。
4 . 创建 RequestQueue 对象,RequestQueue 的 start 方法
Volley.newRequestQueue():
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
// 准备缓存目录
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0";
// 准备 userAgent
try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
} catch (NameNotFoundException var6) {
;
}
// 选择版本
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
// 创建 RequestQueue 并负责开启轮询
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
queue1.start();
return queue1;
}
RequestQueue
1 . 创建 NetWorkQueue CacheQueue 和 CacheDispatcher NetworkDispatcher
2 . 创建 CacheDispatcher(Thread),缓存池线程:从队列中取出请求,检查当前请求是否命中缓存(Cache hit),如果命中则从缓存中直接取出、解析。否则添加到 NetWorkQueue ,请求处理转向网络线程。(其中,如果发现请求已经被取消,则直接continue,略过)
3 . 创建 NetworkDispatcher (Thread) 会传入刚才在Volley.newRequestQueue() 方法中创建的 Network 对象,默认创建并开启4 个网络请求线程,不断检查(while true) NetWorkQueue 中的请求,如果存在,则马上执行 Netwok.performRequest(request)。 (其中,如果发现请求已经被取消(Request.isCancelled = true, 则直接continue,略过)。
4 . 如果,上面的 CacheDispatcher 或 NetworkDispatcher 处理完了,则会将交给分发器 ExecutorDelivery ,ExecutorDelivery 内部是一个 handler message 消息机制,最终,由 ExecutorDelivery 将服务器的结果返回给 mRequest.deliverResponse(mResponse.result);
这就是StringReqeuset 以及我们自定义 GsonRequest 所要实现的方法了。
RequestQueue.start();
public void start() {
this.stop();
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start();
for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
Volley 是如何关联 activty 生命周期的?
答:通过 tag,当用户退出 Activity 时,在 onStop 执行 RequestQueue.cancelTag(tag), Volley 的 BasicNetwork 在执行请求前首先判断该请求是否已被取消,如果被取消则 continue,不再执行将请求放入队列。
参考:
http://blog.csdn.net/guolin_blog/article/details/17656437 Volley 源码分析,郭林
http://www.jianshu.com/p/231e03b918bd Volley 使用详解(翻译官网)
https://developer.android.com/training/volley/index.html 官网(Training)