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正请求网络数据
- 大体流程: