Volley源码学习(一)四个基础类

本人初学,如有纰缪,望指正~  

  按照上一篇博客结尾时的图(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表示

 

  Request的函数大大小小有30多个,有些Getter,Setter还有日志相关的方法看看名字就能知道是干什么的。由于mSequence决定了不同request的优先级,所以mSequence的Setter和Getter是不允许子类覆盖的。关于mShouldCache的方法也被设置为final。
 
  剩下的就是
  public int compareTo(Request<T> other),从源码可以看出,Volley是先比较优先权,如果优先权相同再比较序列号。至于为什么要实现Comparable接口,看到RequestQueue时就知道啦。
 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 Map<String, String> getHeaders() throws AuthFailureError
  如果需要额外的Http头信息,可以在这个函数实现(比如加个Cookie什么的)
  2.protected Map<String, String> getParams() throws AuthFailureError
  如果需要提交数据(如Post登陆信息),可以覆盖这个函数,在Map中加入自己的数据
  3.public byte[] getBody() throws AuthFailureError
  这个函数默认会获取getParams(),并把数据转成byte[],如果需要其他类型的提交数据,那就覆盖它吧。
 
  子类必须实现的方法
  1.abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
  无论从函数名、参数类型还是返回值类型,这个函数都透着浓浓的中间人气质,对,它就是网络原始数据和需要展示的数据之间的桥梁,把原始数据解析成需要的类型,具体怎么实现就要看具体的需要了。这个函数会在NetworkDispathcer中被调用。
  2.abstract protected void deliverResponse(T response);
  它的参数来自于parseNetworkResponse()的返回值,负责分发解析好的response,这个函数在ResponseDelivery中被Handler.post到主线程执行。
 
  具体子类是实现可以看一下StringRequest,一个超级简单的Request子类。如下:
 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:原以为这么写出来回讲的更明白一点,但真正写出来之后再从头看一遍,好像没达到预期效果啊~还得改进一下

原文地址:https://www.cnblogs.com/liu37130/p/3906602.html