Volley详解+源码分析

Posted guangdeshishe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Volley详解+源码分析相关的知识,希望对你有一定的参考价值。

基于1.2.1版本
github地址: https://github.com/google/volley

简介

简单它说,它就是谷歌官方用于更加方便发起网络请求的一个开源框架,它据有以下特点:

  • 自动调度网络请求
  • 并发请求网络(默认支持4个线程同时请求网络)
  • 支持网络数据缓存到本地(可自定义缓存策略)
  • 支持优先级设置(需要重写Request的getPriority() 方法)
  • 支持取消网络请求
  • 支持自定义网络请求方式(可改为OkHttp)
  • 默认支持返回结果类型为String/Json/JsonArray的请求(可自定义数据解析转换过程)

简单使用

1、build.gradle添加依赖

implementation 'com.android.volley:volley:1.2.1'

2、创建RequestQueue;一般全局只需要创建一个,用于管理所有的网络请求

  • 快速创建:

    var queue = Volley.newRequestQueue(this)
    //默认缓存大小:5MB;默认缓存路径:Context.getCacheDir()+"/volley"
    //默认HttpURLConnection实现
    
  • 自定义创建:

    val cache = DiskBasedCache(File("/sdcard/download/"), 1024 * 1024)//创建缓存策略,缓存大小1M,可自定义;
    val network = BasicNetwork(HurlStack())//HttpURLConnection实现,可自定义OkHttp实现
    val queue = RequestQueue(cache, network).apply { start() }//创建RequestQueue并开启循环
    

3、创建单次Request网络请求;默认自带的有StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest;不同类型的Request返回的数据类型不同

val url = "https://www.tianqiapi.com/api/?version=v1"
val request =
	StringRequest(Request.Method.GET, url, { response ->
		mResultTextView.text = response
	}, {
		mResultTextView.text = "error:${it.networkResponse}"
	})

4、发送请求;先从缓存中查找,如果没有缓存则从网络获取并缓存到本地

queue.add(request)

5、取消请求

request.tag = "test"//1.给请求添加标记
queue.cancelAll("test")//2.取消带某个标记的请求

6、自定义请求GsonRequest,将请求回来的数据通过Gson自动转换为某个对象类型,官方示例:

    /**
     * Make a GET request and return a parsed object from JSON.
     *
     * @param url URL of the request to make
     * @param clazz Relevant class object, for Gson's reflection
     * @param headers Map of request headers
     */
    class GsonRequest<T>(
            url: String,
            private val clazz: Class<T>,
            private val headers: MutableMap<String, String>?,
            private val listener: Response.Listener<T>,
            errorListener: Response.ErrorListener
    ) : Request<T>(Method.GET, url, errorListener) {
        private val gson = Gson()

        override fun getHeaders(): MutableMap<String, String> = headers ?: super.getHeaders()

        override fun deliverResponse(response: T) = listener.onResponse(response)

        override fun parseNetworkResponse(response: NetworkResponse?): Response<T> {
            return try {
                val json = String(
                        response?.data ?: ByteArray(0),
                        Charset.forName(HttpHeaderParser.parseCharset(response?.headers)))
                Response.success(
                        gson.fromJson(json, clazz),
                        HttpHeaderParser.parseCacheHeaders(response))
            } catch (e: UnsupportedEncodingException) {
                Response.error(ParseError(e))
            } catch (e: JsonSyntaxException) {
                Response.error(ParseError(e))
            }
        }
    }
    

源码分析

Volley.newRequestQueue:创建请求队列

1:newRequestQueue(Context context)

public static RequestQueue newRequestQueue(Context context) {
   return newRequestQueue(context, (BaseHttpStack) null);
}

2:newRequestQueue(Context context, BaseHttpStack stack)

public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
	BasicNetwork network;
	if (stack == null) {
		if (Build.VERSION.SDK_INT >= 9) {
			network = new BasicNetwork(new HurlStack());
		} else {
			network = new BasicNetwork(new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
		}
	} else {
		network = new BasicNetwork(stack);
	}

	return newRequestQueue(context, network);
}
  • 如果Android SDK版本大于等于9,则使用【HttpURLConnection】实现的【HurlStack】,低于这个版本就是用【HttpClient】实现的【HttpClientStack】,但是这个已经过时很久了。

3:newRequestQueue(Context context, Network network)

private static final String DEFAULT_CACHE_DIR = "volley";
private static RequestQueue newRequestQueue(Context context, Network network) {
	DiskBasedCache.FileSupplier cacheSupplier =
			new DiskBasedCache.FileSupplier() {
				private File cacheDir = null;

				@Override
				public File get() {
					if (cacheDir == null) {
						cacheDir = new File(appContext.getCacheDir(), DEFAULT_CACHE_DIR);
					}
					return cacheDir;
				}
			};
	RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheSupplier), network);
	queue.start();
	return queue;
}
  • 从这段代码我们可以知道,默认数据缓存路径是:Context.getCacheDir()+"/volley"
  • 创建RequestQueue时传入了两个参数:
    • DiskBasedCache就是默认的缓存策略
    • network就是上一步传入的BasicNetwork,也就是网络请求实现类
  • 接着就调用RequestQueue的start方法管理所有添加进来的Request请求

RequestQueue.start:开启请求队列循环

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();
	}
}
  • CacheDispatcher和NetworkDispatcher 都是继承自Thread;它们内部维护着一个PriorityBlockingQueue;从中不断获取Request进行处理,直到退出为止。

  • 同一个RequestQueue都是共用一个CacheDispatcher,而NetworkDispatcher则有多个,也就是说网络请求这个步骤是多个线程并发请求的;通过下面这段代码,可以看出网络请求默认最大并发数是4。

    class RequestQuee{
    	//用于缓存的优先级阻塞队列
    	private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<>();
    
    	//用于网络请求的优先级阻塞队列
    	private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<>();
    	//默认网络请求线程数数量
    	private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    	
    	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.add:添加Request

1:add(Request request)

private final Set<Request<?>> mCurrentRequests = new HashSet<>();
private final AtomicInteger mSequenceGenerator = new AtomicInteger();
public <T> Request<T> add(Request<T> request) {
   //把当前RequestQueue传递给Request
    request.setRequestQueue(this);
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);//用于保存所有的Request
    }

    // 给Request设置序列号,AtomicInteger自增,用于请求的优先级
    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");//添加事件Log
    sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);//回调事件监听器

    beginRequest(request);
    return request;
}
public int getSequenceNumber() {
 return mSequenceGenerator.incrementAndGet();
}
void sendRequestEvent(Request<?> request, @RequestEvent int event) {
    synchronized (mEventListeners) {
        for (RequestEventListener listener : mEventListeners) {
            listener.onRequestEvent(request, event);
        }
    }
}
  • 默认的请求优先级是按照Request添加顺序来的,可以通过Request.setSequence方法改变优先级
  • sendRequestEvent方法用于发送事件给监听者RequestEventListener

2:RequestQueue.beginRequest

 <T> void beginRequest(Request<T> request) {
	// 判断是否需要跳过读取本地缓存
	if (!request.shouldCache()) {
		sendRequestOverNetwork(request);
	} else {
		mCacheQueue.add(request);
	}
}
<T> void sendRequestOverNetwork(Request<T> request) {
   mNetworkQueue.add(request);
}
  • 如果需要优先读取缓存数据,则将Request放入mCacheQueue阻塞队列中;CacheDispatcher分发器会不断的从mCacheQueue队列中获取Request,根据Request查找本地缓存,如果缓存没过期则使用缓存,如果缓存过期则将Request放到mNetworkQueue队列中重新请求网络获取数据
  • 如果不需要读取缓存数据,则直接将Request放入用于网络请求的mNetworkQueue队列中;NetworkDispatcher分发器会不断的从mNetworkQueue队列中获取Request并请求网络
  • 可通过Request.setShouldCache方法设置跳过缓存,默认是支持读取缓存的

CacheDispatcher和NetworkDispatcher

CacheDispatcher:缓存请求分发器

1:CacheDispatcher.run

class CacheDispatcher extends Thread {
	private final Cache mCache;//缓存集合
	private volatile boolean mQuit = false;//退出标记
	public void run() {
		...
        // 初始化缓存数据集合
        mCache.initialize();
        while (true) {
            try {
                processRequest();
            } catch (InterruptedException e) {
                //退出标记为true时则跳出循环结束线程.
                if (mQuit) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }
    public void quit() {
        mQuit = true;
        interrupt();
    }
}
  • CacheDispatcher 继承自Thread
  • Cache默认实现类:DiskBasedCache

2:processRequest()

private void processRequest() throws InterruptedException {
    final Request<?> request = mCacheQueue.take();
    processRequest(request);
}
  • 从阻塞队列mCacheQueue中不断的获取Request并处理

3: processRequest(final Request<?> request)

void processRequest(final Request<?> request) throws InterruptedException {
	...
	try {
		//如果请求已经取消则跳过
		if (request.isCanceled()) {
			request.finish("cache-discard-canceled");
			return;
		}
		//从缓存中读取数据
		Cache.Entry entry = mCache.get(request.getCacheKey());
		if (entry == null) {
			request.addMarker("cache-miss");
			//缓存数据不存在,将Request添加到mNetworkQueue队列中
			if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
				mNetworkQueue.put(request);
			}
			return;
		}
		long currentTimeMillis = System.currentTimeMillis();
		// 判断缓存数据是否过期,如果过期了则将Request添加到mNetworkQueue队列中
		if (entry.isExpired(currentTimeMillis)) {
			request.addMarker("cache-hit-expired");
			request.setCacheEntry(entry);
			if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
				mNetworkQueue.put(request);
			}
			return;
		}
		Response<?> response =
				request.parseNetworkResponse(
						new NetworkResponse(entry.data, entry.responseHeaders));
		request.addMarker("cache-hit-parsed");
		//解析缓存数据,如果解析失败或者是请求失败的缓存数据,则将Request添加到mNetworkQueue队列中
		if (!response.isSuccess()) {
			request.addMarker("cache-parsing-failed");
			mCache.invalidate(request.getCacheKey(), true);
			request.setCacheEntry(null);
			if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
				mNetworkQueue.put(request);
			}
			return;
		}
		if (!entry.refreshNeeded(currentTimeMillis)) {
			//判断缓存数据是否需要刷新,如果不需要则直接分发结果
			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;

			if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
				// 分发缓存结果并立刻发起一次网络请求以更新数据
				mDelivery.postResponse(
						request,
						response,
						new Runnable() {
							@Override
							public void run() {
								try {
									mNetworkQueue.put(request);
								} catch (InterruptedException e) {
									// Restore the interrupted status
									Thread.currentThread().interrupt();
								}
							}
						});
			} else {
				// 当前已经存在一个网络请求,则直接返回结果
				mDelivery.postResponse(request, response);
			}
		}
	} finally {
		request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
	}
}
  • WaitingRequestManager.maybeAddToWaitingRequests用于判断当前Request是否有重复;返回false表示当前没有重复的正在请求网络的Request;返回true则表示已经有相同的Request正请求网络数据
  • 大体流程: