volley网络请求架构分析

大概看了一下volley的源码,源码写得很整洁,代码结构清晰,可扩展性也是很好。

关于volley的源码解析,网上已经有很多的资料,这里就不再进行源码的解析了。但是关于volley的网络请求架构还是值得我们学习的。这里就简单分析一下volley的网络请求架构。

这是volley的网络请求架构图:


整体流程:

首先,网络请求会被加入到请求队列中。先判断网络请求request是否可以缓存的(默认都是可以缓存的),如果不可以缓存,则直接添加到mNetworkQueue网络请求队列中;如果可以缓存,则添加到mCacheQueue缓存队列中。

然后,Volley会维护一个缓存调度线程(cache线程)和一个网络调度线程池(net线程),它们对应两个分发器CacheDispatcher和NetworkDispatcher,它们分别处理mCacheQueue和mNetworkQueue队列中所有请求。

CacheDispatcher中的处理流程是:先从mCacheQueue中取出一个request;然后从本地缓存中存取request对应的数据,如果无法取到对应的数据,则把request添加到mNetworkQueue中,如果取到数据,则把数据进行解析,然后把结果分发到UI线程中进行处理。

NetworkDispatcher的处理流程:先从mNetworkQueue中取出一个request;然后调用BasicNetwork的performRequest()方法进行网络数据的请求,得到网络数据Response后,对Response进行解析;判断,如果request是需要缓存的,就把得到的数据进行缓存,否则直接把数据分发到UI线程进行处理。

这样就完成一个网络请求数据的整个过程。


源码解析

这里只会对volley中的核心的类及类中的核心方法进行解析。

类:RequestQue.java

方法如下:

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

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


解析:

这是一个添加网络请求的方法,我们在客户端使用的时候,只需要简单调用这个add()方法就可以了。

这个方法中涉及到好几个全局变量,我们先分析该变量的作用:

mCurrentRequests:保存所有的添加过得request请求。当我们需要进行网络请求时,会把request添加到该集合中;当请求完成后,会把request从集合中移除。当我们需要取消某个request请求时,会从该集合中找到对应的request,然后执行request.cancel()操作。

mNetworkQueue:保存需要NetworkDispatcher处理的所有request。NetworkDispatcher会轮询从该集合中取得request进行网络数据请求操作。

mWaitingRequests:保存已经添加到请求列表中但是还没处理完成的request。当我们add()一个新的请求的时候,会从该集合中进行判断该集合中是否已经存在该request了,如果是则不添加到mCacheQueue请求列表中,否则添加到请求列表中。当请求完成后,request会从该结合中移除。

mCacheQueue:保存需要CacheDispatcher处理的所有的request。

CacheDispatcher会轮询从该集合中取得request进行处理。

 

该方法对添加网络请求的流程进行了一定的优化操作。

流程如下:

把请求添加到mCurrentRequests集合中进行保存;再判断request请求是否可以缓存,如果不可以缓存则直接添加到mNetworkQueue中,否则先判断request是否已经添加到请求列表中(是否包含在mWaitingRequests中),如果没有,则添加到请求列表mCacheQueue中。该方法的逻辑处理可以防止请求多次添加到请求列表中。


类:CacheDispatcher.java

 

方法如下:

@Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //缓存初始化,会遍历整个缓存文件夹
        // Make a blocking call to initialize the cache.
        mCache.initialize();

        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
            	//获取请求
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");
                //执行代码
                // If the request has been canceled, don't bother dispatching it.
                
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // Attempt to retrieve this item from cache.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                }

                // If it is completely expired, just send it to the network.
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // We have a cache hit; parse its data for delivery back to the request.
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the 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.
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }



解析:

 

CacheDispatcher是一个继承Thread的线程类,该run方法就是线程启动后执行的方法。

线程第一次执行的时候,都会初始化缓存,缓存中有保存需要缓存的数据。然后就会进入死循环,不断轮询从mCacheQueue列表中取得request进行网络数据的请求操作。

轮询流程:

1、先从mCacheQueue队列中取得一个request请求,

2、判断若请求已经取消,则直接finish请求,进行下一次轮询;否则从缓存中取request对应的数据,继续下面的步骤。

3、判断从缓存中获得的数据若是空的,则把请求添加到mNetworkQueue队列中,进行下一次轮询。若不是空,则进行下面的步骤。

4、判断取得的数据是否已经过期了,若已经过期,则把请求添加到mNetworkQueue中重新请求数据。若没有过期则继续下面的步骤。

5、把得到的数据解析解析。再判断数据是否需要刷新,若不需要刷新,则直接把数据分发到UI线程进行处理。若数据需要刷新,则先把数据分发到UI线程,再把请求添加到mNetworkQueue,获取最新的数据。

 

这就是CacheDispatcher处理一次请求的过程,其实就是volley的缓存机制的处理。真正的网络请求操作是在NetworkDispatcher中处理。

 

类:NetworkDispatcher.java

 

方法如下:

@Override
	public void run() {
		Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
		Request<?> request;
		while (true) {
			try {
				// Take a request from the queue.
				// 获取request对象
				request = mQueue.take();
			} catch (InterruptedException e) {
				// We may have been interrupted because it was time to quit.
				if (mQuit) {
					return;
				}
				continue;
			}

			try {
				request.addMarker("network-queue-take");

				// If the request was cancelled already, do not perform the
				// network request.
				if (request.isCanceled()) {
					request.finish("network-discard-cancelled");
					continue;
				}

				addTrafficStatsTag(request);

				// Perform the network request.
				// 访问网络,得到数据
				NetworkResponse networkResponse = mNetwork
						.performRequest(request);
				request.addMarker("network-http-complete");

				// If the server returned 304 AND we delivered a response
				// already,
				// we're done -- don't deliver a second identical response.
				if (networkResponse.notModified
						&& request.hasHadResponseDelivered()) {
					request.finish("not-modified");
					continue;
				}

				// Parse the response here on the worker thread.
				Response<?> response = request
						.parseNetworkResponse(networkResponse);
				request.addMarker("network-parse-complete");
				// 写入缓存
				// Write to cache if applicable.
				// TODO: Only update cache metadata instead of entire record for
				// 304s.
				if (request.shouldCache() && response.cacheEntry != null) {
					mCache.put(request.getCacheKey(), response.cacheEntry);
					request.addMarker("network-cache-written");
				}

				// Post the response back.
				request.markDelivered();
				mDelivery.postResponse(request, response);
			} catch (VolleyError volleyError) {
				parseAndDeliverNetworkError(request, volleyError);
			} catch (Exception e) {
				VolleyLog.e(e, "Unhandled exception %s", e.toString());
				mDelivery.postError(request, new VolleyError(e));
			}
		}
	}

NetworkDispatcher是一个继承Thread的线程类,该run方法就是线程启动后执行的方法。

线程启动后,就是不断的轮询mNetworkQueue队列中的请求,进行网络数据的请求操作。

 

轮询的流程:

1、从mQueue请求队列中获取一个请求。

2、判断如果请求已经取消了,则finish这次请求。否则继续下面的步骤。

3、执行访问网络操作,获取网络返回的数据。

4、判断如果获取的网络数据没有被修改,或者请求结果已经分发过,则结束这次请求,进行下一次的轮询。否则进行下面的步骤。

5、解析从网络取得的数据,如果请求的数据需要缓存,则写到缓存中。

6、分发解析后的数据到UI线程中。

 

这就完成了一次NetworkDispatcher处理请求的过程。

值得一提的是,这样的NetworkDispatcher处理线程一共有四个,也就是说能同时进行网络数据的请求的个数是四个,只能等四个中的某一个请求处理完成后,才能处理下一个请求。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值