Volley的框架解读七(Request)

先看看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));
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值