Request是所有网络请求的基类,它实现了Comparable接口,前面提到RequestQueue可按照优先级队进行排序,这里的Comparable就是为优先级排序作准备。
接下来,我们对Request中比较重要或有趣的成员或方法进行一一解释。
Request中包括一个对支持的Http方法的定义。这里使用的内部接口而不是枚举来实现的。
public interface Method {
int DEPRECATED_GET_OR_POST = -1;
int GET = 0;
int POST = 1;
int PUT = 2;
int DELETE = 3;
int HEAD = 4;
int OPTIONS = 5;
int TRACE = 6;
int PATCH = 7;
}
Volley的日志记录是相关完善的,在定义日志系的时候也有一个小技巧值得我们学习,即在创建对象的时候就判断需要不需要日志,如果不需要日志,则不创建。
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
mIdentifier是唯一标识符,它的生成是在构造函数中调用createIdentifier方法。其算法是由Http方法,URL,当前系统时间以及内部计数器串起来的字符串,然后计算的一个Sha1。
不过我觉得这里的算法不是线程安全的,有可能会产生相同的ID。
private String mIdentifier;
private static String createIdentifier(final int method, final String url) {
return InternalUtils.sha1Hash("Request:" + method + ":" + url +
":" + System.currentTimeMillis() + ":" + (sCounter++));
}
一般网络都需要重试,这里它定义了 一个重试策略。
private RetryPolicy mRetryPolicy;
在volley的设计中这个重试策略的用法是如果抛出VolleyError则不再重试,否则,就会重试。这个重试的机制是由BasicNetwork中的while(true)这个死循环来处理。
它有一个默认实现类DefaultRetryPolicy,实现得比较简单,只是做了一个次数的重试,且只重试一次。但我们看到,DefaultRetryPolicy中还是埋下了一些伏笔。比如,后退因子mBackoffMultiplier,以及超时等待。也就是说,我们在丰富这个重试策略的时候,也可以做成等待多久后,再试一次。
比如下面的代码实现的就是在重试前先等待一段时间,再试。这一段时间就是由mCurrentTimeoutMs来决定。
/**
* Created by Rex on 4/26/2015.
*/
public class WaitRetryPolicy extends DefaultRetryPolicy {
@Override
public void retry(VolleyError error) throws VolleyError {
try {
Thread.sleep(getCurrentTimeout());
} catch (InterruptedException e) {
e.printStackTrace();
}
super.retry(error);
}
}
此外,我们注意到有以几个成员:
// A cheap variant of request tracing used to dump slow requests.
private long mRequestBirthTime = 0;
/** Threshold at which we should log the request (even when debug logging is not enabled). */
private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;
在finish方法中,有这么一段,
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("%d ms: %s", requestTime, this.toString());
}
做什么用呢?当volley发现一个请求的开销超出某个阈值(SLOW_REQUEST_THRESHOLD_MS)时,则不管是不是log开关有没有打开,都会打印一个调试日志,用于尽快的跟踪问题。这个习惯相当的好。
另外,我们注意到,它计时所用的方法是 SystemClock.elapsedRealtime(),而不是System.currentTimeMillis()。 这两个方法有啥区别呢?区别在于currentTimeMillis用户可以更改,即当修改用户时间这个值会变,而前者则是开机后的绝对流逝时间,是无法手动去修改这个值的。因此,使用SystemClock.elapsedRealtime()会非常确准的计算时间差。
volley还有一个特点是,我们注意下这些Set方法。
大多数时候,他们都有一个返回值,return this. 这种写法能够很方便的把各个赋值语句串起来(像builder模式),让调用者非常舒服的使用代码。
总之,这个Request类在写法上考虑了很多的细节,同时又有一些技巧,这些都值得我们学习。