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

  • 相关阅读:
    web----WSGI
    ovs 实现vlan隔离(一)
    ovs流表机制(四)用vxlan实现不同网段通信
    ovs流表机制(四)用vxlan实现同网段通信
    ovs 流表机制(三)--group表
    ovs 流表机制(二)-OVS流表table之间的跳转
    ovs 流表机制(一)
    euler ironic镜像驱动问题(一)镜像启动失败报dracut initqueue timeout
    ovs流表
    arm64 uefi启动
  • 原文地址:https://www.cnblogs.com/liu37130/p/3906602.html
Copyright © 2011-2022 走看看