zoukankan      html  css  js  c++  java
  • 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:原以为这么写出来回讲的更明白一点,但真正写出来之后再从头看一遍,好像没达到预期效果啊~还得改进一下

  • 相关阅读:
    P1121 环状最大两段子段和
    无题
    cdoj 1485 柱爷搞子串 sam treap
    自然数幂和
    Gym 100341C AVL Trees NTT
    线性筛分解质因子
    codeforces 366 Ant Man dp
    UVALive 6914 Maze Mayhem 轮廓线dp
    hdu 5790 Prefix 字典树 主席树
    莫比乌斯反演个人小结
  • 原文地址:https://www.cnblogs.com/liu37130/p/3906602.html
Copyright © 2011-2022 走看看