从源码分析Android的Volley库的工作流程

Volley现在已经被官方放到AOSP里面,已经逐步成为Android官方推荐的网络框架。

类抽象
对Http协议的抽象
Requeset
顾名思义,对请求的封装,实现了Comparable接口,因为在Volley中是可以指定请求的优先级的,实现Comparable是为了在Request任务队列中进行排序,优先级高的Request会被优先调度执行。
NetworkResponse
Http响应的封装,其中包括返回的状态码 头部 数据等。
Response
给调用者返回的结果封装,它比NetworkResponse更加简单,只包含三个东西:数据 异常 和 Cache数据。
Network
对HttpClient的抽象,接受一个Request,返回一个NetworkResponse

反序列化抽象
所谓反序列化,就是将网络中传输的对象变成一个Java对象,Volley中是通过扩展Request类来实现不同的反序列化功能,如JsonRequest StringRequest,我们也可以通过自己扩展一些Request子类,来实现对请求流的各种定制。

请求工作流抽象

RequestQueue
用来管理各种请求队列,其中包含有4个队列
a) 所有请求集合,通过RequestQueue.add()添加的Request都会被添加进来,当请求结束之后删除。
b) 所有等待Request,这是Volley做的一点优化,想象一下,我们同时发出了三个一模一样的Request,此时底层其实不必真正走三个网络请求,而只需要走一个请求即可。所以Request1被add之后会被调度执行,而Request2 和Request3被加进来时,如果Request1还未执行完毕,那么Request2和 Request3只需要等着Request1的结果即可。
c) 缓存队列,其中的Request需要执行查找缓存的工作
d) 网络工作队列 其中的Request需要被执行网络请求的工作

NetworkDispatcher
执行网络Request的线程,它会从网络工作队列中取出一个请求,并执行。Volley默认有四个线程作为执行网络请求的线程。

CacheDispatcher
执行Cache查找的线程,它会从缓存队列中取出一个请求,然后查找该请求的本地缓存。Volley只有一个线程执行Cache任务。

ResponseDelivery
请求数据分发器,可以发布Request执行的结果。

Cache
对Cache的封装,主要定义了如何存储,获取缓存,存取依据Request中的getCacheKey()来描述。

提交请求
Volley通过RequestQueue.add(Request)来往任务队列中增加请求:

一个Request被提交之后有几个去处:

1.Set<Request<?>> mCurrentRequests对应所有请求队列。所有调用add的Request必然都会添加到这里面来。
2.PriorityBlockingQueue<Request<?>> mNetworkQueue 对应网络队列。如果一个Request不需要缓存,那么add之后会被直接添加到网络队列中。
3.PriorityBlockingQueue<Request<?>> mCacheQueue对应缓存请求。如果一个Request需要缓存,并且当前的RequestQueue中并没有一个Request的getCacheKey和当前Request相同(可以认为一个请求),那么加入缓存队列,让缓存工作线程来处理。
4.Map<String, Queue<Request<?>>> mWaitingRequests对应等待队列。如果RequestQueue中已经有一个相同请求在处理,这里只需要将这个Request放到等待队列中,等之前的Request结果回来之后,进行处理即可。

Volley提交任务到队列中是不是很简单?下面来说说优先级请求的事情吧,你可能已经注意到了,上面两个存放需要执行任务的队列都是PriorityBlockingQueue,前面说了Request现实了Comparable,看看这个方法:

@Override public int compareTo(Request<T> other) { Priority left = this.getPriority(); Priority right = other.getPriority(); //mSequence表示请求序列号,add时,会通过一个计数器来指定 return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal(); }

所以,如果我们的工作线程(NetworkDispatcher,CacheDispatcher)取任务时,自然会从头部开始取。

这里的优先级,仅仅是保证一个请求比另外一个请求先处理,而并不能保证一个高优先级请求一定会比低优先级的请求先回来

缓存工作线程处理

@Override public void run() { //初始化Cache mCache.initialize(); Request<?> request; while (true) { //阻塞 获取一个Cache任务 request = mCacheQueue.take(); try { //已经被取消 if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } //如果拿cache未果,放入网络请求队列 Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { request.addMarker("cache-miss"); mNetworkQueue.put(request); continue; } //缓存超时,放入网络请求队列 if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } //根据Cache构造Response Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); //是否超过软过期 if (!entry.refreshNeeded()) { // 直接返回Cache mDelivery.postResponse(request, response); } else { request.setCacheEntry(entry); //设置中间结果 response.intermediate = true; //发送中间结果 final Request<?> finalRequest = request; mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { //中间结果完事之后,讲请求放入网络队列 mNetworkQueue.put(finalRequest); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (Exception e) { } } }

这里可以看到Volley确实对缓存封装很到位,各种情况都考虑到了,其中比较重要的两点:

取出来的Cache并不仅仅是数据,同时还包括这次请求的一些Header
硬过期 软过期
我们可以看到Cache中有两个字段来描述缓存过期: Cache.ttl vs Cache.softTtl。什么区别呢?如果ttl过期,那么这个缓存永远不会被使用了;如果softTtl没有过期,这个数据直接返回;如果softTtl过期,那么这次请求将有两次返回,第一次返回这个Cahce,第二次返回网络请求的结果。想想,这个是不是满足我们很多场景呢?先进入页面展示缓存,然后再刷新页面;如果这个缓存太久了,可以等待网络数据回来之后再展示数据,是不是很赞?
NetworkDispatcher
执行网络请求的工作线程,默认有4个线程,它不停地从网络队列中取任务执行。

public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Request<?> request; while (true) { long startTimeMs = SystemClock.elapsedRealtime(); // release previous request object to avoid leaking request object when mQueue is drained. request = null; try { request = mQueue.take(); } catch (InterruptedException e) { if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); //取消 if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } //通过Http栈实现客户端发送网络请求 NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // 如果缓存软过期,那么会重新走网络;如果server返回304,表示上次之后请求结果数据本地并没有过期,所以可以直接用本地的,因为之前Volley已经发过一次Response了,所以这里就不需要再发送Response结果了。 if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); //更新缓存 if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } //发送结果 request.markDelivered(); mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); } } }

Request
Request中主要封装了一个请求的各类Http协议信息,比如 URL,请求方法,请求的优先级,请求重试的策略,缓存策略等。

这里说一下其中比较有意思的重发策略,如果一次请求发生超时异常,比如SocketTimeoutException  ConnectTimeoutException ,我们可以为Request配置一个RetryPolicy,你可以指定重发这个Request的次数,以及每次失败之后重新设置这个请求的超时时间(第一次失败之后,你可以调整第二次请求的超时时间增加,以减少失败的可能性)。

反序列化
Request最重要的功能就是提供了内容的反序列化,通过不同的子类来实现不同的序列化功能。比如,如果请求结果是一个Json的对象,我们可以使用JsonObjectRequest,如果是一个普通字符,使用StringRequest,同时,我们也可以很方便的定制自己的Request,通过复写Response<T> parseNetworkResponse(NetworkResponse response);方法即可。

默认的JsonRequest使用org.json中的Json解析,我们使用Gson来进行解析能够构造一个更加通用的处理json返回的Request:

public class JsonGRequest<T> extends Request<T> { private static Gson gson = new Gson(); private Response.Listener<T> mListener; public JsonGRequest(String url, Response.ErrorListener listener,Response.Listener responseListener) { super(url, listener); this.mListener = mListener; } public JsonGRequest(int method, String url, Response.ErrorListener listener) { super(method, url, listener); } @Override protected Response<T> parseNetworkResponse(NetworkResponse response) { return Response.success(gson.fromJson(new InputStreamReader(new ByteArrayInputStream(response.data)),getType()), HttpHeaderParser.parseCacheHeaders(response)) } @Override protected void deliverResponse(T response) { if(mListener != null) { mListener.onResponse(response); } } //获取指定的泛型类型 protected Type getType() { Type superclass; for(superclass = this.getClass().getGenericSuperclass(); superclass instanceof Class && !superclass.equals(JsonGRequest.class); superclass = ((Class)superclass).getGenericSuperclass()) { ; } if(superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } else { ParameterizedType parameterized = (ParameterizedType)superclass; return parameterized.getActualTypeArguments()[0]; } } }

ImageRequest
Volley专门为图片请求提供了ImageRequest,主要是反序列化了一下数据流到BitMap,还可以制定图片的大小,质量等参数。

ImageLoader是Volley提供的一个用来加载图片的工具,它的内部还是使用ImageRequest来实现的,主要新加的功能是增加了内存缓存,你可以通过配置ImageCache来设置内存缓存。

时间: 2024-09-11 16:20:59

从源码分析Android的Volley库的工作流程的相关文章

从源码分析Android的Volley库的工作流程_Android

Volley现在已经被官方放到AOSP里面,已经逐步成为Android官方推荐的网络框架. 类抽象 对Http协议的抽象 Requeset顾名思义,对请求的封装,实现了Comparable接口,因为在Volley中是可以指定请求的优先级的,实现Comparable是为了在Request任务队列中进行排序,优先级高的Request会被优先调度执行.NetworkResponseHttp响应的封装,其中包括返回的状态码 头部 数据等.Response给调用者返回的结果封装,它比NetworkResp

从源码分析Android的Glide库的图片加载流程及特点_Android

0.基础知识Glide中有一部分单词,我不知道用什么中文可以确切的表达出含义,用英文单词可能在行文中更加合适,还有一些词在Glide中有特别的含义,我理解的可能也不深入,这里先记录一下. (1)View: 一般情况下,指Android中的View及其子类控件(包括自定义的),尤其指ImageView.这些控件可在上面绘制Drawable (2)Target: Glide中重要的概念,目标.它即可以指封装了一个View的Target(ViewTarget),也可以不包含View(SimpleTar

从源码分析Android的Glide库的图片加载流程及特点

0.基础知识 Glide中有一部分单词,我不知道用什么中文可以确切的表达出含义,用英文单词可能在行文中更加合适,还有一些词在Glide中有特别的含义,我理解的可能也不深入,这里先记录一下. (1)View: 一般情况下,指Android中的View及其子类控件(包括自定义的),尤其指ImageView.这些控件可在上面绘制Drawable (2)Target: Glide中重要的概念,目标.它即可以指封装了一个View的Target(ViewTarget),也可以不包含View(SimpleTa

HBase源码分析之HRegion上MemStore的flsuh流程(二)

        继上篇<HBase源码分析之HRegion上MemStore的flsuh流程(一)>之后,我们继续分析下HRegion上MemStore flush的核心方法internalFlushcache(),它的主要流程如图所示:         其中,internalFlushcache()方法的代码如下: /** * Flush the memstore. Flushing the memstore is a little tricky. We have a lot of upda

Yarn源码分析之MRAppMaster上MapReduce作业处理总流程(二)

        本文继<Yarn源码分析之MRAppMaster上MapReduce作业处理总流程(一)>,接着讲述MapReduce作业在MRAppMaster上处理总流程,继上篇讲到作业初始化之后的作业启动,关于作业初始化主体流程的详细介绍,请参见<Yarn源码分析之MRAppMaster上MapReduce作业初始化解析>一文.         (三)启动         作业的启动是通过MRAppMaster的startJobs()方法实现的,其代码如下: /** * Th

Yarn源码分析之MRAppMaster上MapReduce作业处理总流程(一)

        我们知道,如果想要在Yarn上运行MapReduce作业,仅需实现一个ApplicationMaster组件即可,而MRAppMaster正是MapReduce在Yarn上ApplicationMaster的实现,由其控制MR作业在Yarn上的执行.如此,随之而来的一个问题就是,MRAppMaster是如何控制MapReduce作业在Yarn上运行的,换句话说,MRAppMaster上MapReduce作业处理总流程是什么?这就是本文要研究的重点.         通过MRApp

HBase源码分析之HRegion上MemStore的flsuh流程(一)

        了解HBase架构的用户应该知道,HBase是一种基于LSM模型的分布式数据库.LSM的全称是Log-Structured Merge-Trees,即日志-结构化合并-树.相比于Oracle普通索引所采用的B+树,LSM模型的最大特点就是,在读写之间采取一种平衡,牺牲部分读数据的性能,来大幅度的提升写数据的性能.通俗的讲,HBase写数据如此快,正是由于基于LSM模型,将数据写入内存和日志文件后即立即返回.         但是,数据始终在内存和日志中是不妥当的,首先内存毕竟是有

MySQL · 源码分析 · 无法revoke单库或单表权限

现象 对于拥有全局权限的用户,无法revoke单库或单表的权限,示例如下 mysql> grant select on *.* to 'xx1'@'localhost'; Query OK, 0 rows affected (0.00 sec) mysql> revoke select * test.* from 'xx1'@'localhost'; ERROR 1141 (42000): There is no such grant defined for user 'xx1' on hos

HBase源码分析之HRegionServer上的MovedRegionsCleaner工作线程

        MovedRegionsCleaner是什么呢?我们先来看下它在HRegionServer上的定义: /** * Chore to clean periodically the moved region list * 被移动Region列表的定期清理工作线程 */ private MovedRegionsCleaner movedRegionsCleaner;         原来它是HRegionServer上一个被移动Region列表的定期清理工作线程.而它的类的定义如下: