本人初学,如有纰缪,望指正~
按照上一篇博客结尾时的图(Volley的基本使用),我们分四部分来读Volley的源码(工具类,如ImageLoader并没有出现在图中),自底向上,先看四个基础类(接口):Request、Response、Cache.Entry、HttpStack。这些基础类(接口)都是被操作的对象(比如NetworkDispatcher会根据Request的状态来决定下一步操作),所以感觉主要的目光应该放在对象的状态,也就是变量上。
一、Request
Request是一个比较核心的抽象类,几乎所有操作都围绕着Request进行。在此基础上可以拓展出各种各样的Http请求。
变量名 | 变量类型 | 描述 | 哪能用到 |
mSequence
|
Integer
|
request在请求队列中的序列号 | 用于实现Request的Comparable接口,是Request进行优先级判断的依据之一 |
mRequestQueue
|
RequestQueue
|
request所在的请求队列 |
Request会在自己的finish()函数中调用RequestQueue.finish()把自己从请求队列中删除,表示请求已经处理完了。
|
mShouldCache
|
boolean
|
是否应该被缓存的标志位 |
什么时候被标记:默认为true,可以调用setShouldCache()设置,Volley中默认的都是需要缓存的
什么时候被检查:NetworkDispathcer在完成Http通信,获取解析过的response后会调用request.shouldCache()方法检查本标志位决定是否放入缓存
|
mCanceled
|
boolean | 是否已经被取消的标志位 | 这个标志位是实现“Volley guarantees that your response handler will never be called”的主要依据。
什么时候被标记:调用request.cancel()和requestQueue.cancelAll()
什么时候被检查:CacheDispatcher会在从缓存队列取出一个request之后立即调用request.isCanceled()检查该是否被取消 如果为true调用requestfinish 并开始取出下一个request(在NetworkDospatcher中会有同样的检查),没被取消就会执行网络通信了
|
mResponseDelivered | boolean | 是否已经分发回主线程 |
什么时候被标记:在检查过mShouldCache之后,真正调用Delivery分发结果之前,调用request.markDelivered()设置该标志位为true。
什么时候被检查:执行网络通信得到响应之后,会调用request.hasHadResponseDelivered()方法检查,同时还会检查Http返回状态码是否为304(Not Modified),如果二者都为真,直接request.finish()
|
mRequestBirthTime | long | 记录request的开始时间 初始值为0 |
什么时候被设置:在addMarker()中被设置初始值——SystemClock.elapsedRealtime() 也就是自开机开始经过的时间数(包括睡眠时间)
什么时候被调用:在finish()中会再次调用SystemClock.elapsedRealtime()减去mRequestBirthTime来计算request的耗时,并根据SLOW_REQUEST_THRESHOLD_MS判断是否为慢请求。如果为慢请求会在Log中输出
|
mRetryPolicy
|
RetryPolicy
|
如果request在请求失败是需要重试,规定了如何重试 |
相关方法:getTimeoutMs() getRetryPolicy() setRetryPolicy()
|
mCacheEntry
|
Cache.Entry
|
||
mTag
|
Object | request的标签 |
可以跟踪request的状态,尤其是取消request的时候
|
DEFAULT_PARAMS_ENCODING
|
String | 默认的Http参数编码方式:UTF-8 | |
SLOW_REQUEST_THRESHOLD_MS
|
long | 慢请求的判断阈值,默认3000ms | |
mEventLog
|
MarkerLog
|
负责追踪request的状态
|
|
mMethod
|
int
|
Http请求的方法 | |
mUrl
|
String | Http请求的Url | |
mDefaultTrafficStatsTag
|
int | 在NetworkDispatcher的addTrafficStatsTag()使用 | |
mErrorListener
|
Response.ErrorListener
|
出错监听 | 如果网络连接抛出IOException,Volley会根据状态吗生成不同的error类型,调用request的deliverError(),进而调用mErrorListener中的onErrorResponse() |
要注意的是:Request是抽象类,其中没有定义如何处理解析后的响应,也就不需要Response.Listener接口。这需要在具体的子类中实现
名称 | 类型 | 描述 |
Priority | enum | request的优先级:LOW,NORMAL,HIGH,IMMEDIATE |
Method | interface | 定义了常见的Http请求类型,用int表示 |
1 @Override 2 public int compareTo(Request<T> other) { 3 Priority left = this.getPriority(); 4 Priority right = other.getPriority(); 5 6 // High-priority requests are "lesser" so they are sorted to the front. 7 // Equal priorities are sorted by sequence number to provide FIFO ordering. 8 return left == right ? 9 this.mSequence - other.mSequence : 10 right.ordinal() - left.ordinal(); 11 }
1 public class StringRequest extends Request<String> { 2 private final Listener<String> mListener; 3 4 /** 5 * Creates a new request with the given method. 6 * 7 * @param method the request {@link Method} to use 8 * @param url URL to fetch the string at 9 * @param listener Listener to receive the String response 10 * @param errorListener Error listener, or null to ignore errors 11 */ 12 public StringRequest(int method, String url, Listener<String> listener, 13 ErrorListener errorListener) { 14 super(method, url, errorListener); 15 mListener = listener; 16 } 17 18 /** 19 * Creates a new GET request. 20 * 21 * @param url URL to fetch the string at 22 * @param listener Listener to receive the String response 23 * @param errorListener Error listener, or null to ignore errors 24 */ 25 public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) { 26 this(Method.GET, url, listener, errorListener); 27 } 28 29 @Override 30 protected void deliverResponse(String response) { 31 mListener.onResponse(response); 32 } 33 34 @Override 35 protected Response<String> parseNetworkResponse(NetworkResponse response) { 36 String parsed; 37 try { 38 parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); 39 } catch (UnsupportedEncodingException e) { 40 parsed = new String(response.data); 41 } 42 return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); 43 } 44 }
二、Response
这里要说的Response包括Response类和NetworkResponse类两个类,放在一起纯粹是因为它俩都是Response,从类的角度,二者没有任何关系。
NetworkResponse很简单,因为它只是单纯的承载响应数据,所以除了构造函数之外就只有四个变量了:Http状态码、头信息、byte数组的响应数据、还有判断是缓存是否需要更新的标志位notModified。
Response的内容比NetworkResponse能稍多一点。
变量名 | 类型 | 描述 | 哪能用到 |
result
|
T(这个T是由Response初始化时指定的类型) 如:private Response(T result, Cache.Entry cacheEntry) |
剔除了Http相关信息的响应数据 | 在Request的parseNetworkResponse()中调用Response构造函数生成,在request的deliverResponse()中作为参数传入,最终通过ResponseDelivery和Response.Listener回到主线程,供主线程使用。 |
cacheEntry
|
Cache.Entry
|
此响应的缓存入口 | 在Request的parseNetworkResponse()中调用Response构造函数生成,在NetworkDispatcher中存入Cache |
error
|
VolleyError
|
错误 | 在NetworkDispatcher遇到异常时,调用ResponseDelivery的postError()函数生成一个包含出错信息的Response |
intermediate
|
boolean
|
标志位 | CacheDispatcher中会判断缓存是否软过期(当前没过期,但马上就要过期了,需要刷新缓存),如果是软过期,此位为真,在分发响应结果时(ResponseDelivery中),对应的request会被加入网络请求队列。 |
名称 | 类型 | 描述 |
Listener<T>
|
interface | 只有一个函数public void onResponse(T response);一般会在request的deliverResponse()中调用 |
ErrorListener<T>
|
interface | 只有一个函数public void onErrorResponse(T response);一般会在request的deliverError()中调用 |
最后,Response中的函数只有一个:public boolean isSuccess(),用于ResponseDelivery判断是应该postResponse还是postError。
三、HttpStack
HttpStack是一个接口,只有一个方法:
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError;
Volley中有两个HttpStack的实例:HurlStack(基于java.net.HttpURLConnection)和HttpClientStack(基于org.apache.http.client.HttpClient),可以根据系统的不同版本选一个。
HurlStack的基本流程:
1.补全Http头信息:Cache和自定义的头部信息(调用request.getHeaders()获得)
2.对Url进行过滤(由内部接口UrlRewriter实现,可选)
3.根据请求类型设置Http Body(调用request.getPostBody()获得)
4.设置连接参数、发出请求,记录Http响应头和响应数据
5.返回HttpResponse类型的数据。
有以后有特殊需求的话就可以自己按照这个流程实现HttpSatck了。
四、Cache.Entry
Cache.Entry是缓存数据的真身,HttpHeaderParser中的parseCacheHeaders()函数和NetworkResponse类型的响应数据是它的父母。
1 public static class Entry { 2 //需要缓存的数据 3 public byte[] data; 4 5 //缓存一致性要用到的Tag 6 public String etag; 7 8 //缓存返回时的服务器时间 9 public long serverDate; 10 11 //TTL 12 public long ttl; 13 14 //用于判断软过期的TTL 15 public long softTtl; 16 17 //返回的响应头信息,不能为空且不能改变 18 public Map<String, String> responseHeaders = Collections.emptyMap(); 19 20 //判断是否过期 21 public boolean isExpired() { 22 return this.ttl < System.currentTimeMillis(); 23 } 24 25 //判断是否软过期 26 public boolean refreshNeeded() { 27 return this.softTtl < System.currentTimeMillis(); 28 } 29 }
Entry只是单纯的存下了缓存数据,至于实现Cache key来找到对应的Entry,就是实现Cache接口的子类的事儿了。
PS:原以为这么写出来回讲的更明白一点,但真正写出来之后再从头看一遍,好像没达到预期效果啊~还得改进一下