最新消息:欢迎访问Android开发中文站!商务联系微信:loading_in

Android:Volley源码解析

开发进阶 AndroidChina 2511浏览 0评论

简单实例

Volley是一个封装HttpUrlConnection和HttpClient的网络通信框架,集AsyncHttpClient和Universal-Image-Loader的优点于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载并缓存下载的图片。Volley在性能方面也进行了大幅度的调整,它的设计目标就是进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会比较糟糕。从下面这个简单的实例来研究一下源码。

RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
StringRequest stringRequest = new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
    @Override
    public void onResponse(String s) {
        tv.setText(s);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError volleyError) {

    }
});
mQueue.add(stringRequest);

流程

1、以静态工厂形式实例化一个RequestQueue对象

RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);

  • 首先看一下RequestQueue这个类:
public class RequestQueue {
    private AtomicInteger mSequenceGenerator;
    private final Map<String, Queue<Request>> mWaitingRequests;
    private final Set<Request> mCurrentRequests;
    private final PriorityBlockingQueue<Request> mCacheQueue;
    private final PriorityBlockingQueue<Request> mNetworkQueue;
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    private final Cache mCache;
    private final Network mNetwork;
    private final ResponseDelivery mDelivery;
    private NetworkDispatcher[] mDispatchers;
    private CacheDispatcher mCacheDispatcher;

    public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
        this.mSequenceGenerator = new AtomicInteger();
        this.mWaitingRequests = new HashMap();
        this.mCurrentRequests = new HashSet();
        this.mCacheQueue = new PriorityBlockingQueue();
        this.mNetworkQueue = new PriorityBlockingQueue();
        this.mCache = cache;
        this.mNetwork = network;
        this.mDispatchers = new NetworkDispatcher[threadPoolSize];
        this.mDelivery = delivery;
    }

    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

    public RequestQueue(Cache cache, Network network) {
        this(cache, network, 4);
    }
    ...
}

从构造函数可知,mWaitingRequests、mCurrentRequests、mCacheQueue、mNetworkQueue是以组合形式实例化,后两者是阻塞队列;而mCache、mNetwork是以聚合形式注入;mDelivery默认也是组合形式new ExecutorDelivery(new Handler(Looper.getMainLooper())))实例化。

  • newRequestQueue方法:
public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
}
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), "volley");
        String userAgent = "volley/0";

        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));
            }
        }

        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        queue1.start();
        return queue1;
}

结合RequestQueue类可知,实例化的RequestQueue对象,注入了new DiskBasedCache(cacheDir)network1,缓存方式默认是磁盘缓存,NetWork对象会根据系统版本,选用不同的Http通信方式。

  • queue.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();
        }

}

CacheDispatcher和NetworkDispatcher都是继承Thread类,所以这个方法生成一条缓存分发线程,和四条网络线程。

  • CacheDispatcher类继承Thread类,所有参数都是聚合形式注入,看下关键的run()方法,由于代码较长,这里不贴了,分段分析下几个比较重要的方法
while(true){
    ...
    Request e = (Request)this.mCacheQueue.take();
    ...
}

首先任务是一个死循环,由于mCacheQueue是个阻塞队列,所以将不断地从阻塞队列读取Request

 Entry entry = this.mCache.get(e.getCacheKey());
if(entry == null) {
      e.addMarker("cache-miss");
      this.mNetworkQueue.put(e);
} else if(entry.isExpired()) {
      e.addMarker("cache-hit-expired");
      e.setCacheEntry(entry);
      this.mNetworkQueue.put(e);
} else {
      e.addMarker("cache-hit");
      Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
      ...
}

判断请求是否有缓存,如果没有或者缓存已经过期,将请求放到网络队列里面。否则找到缓存,则进行下面的操作。

Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));

parseNetworkResponse是Request类的抽象方法,我们进去StringRequest看下:

protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException var4) {
            parsed = new String(response.data);
        }

        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}

可看作是对网络下载的数据进行解析处理,然后返回。

this.mDelivery.postResponse(e, response);

最后进行这一步,mDelivery是在RequestQueue里面实例化后注入CacheDispatcher的,具体的实例化对象:new ExecutorDelivery(new Handler(Looper.getMainLooper()))。看下ExecutorDelivery类,找到postResponse方法。

    public void postResponse(Request<?> request, Response<?> response) {
        this.postResponse(request, response, (Runnable)null);
    }

    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
    ...
        this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable    ));
}

继续往下看

private class ResponseDeliveryRunnable implements Runnable {
    ...
    run(){
        ...
        if(this.mResponse.isSuccess()) {
                    this.mRequest.deliverResponse(this.mResponse.result);
                }
        ...
    }
    ...
}

deliverResponse方法同样是Request类的抽象方法,我们进去StringRequest看下

    protected void deliverResponse(String response) {
        this.mListener.onResponse(response);
    }

就一句回调

  • NetworkDispatcher类同样继承Thread类,其分析过程和CacheDispatcher差不多,重要的同样是以下几步:

1、从网络阻塞队列读取请求,request = (Request)this.mQueue.take();
2、网络下载,NetworkResponse e = this.mNetwork.performRequest(request);(如果是CacheDispatcher这一步就是缓存判断)
3、处理下载后的数据,Response response = request.parseNetworkResponse(e);
3、对处理后的数据进行回调,this.mDelivery.postResponse(e, response)。

2、实例化一个Request对象

StringRequest stringRequest = new StringRequest (url,listener,errorListener);

public class StringRequest extends Request<String> {
    private final Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.mListener = listener;
    }

    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(0, url, listener, errorListener);
    }

    protected void deliverResponse(String response) {
        this.mListener.onResponse(response);
    }

    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException var4) {
            parsed = new String(response.data);
        }

        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

由第一个分析步骤可以知道,这个Request主要就是进行两个操作,也就是重写两个方法。

  1. protected abstract Response<T> parseNetworkResponse(NetworkResponse var1);对下载后的数据进行解析处理;
  2. protected abstract void deliverResponse(T var1);最后回调操作这个数据的方法。

所以构造函数仅需下载地址和回调操作的方法。

3、调用queue.add()方法

 if(!request.shouldCache()) {
       this.mNetworkQueue.add(request);
       return request;
 }

如果不需要缓存就直接添加到网络队列里面,Request有个比较重要的布尔字段mShouldCache,默认是用来判断是否要进行磁盘缓存的。

this.mCacheQueue.add(request);

否则将其添加到缓存队列,这个方法上面也会进行一些当前队列和等待队列的防重复的操作。

小结

这里写图片描述

框架部分:
1、实例化一个RequestQueue对象,开启一条缓存线程和默认的四条网络线程,线程不断地从缓存阻塞队列和网络阻塞队列里面读取请求;
2、如果缓存线程从缓存队列里面读取的请求已经缓存过,则解析数据回调操作方法,否则将其添加到网络队列;
3、如果缓存线程从缓存队列里面读取的请求没有缓存过,则添加到网络队列。
4、网络线程从网络阻塞队列不断读取请求,读到请求后则由封装好的HttpStack对象进行网络下载处理、下载后回调对数据处理的方法,处理后回调操作数据的方法。

客户部分:
1、实例化一个请求对象,在请求对象里面重写处理网络下载后的数据的方法,和操作处理后的数据的方法。
2、将请求对象添加到请求队列,请求需要缓存则会被添加到分配到缓存队列,不需要则被添加到网络队列。

之前看过一个问题,说框架和库有什么不同,高人答曰:框架是他调用你代码,库是你调用他代码。优秀的框架拓展性是如此之强,虽然自己远没那个能力,不过也算开了眼界!

转载请注明:Android开发中文站 » Android:Volley源码解析

您必须 登录 才能发表评论!