《Volley源码分析》Part2、Volley源码解析
来源:互联网 发布:成都网络推广公司排名 编辑:程序博客网 时间:2024/06/08 13:28
Volley是什么,怎么用,这里就不做介绍了,大家可以看我的关于Volley的使用以及介绍一文,上面是翻译官方介绍的,并且自己附加了实例代码程序。
1、整体流程整理
根据我们创建RequestQueue的过程,首先看Volley#newRequestQueue方法。
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) { BasicNetwork network; if (stack == null) { if (Build.VERSION.SDK_INT >= 9) { //在sdk版本小于9的时候使用httpClient,否则使用httpUrlConnection network = new BasicNetwork(new HurlStack()); } else { // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html // At some point in the future we'll move our minSdkVersion past Froyo and can // delete this fallback (along with all Apache HTTP code). String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } network = new BasicNetwork( new HttpClientStack(AndroidHttpClient.newInstance(userAgent))); } } else { network = new BasicNetwork(stack); } return newRequestQueue(context, network);}
如果BaseHttpStack
为空的话,会根据系统版本进行区分,sdk小于9的话,会使用httpClient,否则的话,会使用httpUrlConnection。BashHttpStack
是默认的网络请求处理逻辑,Volley支持用户自定义网络处理逻辑。BasicNetwork
是对网络请求的一个统一封装,它负责处理真实网络请求之后获取的结果的分发处理。
在上面代码中最后调用了newRequestQueue(context, network);
,也就是下面这个方法。
private static RequestQueue newRequestQueue(Context context, Network network) { File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); queue.start(); return queue;}
在newRequestQueue
中,构造了一个RequestQueue
对象,它的两个参数分别是Cache
和netWork
,可以看到这里Cache的默认实现是使用的DiskBasedCache,而network的默认实现就是上面创建的BasicNetwork。之后调用了RequestQueue#start()
,在看这个方法做了什么。
//RequestQueue#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(); }}//RequestQueue#stop()public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (final NetworkDispatcher mDispatcher : mDispatchers) { if (mDispatcher != null) { mDispatcher.quit(); } }}
可以看到这里是构造了CacheDispatcher
对象和NetworkDispatcher
对象,其实CacheDispatcher和NetworkDispatcher都是线程,继承自Thread
。在start中,首先调用了stop方法,对线程进行了停止。之后创建了CacheDispatcher对象,并且传入了mCacheQueue
,mNetworkQueue
,mCache
和mDelivery
,分别是缓存队列,网络请求队列,缓存对象和结果分发对象。其实缓存队列和网络请求队列都是使用的阻塞队列实现,而NetworkDispatcher
的创建是根据mDispatchers数组的大小进行创建,而这个队列默认初始化是4,所以会创建四个用于网络请求的线程。并且在最后对缓存线程和网络请求线程都调用了start()
,让线程进入运行状态,而由于这里使用了阻塞队列,因此这两个线程都是会在没有任务的时候一直阻塞,直到有任务到达或者调用了quit退出。
从看到代码我们知道在缓存线程(CacheDispatcher
)和网络请求线程(NetworkDispatcher
)中,都传入了一个mDispatchers
,所以在研究请求到来的流程之前,先看看这个结果分发类做了什么。
public interface ResponseDelivery { /** * Parses a response from the network or cache and delivers it. */ void postResponse(Request<?> request, Response<?> response); /** * Parses a response from the network or cache and delivers it. The provided * Runnable will be executed after delivery. */ void postResponse(Request<?> request, Response<?> response, Runnable runnable); /** * Posts an error for the given request. */ void postError(Request<?> request, VolleyError error);}
可以看到ResponseDelivery
是一个接口,内部实现了对请求结果的分发和错误分发。(在Volley中,大量的使用到了接口变成,让代码的耦合清晰明了,简直经典)而ResponseDelivery
的真实实现类是ExecutorDelivery
,可以在
public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));}
中找到。并且可以看到传入了一个Handler,并且这个Handler是使用MainLooper创建的,也就是这个Handler发送消息会传到主线程哦。可以猜测,最后的从任务线程切换到主线程就是这个Handler处理的。
而在ExecutorDelivery
中的postResponse和postError中,结果和错误实际上交给了ExecutorDelivery#ResponseDeliveryRunnable类处理,这个类是作为一个handler的消息处理的,最后根据请求是否有错误进行了分发。会传到了request的deliverResponse和deliverError,最后在Request中设置的监听会调用到最后用户处理。
if (mResponse.isSuccess()) { mRequest.deliverResponse(mResponse.result);} else { mRequest.deliverError(mResponse.error);}
到目前我们知道了RequestQueue做了什么操作,这里简单总结一下:
RequestQueue中创建了1个缓存线程和4个网络请求线程,以及一个结果回传分发任务,并且对缓存线程和网络请求线程都进行了运行,让他们一直处于阻塞等待状态。当有任务加入到mCacheQueue或者mNetworkQueue中的时候,线程就会开始运行处理逻辑了。而ResponseDelivery作为结果分发类,它的对象被传入了缓存线程和网络请求任务线程中,在ResponseDelivery中分别有正确结果分发和错误分发操作。
你现在可能很急于知道当有一个request加入到RequestQueue的时候,具体做了什么操作?下面马上就说这部分内容。
Volley内建提供了StringRequest、JsonObjectRequest、JsonArrayRequest,好像最近也加入了GsonRequest(因为看isuue中有提到)。他们其实都是Request<T>
的子类。
当有一个Request发起请求时,只需要调用RequestQueue的public <T> Request<T> add(Request<T> request)
方法即可。下面看下这个方法。
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; } mCacheQueue.add(request); return request; }
从代码可以看到,首先会给request绑定一个requestQueue,之后加入到mCurrentRequests队列中,这个mCurrentRequests队列在这里添加之后,会在finish和cancel的时候移除,实际上感觉是维系了一个请求队列的整体描述队列吧,方便在RequestQueue中找到要处理的request。之后判断是否需要做缓存处理,Volley默认shouldCache是true。因此会先添加到mCacheQueue中。那么我们就来看看CacheDispatcher的run方法中做了什么处理吧。
这里代码还是比较多的,我把Volley自己添加日志打点的代码去掉了,并对格式缩行了。可以先看一下代码,代码加了注释。
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(); //这里是对cache的初始化,存贮路径不存在的话,会创建缓存文件存贮路径,如果存在的的话则会扫描所有文件,映射到一个map中。 while (true) { //这里构造了一个死循环,退出条件是线程出现中断。(报InterruptedException) try { // Get a request from the cache triage queue, blocking until // at least one is available. //这里是从缓存队列中取出来request,因为mCacheQueue是一个阻塞队列,因此这里如果队列为空的话, take会一直阻塞,直到有Request来。 final Request<?> request = mCacheQueue.take(); // If the request has been canceled, don't bother dispatching it. if (request.isCanceled()) { //如果取消了的话,则调用request.finish将自己从RequestQueue中移除。并跳过此次任务。 request.finish("cache-discard-canceled"); continue; } // Attempt to retrieve this item from cache. Cache.Entry entry = mCache.get(request.getCacheKey()); //从缓存中取出来缓存对象,如果不存在,判断这个请求是否已经有相同缓存key值得请求在执行中(in flight),没有的话,则添加到mNetworkQueue中,执行网络请求。(对于这块理解可能不准确,欢迎讨论) if (entry == null) { request.addMarker("cache-miss"); // Cache miss; send off to the network dispatcher. if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) { mNetworkQueue.put(request); } continue; } // If it is completely expired, just send it to the network. if (entry.isExpired()) { request.setCacheEntry(entry); if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) { mNetworkQueue.put(request); } continue; } // We have a cache hit; parse its data for delivery back to the request. //从缓存中解析出来Response,之后调用mDelivery进行分发。 Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); 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.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) { // 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) { // Restore the interrupted status Thread.currentThread().interrupt(); } } }); } else { // request has been added to list of waiting requests // to receive the network response from the first request once it returns. mDelivery.postResponse(request, response); } } } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } } }}
代码还是很清晰的,简单总结一下:
拿到一个request之后,首先判断这个请求是否存在缓存,是否过期,如果过期的话,并且没有正在运行的相同key的任务,就添加到网络请求中,进行请求。如果有缓存的话,则调用parseNetworkResponse解析出来一个Response对象,并调用mDelivery进行结果的分发。
再看一下NetworkDispatcher的流程吧。
这里列出来核心代码
NetworkResponse networkResponse = mNetwork.performRequest(request); //实际上是调用了networkStact的performRequest,....Response<?> response = request.parseNetworkResponse(networkResponse); //解析请求到的结果
它的代码相比简单很多,因为真实的请求是BaseHttpStack以及请求封装类BasicNetworky。
自己整理的Volley请求流程图,看着可能比较乱,可以先简单看一下,阅读完文字分析之后再看一下,应该会比较容易能理解整体流程。
- 《Volley源码分析》Part2、Volley源码解析
- 【Volley】Volley源码解析
- volley源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- 有return 0 为什么出错还正常显示;为什么不溢出显示-32768
- 51nod 1018 排序
- Linux文件系统之一:inode节点和inode节点包含的block寻址信息
- git fatal: Unable to find remote helper for 'https'
- SSH与SSM学习之hibernate20——多对多操作
- 《Volley源码分析》Part2、Volley源码解析
- LeetCode 169. Majority Element (Easy)
- 插件化实践
- Python读取系统文件夹内所有文件并统计数量
- MongoDB缓存技术总结
- 最短路径
- java学习基础第一篇
- Python获取指定目录下文件数量及总大小
- c# 基础 一些与c++不同的