Volley 的使用和执行流程分析

概述

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。分别用来解析不同格式的数据。

封装 VolleyTools

学会了基本使用,是不够的。为了更加方便,日常开发中还需要对其进行封装。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 流程分析

下图已经将 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)

你可能感兴趣的:(Android)