Volley源码分析(二)

1.Volley源码分析(一)
2.Volley源码分析(二)
3.Volley源码分析(三)
4.XVolley-基于Volley的封装的工具类

上一篇分析完了Volley.newRequestqueue()方法。方法最后执行到了requestqueue.start()方法

 /**
     * Starts the dispatchers in this queue.
     */
    public void start() {
        //停止当前所有线程
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        //创建一个缓冲线程,并start
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        //创建4个网络请求线程
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

首先看stop方法

/**
     * Stops the cache and network dispatchers.
     */
    public void stop() {
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        for (final NetworkDispatcher mDispatcher : mDispatchers) {
            if (mDispatcher != null) {
                mDispatcher.quit();
            }
        }
    }

可以看到,stop方法里将所有的线程都quit掉了。

stop方法执行完毕后,会创建一个CacheDispatcher对象和NetworkDispatcher对象的数组,这里先提前说明一下,这两个对象都是继承的Thread类(后面会单独分析这两个类)再通过名字就很好理解了,这里stop后会创建一个缓存线程和4个网络线程,并调用start方法。

4个线程的来历:可以看下RequestQueue是我们在创建RequestQueue时的构造方法,默认调用的是第一个构造方法,对应的DEFAULT_NETWORK_THREAD_POOL_SIZE=4

public RequestQueue(Cache cache, Network network) {
        /**
         * 默认线程池大小=4
         */
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }
    
    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                //Looper.getMainLooper()对应主线程,所以请求成功后的接口回调对应是在主线程中执行。
                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;
    }

分析完start方法,现在分析requestqueue的add方法。

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);
        //mCurrentRequest是一个HashSet,不是线程安全的,所以进行加锁操作,保证同时只能加一个
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        //添加序列号,这里用到了AtomicInteger,是一个线程安全的Integer,适用于高并发的Integer加减
        request.setSequence(getSequenceNumber());
        //添加一个Log信息
        request.addMarker("add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        //判断request是否需要缓存,默认是需要的
        if (!request.shouldCache()) {
            //不需要缓存的话直接加入队列,使用的是PriorityBlockingQueue---一个基于优先级堆的无界的并发安全的优先级队列
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there's already a request with the same cache key in flight.
        //mWaitingRequest 对应一个map<key,queue<request>>
        synchronized (mWaitingRequests) {
            //key对应着url
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                //如果已经有一个相同的请求已经在等待队列里,则将现在这个请求放入相同key的等待队列中
                // There is already a request in flight. Queue up.
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                //没有则new一个
                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.
                //没有的话则插入一个key-null的信息,当为null表明这个key对应的请求就这一个,由于需要缓存,则加入缓存队列
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

第一步:首先将request加入mCurrentRequests。这里注意:
mCurrentRequests是一个HashSet,HashSet底层是一个HashMap,所以不是线程安全的,这里为了线程安全,利用synchronized关键字实现了加锁操作。
第二步:给request添加了序列号。
这里用到了AtomicInteger,是一个线程安全的Integer,适用于高并发的Integer加减

/**
     * Gets a sequence number.
     */
    public int getSequenceNumber() {
        return mSequenceGenerator.incrementAndGet();
    }
    /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }

可以看到,这里利用AtomicInteger,每次获取的序列号的时候,增加1。
第三步:判断request是否需要缓存,每一个新建的request默认都是需要缓存的,如果不需要,则需要显式的调研request的setShouldCache方法。这里如果不需要缓存,则直接将request加入网络请求队列(如下代码所示)。这里使用的是PriorityBlockingQueue---一个基于优先级堆的无界的并发安全的优先级队列

if (!request.shouldCache()) {
            //不需要缓存的话直接加入队列,使用的是PriorityBlockingQueue---一个基于优先级堆的无界的并发安全的优先级队列
            mNetworkQueue.add(request);
            return request;
        }

第四步:如果需要缓存,这里对应需要插入到两个地方mWaitingRequests和mCacheQueue,这里由于mWaitingRequests是一个HashMap,所以同样,需要通过synchronized关键字进行加锁操作。这里分细一点看:

String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                //如果已经有一个相同的请求已经在等待队列里,则将现在这个请求放入相同key的等待队列中
                // There is already a request in flight. Queue up.
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                //没有则new一个
                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);
                }
            }

1)这里mWaitingRequest对应的数据结构是map<key,queue<request>>,key对应的是url。首先判断mWaitingRequest中是否存在相同的url的request,如果存在,则取出存放这种url的requestqueue,存在这个url,但对应的queue为空,则new一个,并且将这个request加入queue,并将queue加入mWatingRequest。

 else {
                // Insert 'null' queue for this cacheKey, indicating there is now a request in
                // flight.
                //没有的话则插入一个key-null的信息,当为null表明这个key对应的请求就这一个,由于需要缓存,则加入缓存队列
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }

2)如果不存在,则插入一个key-null到mWaitingRequest中,并将这个请求加入mCacheQueue缓存队列。

所以这里一个需要缓存的request进入情况就很好分析了,一个新的request加入进来,对应的,mWaitingRequest存放一个key-null。当同样的一个url的request进入的时候,就会放到mWaitingRequest中等待,而这时候mWaitingRequest存在该url的队列,只不过queue为null,这时候就会new一个新的queue放入mWaitingRequest,等下次有同样的url进入的时候,就会直接加入这个队列中等待。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容