Volley源码解析 --- Volley组成(1)

来源:互联网 发布:淘宝网衬衫连衣裙女 编辑:程序博客网 时间:2024/05/19 19:12

简介

volley框架是Google公司开发的一套网络访问开源框架,具有体积小、效率高和适合频繁的网络请求等特点;虽然现在的网络主流都推荐用Retrofit+Okhttp,但是lz认为用什么样的网络框架还是得根据什么样的项目,不是一种功夫就可以打遍天下无敌手的;所有这里分析Volley的目的不一定是去用,而是学习分析Volley的代码设计、网络请求、缓存设计等知识,补充我们的知识,更好的帮助我们开发今后的工作。

知识点简介

本文将以以下几个方面逐点分析
+ Volley由哪些重要的成员组成 ?
+ Volley执行的流程如何以及它的成员运行流程如何 ?
+ Volley一些特性点怎么实现,缓存设计、HTTPS等

Volley重要成员

NetWork.java
网络控制中心,执行真正的网络请求部分,所有的请求最终执行网络访问就是它来完成的

Request.java
Volley提供的网络请求类,封装网络请求参数

RequestQueue.java
这个类很重要,网络请求队列类,所有的Request都是要add到这个类中,而网络线程和缓存线程都是从这个队列里面去取

CacheDispatcher.java
缓存线程,默认只有一个,这个线程用于处理一些具有缓存特性的请求任务,如果有缓存就去缓存中取,没有或者该任务是实时性的,就需要重新把该任务添加到网络线程中去执行

NetworkDispatcher.java
网络线程,所有的网络请求都是由这类型网络线程发出执行的,Volley默认该线程由4个,协同处理所有的请求Request任务

数据结构:

Set

Volley源码分享 – 从最简单的使用方法入手

RequestQueue queue = Volley.newRequestQueue(this);StringRequest request = new StringRequest(Request.Method.GET, "https://www.baidu.com/", new Response.Listener<String>() {            @Override            public void onResponse(String response) {                tv.setText(response);            }        }, new Response.ErrorListener() {            @Override            public void onErrorResponse(VolleyError error) {                tv.setText("error"+error.getMessage());            }        });        queue.add(request);

Volley.newRequestQueue(this)

追踪进去发现它调用了另一个方法newRequestQueue(Context context, HttpStack stack)

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);        ......        if (stack == null) {            if (Build.VERSION.SDK_INT >= 9) {                stack = new HurlStack(); //内部使用HttpURLConnection            } else {              //内部使用HttpClient,但是现在已经无法使用了                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }        Network network = new BasicNetwork(stack);        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        queue.start();        return queue;    }

以上任务有三:
1. 创建缓存目录;
2. 实例化网络控制核心NetWork,api>9使用HurlStack,<9的使用HttpClient,追踪AndroidHttpClient.newInstance,发现它直接抛了一个异常,说明现在<9的是无法使用Volley
3. 实例化RequestQueue,并传递了缓存类和网络控制类,RequestQueue很重要,继续分析:

RequestQueue构造方法

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

上面完成了RequestQueue的成员赋值,实例化了DEFAULT_NETWORK_THREAD_POOL_SIZE(4)个网络线程,还加上一个回调到主线程的工具类ExecutorDelivery;ExecutorDelivery就是内部封装了handler.postRunnable方法,具体可以点击查看源码

回到newRequestQueue执行queue.start()

public void start() {        stop();  // Make sure any currently running dispatchers are stopped.        // Create the cache dispatcher and start it.        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);        mCacheDispatcher.start();        // Create network dispatchers (and corresponding threads) up to the pool size.        for (int i = 0; i < mDispatchers.length; i++) {            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,                    mCache, mDelivery);            mDispatchers[i] = networkDispatcher;            networkDispatcher.start();        }    }

默认情况下,是创建一个CacheDispatcher缓存线程和4个NetworkDispatcher网络线程并运行,它们两个的大致工作就是轮询去mCacheQueue或者mNetworkQueue队列取出Request请求,并执行;
上面代码还有一个很重要的知识点,当前线程如何停止其他线程?解决办法就是该方法的第一条stop()方法,Java默认是无法直接停止其他线程的,只有利用interrupt产生中断异常,由其他线程设置标志位自行完结自己

回到Volley最简单的用法 — RequestQueue.add(Request)

上面示例的StringRequest不是设计主要的就暂不分析了,如有兴趣的可点击这里

public <T> Request<T> add(Request<T> request) {        // Tag the request as belonging to this queue and add it to the set of current requests.        request.setRequestQueue(this);        synchronized (mCurrentRequests) {            mCurrentRequests.add(request);  //所有的Request都要加入mCurrentRequests队列中去,方便管理        }        // Process requests in the order they are added.        request.setSequence(getSequenceNumber());        request.addMarker("add-to-queue");        // If the request is uncacheable, skip the cache queue and go straight to the network.        if (!request.shouldCache()) {            mNetworkQueue.add(request);            return request;        }        // Insert request into stage if there's already a request with the same cache key in flight.        synchronized (mWaitingRequests) {            String cacheKey = request.getCacheKey();            if (mWaitingRequests.containsKey(cacheKey)) {                // There is already a request in flight. Queue up.                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);                if (stagedRequests == null) {                    stagedRequests = new LinkedList<>();                }                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;        }    }

以上完成任务:
1. 将所有Request都加入mCurrentRequests这个Set集合里面来,方便管理;比如有时想取消某个Request就会从set.remove(Request)
2. 如果Request任务不是一个缓存特性的请求,就直接加入到mNetworkQueue队列去,方法结束
3. Request是一个缓存特性,就要根据一定的逻辑加入到mCacheQueue队列里面去;分析这个逻辑:
Map

if (request.shouldCache()) {            synchronized (mWaitingRequests) {                String cacheKey = request.getCacheKey();                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);                if (waitingRequests != null) {                    if (VolleyLog.DEBUG) {                        VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",                                waitingRequests.size(), cacheKey);                    }                    // Process all queued up requests. They won't be considered as in flight, but                    // that's not a problem as the cache has been primed by 'request'.                    mCacheQueue.addAll(waitingRequests);                }            }        }

它会将相同的key的value,从mWaitingRequests的Map集合中取出来,在从新假如到mCacheQueue中去,在CacheDispatcher线程中在轮训mCacheQueue队列把上次网络访问的数据赋值给新的请求,就不必再从新进行一次网络请求

总结

一张流程图

Volley.png

至此,RequestQueue重要方法已经分析差不多了;以上是lz一点见解,如有不足还请指教;明天再继续分析CacheDispatcher和NetworkDispatcher线程执行流程

原创粉丝点击