Google网络请求框架Volley源码浅析(四)

上一篇中,我们分析了Request执行请求的详细过程,可以看到最后ResponseDelivery把请求结果交给了Request的deliverResponse方法,大概意思就是说,我已經吧请求结果带到主线程来了,以后的路就要靠你Request自己了,OK,那接下来我们就先来看看Request。


Request

Request一看就是一个抽象类,实现了Comparable,可见每个Request是可以比较的,具体比较什么我们待会儿看到compareTo方法的时候就明白了。我们这里还是按照老方式,先分析成员,后分析方法。
不分静态或者非静态,成员主要有这么几个:

  • DEFAULT_PARAMS_ENCODING:默认的POST或者GET的参数编码集,也就是说默认情况下我们发送或者接收的参数,都是用UTF-8来编码。
  • mMethod:该Request的请求方式,也就是我们常见的HTTP八种请求方式,取值为这个Method接口中的常量之一。
  • mUrl:请求的URL地址,不废话。
  • mRedirectUrl:重定向URL,当我们收到3XX系列的返回码的时候,就会从返回头中取出重定向URL赋值给mRedirectUrl,接着就按照mRedirectUrl重新去请求资源。
  • mIdentifier:该Request的唯一标识,用于在茫茫Request海中找到那个属于你的Request。
  • mDefaultTrafficStatsTag:用于流量统计的Tag标记,关于流量统计的机制,我们在之前的篇幅中已经介绍过了。
  • mErrorListener:用脚想就知道是我们创建Request的时候创建的那个ErrorListener。
  • mSequence:排序的序号,这个上面的mIdentifier还是不一样的,别搞混了。
  • mRequestQueue:不用说了吧。
  • mShouldCache:标记该Request是否使用Cache,这个变量我们前边用过不止一次,它默认初始化为true,也就是说默认都会使用Cache。
  • mResponseDelivered:这个Request的响应消息是否已经传递出去。默认肯定是没有啦。那么到啥时候算是传递出去了呢?待会儿就知道了。
  • mCacheEntry:当一个请求可以从cache中检索到但是又必须从网络中刷新时, 缓存的entry将会被存储到这里,以防接收到 “Not Modified” 响应的时候, 来保证它还没有从cache中删除掉。
  • mTag:还记得之前的批量Cancel方法嘛?这个变量就是给它用的。
  • sCounter:用于参与计算我们的mIdentifier,也就这点用处。

接下来是构造方法,需要传method,url,和mErrorListener,除了这些,还在构造中初始化了mIdentifier,mRetryPolicy,以及mDefaultTrafficStatsTag,其中,mRetryPolicy使用了DefaultRetryPolicy,这个我们一会儿再分析,mDefaultTrafficStatsTag是一个无关痛痒的变量,所以这个findDefaultTrafficStatsTag方法我们就直接跳过。
下面就来看看成员方法:

  • getMethod|getErrorListener|getTrafficStatsTag|setRetryPolicy|get/setTag:这些方法对我们分析来说没有一点营养,跳过!
  • addMarker方法,用于在我们的Request的mEventLog中添加一条Log记录。
  • finish方法我们得详细了解一下,看代码
/**
 - 通知请求队列,这个请求已经完成(成功或错误).
 -  - <p>同时Dump该Request的所有事件Log;</p>
 */
public void finish(final String tag) {
    if (mRequestQueue != null) {
        mRequestQueue.finish(this);
        onFinish();
    }
    // Dumping Log
    if (MarkerLog.ENABLED) {
        final long threadId = Thread.currentThread().getId();
        if (Looper.myLooper() != Looper.getMainLooper()) {
            // 如果没有在主线程完成标记, 我们需要在主线程Dump以保证正确的序列.
            Handler mainThread = new Handler(Looper.getMainLooper());
            mainThread.post(new Runnable() {
                @Override
                public void run() {
                    mEventLog.add(tag, threadId);
                    mEventLog.finish(this.toString());
                }
            });
            return;
        }
        mEventLog.add(tag, threadId);
        mEventLog.finish(this.toString());
    }
}
  • setSequence|getSequence|setRequestQueue|getUrl|getOriginUrl|getIdentifier|setRedirectUrl|getCacheKey|set/getCacheEntry|cancel|isCanceled同样没有营养,只需要留意一下getCacheKey看看他是如何组织自己的Cache的Key的。
  • getHeaders方法可以获取该Request的头信息,这里返回一个空的集合,子类可以根据自己的需求,返回各种信息
  • 接下来几个被废弃的方法我们就跳过,看看他们的替代方法是什么
  • getParams这个用脚想都知道,返回Request的参数
  • getParamsEncoding返回我们的DEFAULT_PARAMS_ENCODING常量,也就是UTF-8
  • getBodyContentType返回本Request的ContentType,这是一个头数据,这里返回的是这么个玩意儿”application/x-www-form-urlencoded; charset=UTF-8”,基本符合常用的HTTP通信
  • getBody返回通信的内容,也就是你要发送给服务器的具体信息。
  • encodeParameters用于把我们要发送的数据组织成这样的格式”key1=value1&key2=value2”;
  • setShouldCache|shouldCache|getPriority|getTimeoutMs|getRetryPolicy统统跳过。
  • markDelivered一眼看去有些熟悉,我们在ResponseDelivery表示这个Response所请求的数据应返回,hasHadResponseDelivered是和它相对的
  • parseNetworkResponse:抽象方法,等待子类去实现,将网络请求回来的数据转换为Response对象
  • parseNetworkError方法和上一个方法配合使用,在转换发生异常的时候被调用
  • deliverResponse方法用于传递请求数据,上次我们就分析到这里的,只不过这个方法是个抽象方法,具体实现还要看各个子类。
  • deliverError传递异常,配合上个方法来使用
  • compareTo实现自Compare接口,用于比较两个Request并且排序,看完这个方法就知道了每个Request在RequestQueue里面是怎么排队的了。
  • 下面的两个方法,略过!

在这个Request接口中,那些无关紧要的方法我们直接省略,比较重要的方法也已经理解了,最重要的方法都在子类中实现着呢,看来的去找个子类看看是怎么一个实现方法。那就看看我们最常用的StringRequest吧。

StringRequest

我们主要关心的是他的实现方法:

  • 构造函数中,多了一个Listener,这个没错了,就是我们在外部传递进来的Listener,这下我们明白了在时候的时候传递进来的参数都是干嘛用了。
  • 或许有同学有疑问说为啥父类里只有ErrorListener而没有Listener,这个应该很好理解,就是处理方式不同,一般情况下我们的请求发生一场,我们都需要自己去根据业务处理各种情况,所以ErrorListener是必须的,在子类和父类都要有,但是一旦请求成功之后,我们对请求的数据处理是多样性的,有的时候我们需要拿到这个数据去做别的事情,而有的时候我们就不需要这个Listener监听,而是直接让结果参与进行下一步的动作,例如如果这个请求是显示图片到ImageView上,我们就可以直接把Listener替换为ImageView对象,直接让图片显示岂不妙哉?
  • StringRequest还实现了onFinish方法,在这个方法中释放了两个Listener
  • parseNetworkResponse是必须要实现了,我们看到它在这里取出了NetworkResponse的数据,解析成了String。
  • deliverResponse最后就是把String直接传递给了Listener,这样我们外部就接收到了数据。

StringRequest算是一个比较简单的Request实现,通过这么一个分析,我们明白了每一个Request的子类最重要的两个事情,parseNetworkResponse就是把数据解析成我们需要的格式,然后通过deliverResponse来把数据传递到需要的地方,就是这么简单,以后如果我们自己需要实现自己的Request,完全可以参照这个方法来进行,是不是So Easy?

RetryPolicy

接下来我们来完成最后的一个小角色的分析,就是RetryPolicy,它定义并控制者每一个Request在请求数据发生异常的时候的重试策略,这个接口是如此的简单:

public interface RetryPolicy {

    /**
     * 返回超时时间.
     */
    int getCurrentTimeout();

    /**
     * 返回重试次数.
     */
    int getCurrentRetryCount();

    /**
     * 准备进行下一次重试.
     * @param error 上次请求的返回码.
     * @throws VolleyError 重试不能被执行的时候抛出.
     */
    void retry(VolleyError error) throws VolleyError;
}
  • getCurrentTimeout方法返回当前Request已经在重试上耗费的时间,其实没什么用。
  • getCurrentRetryCount返回当前的重试次数,只要不大于总的重试次数就会一直调用retry方法
  • retry则需要每个重试策略子类去实现,根据不同的业务需求进行重试,有的业务也可能不需要重试,这是很正常的。

那就来看看子类吧,我们还记得之前创建Request的时候使用的重试策略吗?它是DefaultRetryPolicy。

DefaultRetryPolicy

这个子类不长,我们一点一点分析,先看成员变量:

/** 当前的超时毫秒数. */
private int mCurrentTimeoutMs;

/** 当前的重试次数. */
private int mCurrentRetryCount;

/** 最大重试次数. */
private final int mMaxNumRetries;

/** 策略乘法器. */
private final float mBackoffMultiplier;

/** 默认的Socket超时毫秒数 */
public static final int DEFAULT_TIMEOUT_MS = 2500;

/** 默认的重试次数,不重试 */
public static final int DEFAULT_MAX_RETRIES = 0;

/** 默认的乘法器 */
public static final float DEFAULT_BACKOFF_MULT = 1f;

前三个参数不用说,第四个参数是一个乘数,代表每重试失败一次,就会在当前的mCurrentTimeoutMs乘以几,不过看起来似乎没什么用,最多用在Log上记录一下重试时间。

DEFAULT_TIMEOUT_MS用于初始化我们Request的超时时间,没什么好说的。
DEFAULT_MAX_RETRIES默认重试次数0,代表不重试,请求失败就失败,不管了。
DEFAULT_BACKOFF_MULT请求失败默认乘数,这个确实没啥用。

看看最主要的retry方法是什么样的:

/**
 * 准备下一次重试.
 * @param error 上次重试的返回码.
 */
@Override
public void retry(VolleyError error) throws VolleyError {
    mCurrentRetryCount++;
    mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
    if (!hasAttemptRemaining()) {
        throw error;
    }
}

看来仅仅是增加了超时时间,顺便判断了一次剩余重试次数,并没有做什么重试的具体动作,这可能是DefaultRetryPolicy能做的最好的选择,因为Request的重试还是需要根据具体的请求实际对待,如果是我们自定义的重试策略,那我们就可以在这里实现我们自己的重试逻辑了。

小结

现在,我们也把重试的模块分析完了,至此我们已经把Volley中重要的部分分析完毕,相信大家对Volley的整个执行都有了一定的理解,Volley中剩下的部分还有很多,比如Log模块,Error模块等等,但不是我们这次的重点。希望大家通过这几篇文章,懂得Volley内部的执行流程,进一步去修改优化,为自家的Android应用在性能上添砖加瓦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值