Volley的框架解读七(Request)

来源:互联网 发布:软考数据库工程师 编辑:程序博客网 时间:2024/05/22 17:00

先看看UML图

request

 

Request也是volley的核心类之一,是一个抽象类。volley中所有的请求都是由Request的子类来完成的,它定义了一些公共性的内容,并抽象出来,其子类只需要继承该类并实现其预留的抽象方法即可。比如volley默认实现的请求如StringRequest、JsonRequest、JsonObjectRequest等都是继承自Request。再比如在Google的官方文档中定义的GsonRequest也是继承该类。如果有需要开发者也可以自己继承Request,定制自己的请求。扩展性极好。

OK,进入正题,接下来主要记录下自己对于Request的一些理解。

其实从字面理解Request,是请求的意思,有很长一段时间我都把它认为是一个网络请求,后来随着学习的深入,才了解到,不能把他归为一个请求,更准确的说应该是:对一个网络请求所必须的属性参数的描述。可以这么说,Request中描述了一个网络请求所需要的请求方式(post get等)、参数、编码方式、url等。

另外Request还定义了一个请求在volley中特有的属性,比如该request的序列号、是否可以被缓存、请求是否被取消了、请求的响应时间、请求的tag、数据体等

接下来看一下Request的源码:

volley支持的网络请求方式:

 

public interface Method {          int DEPRECATED_GET_OR_POST = -1;          int GET = 0;          int POST = 1;          int PUT = 2;          int DELETE = 3;      }

Request的构造方法:在创建request的时候需要传入请求方式、目标url和请求失败的监听。而在内部还定义了默认的请求重试策略new DefaultRetryPolicy()

public Request(int method, String url, Response.ErrorListener listener) {          mMethod = method;          mUrl = url;          mErrorListener = listener;          setRetryPolicy(new DefaultRetryPolicy());          mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode();      }

另外还对外提供获取请求方式的方法getMethod() 、获取tag的方法getTag() 设置重试策略的方法public void setRetryPolicy(RetryPolicy retryPolicy)等。

当一个request完成请求或是失败时,有一个finish方法,作用就是在请求队列中移除request。

 

    /**      * Notifies the request queue that this request has finished (successfully or with error).      * 当请求成功或失败后,根据tag结束该请求      * <p>Also dumps all events from this request's event log; for debugging.</p>      */      void finish(final String tag) {          if (mRequestQueue != null) {              mRequestQueue.finish(this);          }      }该方法其实主要是在RequestQueue内实现的,如下:    /**      * Called from {@link Request#finish(String)}, indicating that processing of      * the given request has finished.      *       * 告诉请求队列,这个请求已经完成了。这个方法是在request内的finish方法调用的(注意请求完成(finish)和请求取消(cancel)      * 是不同的概念,完成表示已经发送完网络请求并且拿回了数据,取消表示正在发送网络请求或拿数据但是还没完成)      *       * (1). 首先从正在进行中请求集合mCurrentRequests中移除该请求。 (2).      * 然后查找请求等待集合mWaitingRequests中是否存在等待的请求      * ,如果存在,则将等待队列移除,并将等待队列所有的请求添加到缓存请求队列中,让缓存请求处理线程CacheDispatcher自动处理。      * <p>      * Releases waiting requests for <code>request.getCacheKey()</code> if      * <code>request.shouldCache()</code>.      * </p>      */      void finish(Request request) {          // Remove from the set of requests currently being processed.          //加锁,是因为多线程都有可能操作这个集合          synchronized (mCurrentRequests) {              // 在当前对队列中将该请求给移除              mCurrentRequests.remove(request);          }          if (request.shouldCache()) {              // 如果该请求是可以被缓存的              synchronized (mWaitingRequests) {                  String cacheKey = request.getCacheKey();                  // 从等待队列中获取该key对应的所有的请求                  Queue<Request> waitingRequests = mWaitingRequests                          .remove(cacheKey);                  if (waitingRequests != null) {                      if (VolleyLog.DEBUG) {                          VolleyLog                                  .v("Releasing %d waiting requests for cacheKey=%s.",                                          waitingRequests.size(), cacheKey);                      }                      // Process all queued up requests. They won't be considered                      // as in flight, but                      // that's not a problem as the cache has been primed by                      // 'request'.为什么要添加到缓存队列中,这是因为当一个请求已经完成,并且获取到了响应,就会将该请求标记为已完成也就是说需要再当前的请求队列中finish掉该请求,而在等待队列中存在的那些相同url的请求,就不需要再次发送网络请求了                      // ,而认为是可以获取之前的请求的数据,因此需要将这些相同的请求从等待队列中移除,添加到缓存队列中,这样这些请求就会直接从缓存队列中获取数据,而不用再去发送网络请求了                      mCacheQueue.addAll(waitingRequests);                  }              }          }      }

需要注意的是,在Request中还定一了一个用于缓存的字段cacheEntry ,以及对应的cacheKey.。 在volley中默认的cacheKey是该request的url,这样能保证唯一性和重复请求的确定性,就是说当有多个请求是同一个url的时候,我们可以把除了第一个request外的其他request都放到等待队列中,这样就不用重复请求相同的url了,只需要请求一次,其他使用缓存就行了。而cachekey是判断是否同一request的标志。

    /**      * Returns the cache key for this request.  By default, this is the URL.      * 获得该Request的cachekey  该key用于在缓存时当做键来使用      */      public String getCacheKey() {          //把请求的url当做key          return getUrl();      }

大家都知道,当请求方式是post的时候,请求的参数是需要放在请求体中的,在Request中也提供了供开发者传入参数的方法getParams(),如果需要传入参数开发者需要重写该方法。

    /**      * 当是POST  PUT请求的是时候,在请求时添加的参数需要在这里进行传递,也就是说当是POST的时候,想要传递参数就要重写getParams()方法      * Returns a Map of parameters to be used for a POST or PUT request.  Can throw      * {@link AuthFailureError} as authentication may be required to provide these values.      *      * <p>Note that you can directly override {@link #getBody()} for custom data.</p>      *      * @throws AuthFailureError in the event of auth failure      */      protected Map<String, String> getParams() throws AuthFailureError {          return null;      }

在Request中定义了两个抽象方法,需要子类来实现:

    /**     * 子类重写此方法,将网络返回的原生字节内容,转换成合适的类型。此方法会在工作线程中被调用。     *      * Subclasses must implement this to parse the raw network response     * and return an appropriate response type. This method will be     * called from a worker thread.  The response will not be delivered     * if you return null.     * @param response Response from the network     * @return The parsed response, or null in the case of an error     */     abstract protected Response<T> parseNetworkResponse(NetworkResponse response);     /**     * 子类重写此方法,将解析成合适类型的内容传递给它们的监听回调。     *      * Subclasses must implement this to perform delivery of the parsed     * response to their listeners.  The given response is guaranteed to     * be non-null; responses that fail to parse are not delivered.     * @param response The parsed response returned by     * {@link #parseNetworkResponse(NetworkResponse)}     */     abstract protected void deliverResponse(T response);

通过上边两个抽象方法,开发者可以定制自己的request。比如简单的StringRequest

StringRequest

public class StringRequest extends Request<String> {    private final Listener<String> mListener;    public StringRequest(int method, String url, Listener<String> listener,            ErrorListener errorListener) {        super(method, url, errorListener);        mListener = listener;    }    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {        this(Method.GET, url, listener, errorListener);    }    @Override    protected void deliverResponse(String response) {        mListener.onResponse(response);    }    /* 完成将byte[]到String的转化,     * 可能会出现字符乱码,     * HttpHeaderParser.parseCharset(response.headers)方法在尚未指定是返回为ISO-8859-1,     * 可以修改为utf-8*/    @Override    protected Response<String> parseNetworkResponse(NetworkResponse response) {        String parsed;        try {            //将data字节数据转化为String对象            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));        } catch (UnsupportedEncodingException e) {            parsed = new String(response.data);        }        //返回Response对象,其中该对象包含访问相关数据        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));    }
原创粉丝点击