流行网络库第(二)篇---Volley源码解析

来源:互联网 发布:php字符串截取添加 编辑:程序博客网 时间:2024/05/18 00:36

在上一篇,流行网络库第(一)篇—Volley用法解析中了解了Volley的基本使用,但是对于Volley可能有些朋友还不是特别清楚。我也是结合源码与其他人的分析,才真正弄清楚Volley的工作原理。

Volley架构图
先看一个Demo,Volley十几行代码就完成了一次HTPP请求,我们看看这段代码内部究竟发生了什么?

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);        mQueue.add(getStringRequest());    }    public StringRequest getStringRequest() {        return new StringRequest("https://suggest.taobao.com/sug?code=utf-8&q=beizi",                new Response.Listener<String>() {                    @Override                    public void onResponse(String response) {                        Log.e(getClass().getName(), response);                    }                },                new Response.ErrorListener() {                    @Override                    public void onErrorResponse(VolleyError error) {                        Log.e(getClass().getName(), error.getMessage());                    }                }        );    }}

首先第一句代码,创建一个消息队列

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

创建消息队列的逻辑,被封装在Volley中,以静态方法暴露出来,供外部调用,总共有3个重载方法,上面使用的是第一个方法(只有一个参数),第一个方法会调用第二个方法(两个参数),第二个方法会调用第三个方法(三个参数)。

public static RequestQueue newRequestQueue(Context context) {    return newRequestQueue(context, null);}public static RequestQueue newRequestQueue(Context context, HttpStack stack){    return newRequestQueue(context, stack, -1);}public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) {     return newRequestQueue(context, null, maxDiskCacheBytes);}

现在看第三个构造方法中做了什么?,这是第一个重点。

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {        //应用的默认缓存目录,DEFAULT_CACHE_DIR值是volley,即文件cacheDir的文件名是volley        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);        // 通过应用程序的包名和版本信息来完善userAgent字符串        String userAgent = "volley/0";        try {            String packageName = context.getPackageName();            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);            userAgent = packageName + "/" + info.versionCode;        } catch (NameNotFoundException e) {        }        //如果stack为NULL,就建立一个默认的HttpStack        if (stack == null) {            if (Build.VERSION.SDK_INT >= 9) {                //版本在2.3之后,创建HurlStack,其内部用了HttpURLConnection                stack = new HurlStack();            } else {                //版本在2.3之前,创建HttpClientStack,其内部用了HttpClient                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }        //将stack传到Network中,Network是负责网络请求的        Network network = new BasicNetwork(stack);        RequestQueue queue;       //创建RequestQueue时要传入一个Cache对象,用来做磁盘缓存        if (maxDiskCacheBytes <= -1) {            // No maximum size specified            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        } else{            // Disk cache size specified            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);        }        // 开始请求队列        queue.start();        return queue;    }

注释都写清楚了,主要就是在创建请求队列的时候,做一些初始化的工作。
- 1、创建应用缓存目录,这个目录是volley;
- 2、 通过应用程序的包名和版本信息来完善userAgent字符串,这个userAgent只有在使用HttpClientStack才有用到;
- 3、如果用户没有建立stack,就建立一个默认的Stack,这里根据版本号判断使用 HttpURLConnection还是使用HttpClient创建Stack;
- 4、创建Stack的目的是作为参数创建Network,Network是负责网络请求的;
- 5、有了Network和DiskBasedCache,最后创建消息队列并且开启消息队列。

到这里你可能还不知道,做这些初始化操作的目的是什么,继续看。

看到最后返回的是RequestQueue对象,我们现在看RequestQueue是怎么工作的。先看看RequestQueue里面几个重要的成员变量。

public class RequestQueue {    //等待队列,当之前已经存在了与本次请求相同的请求时,会把相同的请求放在队列中,以URL为关键字(key)    private final Map<String, Queue<Request<?>>> mWaitingRequests =            new HashMap<String, Queue<Request<?>>>();    //存放所有请求,包括已经运行的和正在等待的请求(Request)    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();    //表示目前缓存的请求队列,这个队列会被传入到CacheDispatcher做判断。    private final PriorityBlockingQueue<Request<?>> mCacheQueue =        new PriorityBlockingQueue<Request<?>>();    //表示请求网络连接的队列,里面的请求都会向服务器进行请求(这才是唯一和服务器打交道的队列)。    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =        new PriorityBlockingQueue<Request<?>>();    // Volley类中调用的构造函数,传入了默认的网络线程数,DEFAULT_NETWORK_THREAD_POOL_SIZE = 4(默认是4个线程)    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;    //缓存相关    private final Cache mCache;    //网络请求相关    private final Network mNetwork;   //请求结果分发器,会把响应得到的结果发送到主线程的回调方法中,最终传递给监听器。    private final ResponseDelivery mDelivery;    //阻塞式线程数组,不断循环处理Request。    private NetworkDispatcher[] mDispatchers;   //阻塞式线程,不断循环处理Request。    private CacheDispatcher mCacheDispatcher;    //请求完成时的监听数组    private List<RequestFinishedListener> mFinishedListeners =            new ArrayList<RequestFinishedListener>();    public static interface RequestFinishedListener<T> {        public void onRequestFinished(Request<T> request);    }}

上面的成员变量代表的意思,请看注释,现在看如何创建一个RequestQueue。这是第二个重点。

  public RequestQueue(Cache cache, Network network, int threadPoolSize,            ResponseDelivery delivery) {      //初始化了network,dispatchers,delivery,cache对象,这里的cache是磁盘缓存的对象      //初始化完成后,立刻调用了start()方法        mCache = cache;        mNetwork = network;        mDispatchers = new NetworkDispatcher[threadPoolSize];        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, DEFAULT_NETWORK_THREAD_POOL_SIZE);    }    public void start() {        //先停止两个阻塞式线程        stop();          //创建一个缓存线程(缓存分发器),不断的处理Request        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);        mCacheDispatcher.start();       //创建4个网络线程(网络分发器),不断的处理Request        for (int i = 0; i < mDispatchers.length; i++) {            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,                    mCache, mDelivery);            mDispatchers[i] = networkDispatcher;            networkDispatcher.start();        }    }    //停止5个阻塞式线程,一个缓存线程,4个网络线程    public void stop() {        if (mCacheDispatcher != null) {            mCacheDispatcher.quit();        }        for (int i = 0; i < mDispatchers.length; i++) {            if (mDispatchers[i] != null) {                mDispatchers[i].quit();            }        }    }

现在看看,其实

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

这一行代码,原来做了这么多的事情,用最后两行代码来概括

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir),network); queue.start();

就是创建了一个RequestQueue对象,调用start对象,开启了4个线程,同时我们要记清楚,RequestQueue并不是一个简单的Queue,内部有好几个Queue,其中mCurrentRequests 存放所有请求,包括已经运行的和正在等待的请求;mCacheQueue (Request)表示目前缓存的请求队列,这个队列会被传入到CacheDispatcher做判断;mNetworkQueue 表示请求网络连接的队列,里面的请求都会向服务器进行请求(这才是唯一和服务器打交道的队列)。并且RequestQueue中还创建了5个线程,其中CacheDispatcher是缓存线程,数量为1,NetworkDispatcher是网络线程,存放在mDispatchers中,数量默认为4。

RequestQueue准备好了,现在该看看,往里面add了。这是第三个重点。

 mQueue.add(getStringRequest());

add方法如下

     public <T> Request<T> add(Request<T> request) {       //每个request的内部维持着一个RequestQueue,先给内部维持的RequestQueue赋值        request.setRequestQueue(this);        synchronized (mCurrentRequests) {            //mCurrentRequests中保存了所有的request,所以一上来就添加            mCurrentRequests.add(request);        }        //设置request的序列号        request.setSequence(getSequenceNumber());         // 设置request当前的真正进行的事件,标记现在是被添加到队列中了        request.addMarker("add-to-queue");        // 如果不允许有磁盘缓存,跳过mCacheQueue,直接把request添加到networkQueue中,然后去请求网络        if (!request.shouldCache()) {        // 添加到mNetworkQueue中,进行网络交互            mNetworkQueue.add(request);            return request;        }        // Insert request into stage if there's already a request with the same cache key in flight.        //mWaitingRequests保存了当前正在执行的请求与等待的请求        synchronized (mWaitingRequests) {            //这个cacheKey是请求的url和请求方式Method拼接起来的,保证了Key的唯一性            String cacheKey = request.getCacheKey();            // 判断等待队列中是否已经有这个request,如果有了就需要过滤重复请求            if (mWaitingRequests.containsKey(cacheKey)) {                // There is already a request in flight. Queue up.                // mWaitingRequests的类型是HashMap<String, Queue<Request<?>>>                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);                if (stagedRequests == null) {                    stagedRequests = new LinkedList<Request<?>>();                }                stagedRequests.add(request);                mWaitingRequests.put(cacheKey, stagedRequests);                if (VolleyLog.DEBUG) {                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);                }            } else {                // Insert 'null' queue for this cacheKey, indicating there is now a request in                // flight.                mWaitingRequests.put(cacheKey, null);                mCacheQueue.add(request);            }            return request;        }    }

请注意mWaitingRequests,mWaitingRequests的类型是HashMap

 public void start() {        stop();         mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);        //先开启缓存线程        mCacheDispatcher.start();        for (int i = 0; i < mDispatchers.length; i++) {            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,                    mCache, mDelivery);            mDispatchers[i] = networkDispatcher;            //在开启网络线程            networkDispatcher.start();        }    }

所以我们看一下,当一个request已经添加到了mWaitingRequests中了,缓存线程mCacheDispatcher是怎么处理它的。注意CacheDispatcher接收了mCacheQueue, mNetworkQueue, mCache, mDelivery这些参数。
先看看CacheDispatcher类的成员。

  public class CacheDispatcher extends Thread {    private static final boolean DEBUG = VolleyLog.DEBUG;    //缓存队列    private final BlockingQueue<Request<?>> mCacheQueue;    //网路请求队列    private final BlockingQueue<Request<?>> mNetworkQueue;    /** The cache to read from. */    private final Cache mCache;    //将响应分发到主线程    private final ResponseDelivery mDelivery;    /** Used for telling us to die. */    private volatile boolean mQuit = false;    public CacheDispatcher(            BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,            Cache cache, ResponseDelivery delivery) {        mCacheQueue = cacheQueue;        mNetworkQueue = networkQueue;        mCache = cache;        mDelivery = delivery;    }}

主要看CacheDispatcher线程中的run方法,这是第四个重点。

    @Override    public void run() {        if (DEBUG) VolleyLog.v("start new dispatcher");        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        // 初始化缓存区,就把之前文件中所保存的对象,一个一个读出来,保存在Map<String, CacheHeader> mEntries这个变量中        mCache.initialize();        Request<?> request;        while (true) {            // release previous request object to avoid leaking request object when mQueue is drained.            request = null;            try {                // 从cache队列中取出一个request对象                request = mCacheQueue.take();            } catch (InterruptedException e) {                // We may have been interrupted because it was time to quit.                if (mQuit) {                    return;                }                continue;            }            try {                // 设置当前事件名称,标记缓存队列中取出的                request.addMarker("cache-queue-take");                // 如果这个request已经被取消了,就直接finish掉,进行下一次循环                if (request.isCanceled()) {                    request.finish("cache-discard-canceled");                    continue;                }                //上面缓存区已经被初始化,所以可以从缓冲区中取出一个缓存,从mCache内部维持的mEntries中取                Cache.Entry entry = mCache.get(request.getCacheKey());                if (entry == null) {                    // 设置当前事件名称                    request.addMarker("cache-miss");                    // Cache miss; send off to the network dispatcher.                    //加入到mNetworkQueue中,进行网络请求,进行下一次循环,处理下一次request                    mNetworkQueue.put(request);                    continue;                }                // entry不为null,但是已经完全过期了,让它重新访问网络                // If it is completely expired, just send it to the network.                if (entry.isExpired()) {                   //设置当前事件名称,标记缓存命中且过期                    request.addMarker("cache-hit-expired");                    request.setCacheEntry(entry);                    //加入到mNetworkQueue中,进行下一次循环,处理下一次request                    mNetworkQueue.put(request);                    continue;                }               //设置当前事件名称,标记缓存命中                request.addMarker("cache-hit");                Response<?> response = request.parseNetworkResponse(                        new NetworkResponse(entry.data, entry.responseHeaders));                //设置当前事件名称,标记这个request已经被解析了                request.addMarker("cache-hit-parsed");                // 判断是否需要刷新                if (!entry.refreshNeeded()) {                   // 发送到response中                    mDelivery.postResponse(request, response);                } else {                    // Soft-expired cache hit. We can deliver the cached response,                    // but we need to also send the request to the network for                    // refreshing.                     //设置当前事件名称,标记缓存需要刷新                    request.addMarker("cache-hit-refresh-needed");                    request.setCacheEntry(entry);                    // Mark the response as intermediate.                    response.intermediate = true;                    // Post the intermediate response back to the user and have                    // the delivery then forward the request along to the network.                    final Request<?> finalRequest = request;                    mDelivery.postResponse(request, response, new Runnable() {                        @Override                        public void run() {                            try {                                mNetworkQueue.put(finalRequest);                            } catch (InterruptedException e) {                                // Not much we can do about this.                            }                        }                    });                }            } catch (Exception e) {                VolleyLog.e(e, "Unhandled exception %s", e.toString());            }        }    }

经过一系列的操作得到的request,最终会把一个request解析成一个Response,调用的是parseNetworkResponse方法。不同的request有不同的parseNetworkResponse方法,我们最上面的Demo用的是StringRequest。

  @Override    protected Response<String> parseNetworkResponse(NetworkResponse response) {        String parsed;        try {            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));        } catch (UnsupportedEncodingException e) {            parsed = new String(response.data);        }        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));    }

得到了Response之后,将这个Response交给mDelivery,mDelivery称为数据分发器,最开始由构造函数传进来的。关于数据的分发,这是第五个重点

mDelivery.postResponse(request, response);

在RequestQueue构造中,mDelivery被初始化,是个ExecutorDelivery对象。

 public RequestQueue(Cache cache, Network network, int threadPoolSize,            ResponseDelivery delivery) {        mCache = cache;        mNetwork = network;        mDispatchers = new NetworkDispatcher[threadPoolSize];        mDelivery = delivery;    }    public RequestQueue(Cache cache, Network network, int threadPoolSize) {        this(cache, network, threadPoolSize,                new ExecutorDelivery(new Handler(Looper.getMainLooper())));    }

ExecutorDelivery是个接口,继承ResponseDelivery

public interface ResponseDelivery {    /**     * Parses a response from the network or cache and delivers it.     */    public void postResponse(Request<?> request, Response<?> response);    /**     * Parses a response from the network or cache and delivers it. The provided     * Runnable will be executed after delivery.     */    public void postResponse(Request<?> request, Response<?> response, Runnable runnable);    /**     * Posts an error for the given request.     */    public void postError(Request<?> request, VolleyError error);}

ExecutorDelivery内部有一个Runnable,/用于分发网络的响应结果到主线程的,这是第六个重点。

 private class ResponseDeliveryRunnable implements Runnable {        private final Request mRequest;        private final Response mResponse;        private final Runnable mRunnable;        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {            mRequest = request;            mResponse = response;            mRunnable = runnable;        }        @SuppressWarnings("unchecked")        @Override        public void run() {           // 看看是否已经被取消了,如果被取消了,结束它,不做分发处理            if (mRequest.isCanceled()) {                //设置当前事件名称,标记request被取消                mRequest.finish("canceled-at-delivery");                return;            }           // 分发一个的响应结果            if (mResponse.isSuccess()) {               //成功回调                mRequest.deliverResponse(mResponse.result);            } else {                 //错误回调                mRequest.deliverError(mResponse.error);            }            // If this is an intermediate response, add a marker, otherwise we're done            // and the request can be finished.            if (mResponse.intermediate) {                mRequest.addMarker("intermediate-response");            } else {                    //设置当前事件名称,标记request已经完成                mRequest.finish("done");            }            // If we have been provided a post-delivery runnable, run it.            if (mRunnable != null) {                mRunnable.run();            }       }    }

mListener,mErrorListener就是我们Demo中设置的回调接口。

  @Override    protected void deliverResponse(String response) {        if (mListener != null) {            mListener.onResponse(response);        }    }
    public void deliverError(VolleyError error) {        if (mErrorListener != null) {            mErrorListener.onErrorResponse(error);        }    }

现在再看那张图
Volley架构图

其中蓝色部分代表主线程,绿色部分代表缓存线程,橙色部分代表网络线程。我们在主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。

Volley工作流程总结:http://www.cnblogs.com/tianzhijiexian/p/4264468.html
1. 当一个RequestQueue被成功申请后会开启一个CacheDispatcher(缓存调度器)和4个(默认)NetworkDispatcher(网络请求调度器);
2. CacheDispatcher缓存调度器最为第一层缓冲,开始工作后阻塞的从缓存序列mCacheQueue中取得请求:
a. 对于已经取消了的请求,直接标记为跳过并结束这个请求
b. 全新或过期的请求,直接丢入mNetworkQueue中交由N个NetworkDispatcher进行处理
c. 已获得缓存信息(网络应答)却没有过期的请求,交由Request的parseNetworkResponse进行解析,从而确定此应答是否成功。然后将请求和应答交由Delivery分发者进行处理,如果需要更新缓存那么该请求还会被放入mNetworkQueue中
3. 用户将请求Request add到RequestQueue之后:
a. 对于不需要缓存的请求(需要额外设置,默认是需要缓存)直接丢入mNetworkQueue交由N个NetworkDispatcher处理;
b. 对于需要缓存的,全新的请求加入到mCacheQueue中给CacheDispatcher处理
c. 需要缓存,但是缓存列表中已经存在了相同URL的请求,放在mWaitingQueue中做暂时雪藏,待之前的请求完毕后,再重新添加到mCacheQueue中;
4. 网络请求调度器NetworkDispatcher作为网络请求真实发生的地方,对消息交给BasicNetwork进行处理,同样的,请求和结果都交由Delivery分发者进行处理;
5. Delivery分发者实际上已经是对网络请求处理的最后一层了,在Delivery对请求处理之前,Request已经对网络应答进行过解析,此时应答成功与否已经设定。而后Delivery根据请求所获得的应答情况做不同处理:
a. 若应答成功,则触发deliverResponse方法,最终会触发开发者为Request设定的Listener
b. 若应答失败,则触发deliverError方法,最终会触发开发者为Request设定的ErrorListener
处理完后,一个Request的生命周期就结束了,Delivery会调用Request的finish操作,将其从mRequestQueue中移除,与此同时,如果等待列表中存在相同URL的请求,则会将剩余的层级请求全部丢入mCacheQueue交由CacheDispatcher进行处理。

一个Request的生命周期:
1. 通过add加入mRequestQueue中,等待请求被执行;
2. 请求执行后,调用自身的parseNetworkResponse对网络应答进行处理,并判断这个应答是否成功;
3. 若成功,则最终会触发自身被开发者设定的Listener;若失败,最终会触发自身被开发者设定的ErrorListener。

参考链接:
http://www.cnblogs.com/tianzhijiexian/p/4264468.html
http://blog.csdn.net/guolin_blog/article/details/17656437
http://blog.csdn.net/ttdevs/article/details/17764351

0 0
原创粉丝点击