Volley的基本用法
// 创建请求队列
RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
String url = "http://xxx";
// 获取字符串使用StringResult,获取JsonObject使用JsonObjectRequest,获取Bitmap使用ImageRequest
StringRequest stringRequest = new StringRequest(Request.Method.GET,url,new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// 回调在主线程
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// 回调在主线程
}
});
// 设置tag当调用queue.cancelAll(tag)会取消所有拥有这个tag的request,一般用于在onStop()中关闭网络连接
stringRequest.setTag(this);
queue.add(stringRequest);
由上述代码可知,使用Volley的主要步骤包括创建RequestQueue、创建Request、将Request加入到RequestQueue三步,下面一一讲述
1.1 常规用法
常规做法是调用Volley.newRequestQueue这个API创建,代码如下
// MainActivity.java
RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
// Volley.java
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (BaseHttpStack) null);
}
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
// 小于2.3使用HttpClient忽略
}
} else {
network = new BasicNetwork(stack);
}
return newRequestQueue(context, network);
}
private static RequestQueue newRequestQueue(Context context, Network network) {
// network = BasicNetwork(new HurlStack())
// DEFAULT_CACHE_DIR = "volley"
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
public void start() {
stop();
// PriorityBlockingQueue> mCacheQueue = new PriorityBlockingQueue<>();
// PriorityBlockingQueue> mNetworkQueue = new PriorityBlockingQueue<>();
// 这里的mCacheQueue、mNetWorkQueue都是优先级阻塞队列mCache是传入的DiskBaseCache
// mDeliver则是在构造器里面创建的ExecutorDelivery其拥有一个Looper是主线程的Handler
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
1.2 PriorityBlockingQueue 优先级阻塞队列
可以把RequestQueue当做缓存队列和网络请求队列的生产者,NetworkDispatcher、CacheDispatcher当做消费者,当然有时候CacheDispatcher也会被当做网络请求队列的生产者(缓存过期、没命中等情况),Request内有一个枚举类定义了4种优先级,默认情况是NORMAL,我们可以重写Request.getPriority从而改变优先级,PriorityBlockingQueue会调用Request.compareTo来判断顺序(优先级越高越先出去,优先级相等则按照FIFO)
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal();
}
1.3 mCacheDispatcher.start
因为CacheDispatcher派生自Thread所以直接看其run方法
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 调用实现类,初始化缓存比如从磁盘读取缓存文件
mCache.initialize();
while (true) {
try {
processRequest();
} catch (InterruptedException e) {
// 如果是主动调用quit出循环的,再调用interrupt退出循环
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
}
}
}
1.3.1 下面首先看看mCache.initialize(),在这里mCache对应于DiskBasedCache
public synchronized void initialize() {
// 目录不存在就创建目录,由于目录都没缓存文件肯定也没直接返回
if (!mRootDirectory.exists()) {
if (!mRootDirectory.mkdirs()) {
VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
}
return;
}
// 获得目录下所有的文件
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
try {
long entrySize = file.length();
// 这个装饰类,只是在原有的BufferedInputStream上加上获取剩余字节的方法
CountingInputStream cis =
new CountingInputStream(
new BufferedInputStream(createInputStream(file)), entrySize);
try {
CacheHeader entry = CacheHeader.readHeader(cis);
// 通过初始化的entry其size等于缓存文件的大小,当网络请求回来其size等于响应体的大小
entry.size = entrySize;
// 保存到mEntries注意这里LinedHashMap是有序的按照LRU的顺序排序
putEntry(entry.key, entry);
} finally {
cis.close();
}
} catch (IOException e) {
// readHeader抛出的IOException在这里被处理掉了,删除了文件
file.delete();
}
}
}
1.3.1.1 CountingInputStream
CountingInputStream只是在原有的BufferedInputStream上多维护了一个已经读取多少字节的变量,代码如下
static class CountingInputStream extends FilterInputStream {
private final long length;
private long bytesRead;
CountingInputStream(InputStream in, long length) {
super(in);
this.length = length;
}
@Override
public int read() throws IOException {
int result = super.read();
if (result != -1) {
bytesRead++;
}
return result;
}
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
int result = super.read(buffer, offset, count);
if (result != -1) {
bytesRead += result;
}
return result;
}
@VisibleForTesting
long bytesRead() {
return bytesRead;
}
// 还有多少字节没有读
long bytesRemaining() {
return length - bytesRead;
}
}
1.3.1.2 CacheHeader.readHeader
static CacheHeader readHeader(CountingInputStream is) throws IOException {
// 读取4个字节组成一个int,如果该值不等于所谓的魔法数字就抛出异常 int CACHE_MAGIC = 0x20150306
int magic = readInt(is);
if (magic != CACHE_MAGIC) {
// 这里不用去删除它,最终会被外界捕获到异常将其删除
throw new IOException();
}
// 读取这六个属性
String key = readString(is);
String etag = readString(is);
long serverDate = readLong(is);
long lastModified = readLong(is);
long ttl = readLong(is);
long softTtl = readLong(is);
List<Header> allResponseHeaders = readHeaderList(is);
// 通过这7个参数构建了CacheHeader对象
return new CacheHeader(
key, etag, serverDate, lastModified, ttl, softTtl, allResponseHeaders);
}
1.3.1.2.1 readInt
// 读取4个字节组成一个int
static int readInt(InputStream is) throws IOException {
int n = 0;
n |= (read(is) << 0);
n |= (read(is) << 8);
n |= (read(is) << 16);
n |= (read(is) << 24);
return n;
}
1.3.1.2.2 readLong
// 读取8个字节组成一个long
static long readLong(InputStream is) throws IOException {
long n = 0;
n |= ((read(is) & 0xFFL) << 0);
n |= ((read(is) & 0xFFL) << 8);
n |= ((read(is) & 0xFFL) << 16);
n |= ((read(is) & 0xFFL) << 24);
n |= ((read(is) & 0xFFL) << 32);
n |= ((read(is) & 0xFFL) << 40);
n |= ((read(is) & 0xFFL) << 48);
n |= ((read(is) & 0xFFL) << 56);
return n;
}
1.3.1.2.3 readString
// 首先读取8个字节以用来确定后面那个字符串的长度,然后再读取该长度将其转化为字符串
static String readString(CountingInputStream cis) throws IOException {
long n = readLong(cis);
byte[] b = streamToBytes(cis, n);
return new String(b, "UTF-8");
}
1.3.1.2.3.1 streamToBytes
// 从流中读取指定长度的字节,然后返回
static byte[] streamToBytes(CountingInputStream cis, long length) throws IOException {
long maxLength = cis.bytesRemaining();
// 长度不能是负数或者大于余下的字节数,并且不能大于int的最大值,这里是根据强转判断的
if (length < 0 || length > maxLength || (int) length != length) {
throw new IOException("streamToBytes length=" + length + ", maxLength=" + maxLength);
}
byte[] bytes = new byte[(int) length];
// 读取length长度的字节
new DataInputStream(cis).readFully(bytes);
return bytes;
}
1.3.1.2.4 readHeaderList
static List<Header> readHeaderList(CountingInputStream cis) throws IOException {
// 先读取下有几个响应头
int size = readInt(cis);
if (size < 0) {
throw new IOException("readHeaderList size=" + size);
}
List<Header> result =
(size == 0) ? Collections.<Header>emptyList() : new ArrayList<Header>();
// 然后一个个读取出来放到result中
for (int i = 0; i < size; i++) {
String name = readString(cis).intern();
String value = readString(cis).intern();
result.add(new Header(name, value));
}
return result;
}
1.3.1.3 putEntry DiskBaseCache内部维护这一个HashMap并且是有序的,从里面读取元素是按照LRU的规格读取的,mTotalSize则维护着当然所有缓存文件的总大小,默认是5m,超出后会从mEntries取出删除
private void putEntry(String key, CacheHeader entry) {
if (!mEntries.containsKey(key)) {
mTotalSize += entry.size;
} else {
CacheHeader oldEntry = mEntries.get(key);
mTotalSize += (entry.size - oldEntry.size);
}
mEntries.put(key, entry);
}
1.3.2 processRequest 从缓存阻塞队列中取Request,取不到则阻塞
private void processRequest() throws InterruptedException {
// 当Request被添加到缓存队列前会被阻塞
final Request<?> request = mCacheQueue.take();
processRequest(request);
}
1.4 networkDispatcher.start
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
try {
processRequest();
} catch (InterruptedException e) {
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
}
}
}
1.4.1 processRequest 从网络请求阻塞队列中取Request,取不到则阻塞
private void processRequest() throws InterruptedException {
// 同样当没有request添加到网络请求队列中也会阻塞
Request<?> request = mQueue.take();
processRequest(request);
}
getCacheKey 该方法会根据Url和Method返回一个字符串,该字符串决定了缓存文件的名称(分成两半取hash值然后相加)
public String getCacheKey() {
String url = getUrl();
int method = getMethod();
if (method == Method.GET || method == Method.DEPRECATED_GET_OR_POST) {
return url;
}
return Integer.toString(method) + '-' + url;
}
getHeaders,Volley会在做网络请求前调用该方法设置请求参数,所以如果我们要自定义请求头那么可以重写该方法,默认是一个空Map
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
getParams, 如果该请求方法是Post,Volley会在网络请求前调用该方法将其作为输出源写给HttpUrlConnection.getOutputStream,默认返回null
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
finish 通知RequestQueue该请求结束了,内部调用RequestQueue.finish,如果日志可以打印的话会打印该请求的所有日志
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
// 打印日志续文会讲
}
// RequestQueue.finish
<T> void finish(Request<T> request) {
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
// 回调监听
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
}
parseNetworkResponse 抽象方法需要各子类实现,解析网络响应(可能来自缓存或者网络请求)包装成合适的Response对象返回,比如StringRequest就包装成Response
cancel 取消请求,如果是在主线程调用的该方法那么就可以保证不会回调Request.deliverResponse和Request.deliverError
hasHadResponseDelivered 用于判断当前请求是否已经分发了,主要用于SoftTtl过期了,Ttl没过期,该情况会先返回缓存的响应的响应然后再去请求如果响应码是304就调用该方法判断请求是否已经分发了,如果已经分发了那么就不进行第二次分发了
getBodyContentType,决定Post和Put请求body的类型
shouldCache,决定该请求是否需要缓存,如果不需要加入到网络请求阻塞队列中(网络请求结束后也不会缓存),否则加入缓存阻塞队列中,我们可以调用setShouldCache来控制是否需要缓存
getTimeoutMs、getRetryPolicy都跟重试策略,本文的续文会讲到
public <T> Request<T> add(Request<T> request) {
// 标志着当前request属于哪个RequestQueue
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// 设置序列号从零开始0、1、2
request.setSequence(getSequenceNumber());
// 设置日志,最后讲
request.addMarker("add-to-queue");
// 如果这个请求不需要缓存,那么跳过加入缓存队列,直接添加到网络请求队列
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
mCacheQueue.add(request);
return request;
}
4.1 processRequest(Request)
void processRequest(final Request<?> request) throws InterruptedException {
request.addMarker("cache-queue-take");
// 如果在被添加到缓存队列前就已经取消了直接结束该请求
if (request.isCanceled()) {
// 内部会将自己从RequestQueue中移除,回调RequestFinishedListener.onRequestFinished()
// 并且打印日志(如果开启了等级为VERBOSE)
request.finish("cache-discard-canceled");
return;
}
// 尝试从缓存中恢复
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 缓存没命中,如果已经有了相同缓存key的请求并且响应还没下来,那么不需要加入到网络请求队列
// 否则加入到缓存队列中去,这会使4个NetworkDispatcher中的一个脱离阻塞
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
// 如果缓存的内容已经完全过期了那么只能再请求网络,内部判断ttl与System.currentTimeMillis
// 满足ttl >= softTtl
if (entry.isExpired()) {
// 缓存命中了但是过期了
request.addMarker("cache-hit-expired");
// 设置一下过期的缓存内容,内部的etag,last_modify将会在下次请求时带给服务端
request.setCacheEntry(entry);
// 与上面一样
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
// 下面就是缓存命中了,并且没有完全过期
request.addMarker("cache-hit");
// 组装NetWorkResponse对象,给Request解析
Response<?> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 判断softTtl < System.currentTimeMillis()
if (!entry.refreshNeeded()) {
// 如果softTtl也小于当前时间,那么缓存有效,直接分发响应
mDelivery.postResponse(request, response);
} else {
// softTtl已经大于当前时间了,也可以直接分发这个响应,但是分发后还要再去请求一下网络
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// 标记当前的响应是中间态,可能该请求会触发两次响应
response.intermediate = true;
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
// 分发响应然后再把请求放入网络请求队列中
mDelivery.postResponse(
request,
response,
new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
});
} else {
// 已经有同样的请求了那么就直接分发响应
mDelivery.postResponse(request, response);
}
}
}
4.1.1 mCache.get 其中mCache对应DiskBaseCache
public synchronized Entry get(String key) {
CacheHeader entry = mEntries.get(key);
// 如果缓存内容不存在返回null
if (entry == null) {
return null;
}
// 内部根据key获取了文件名然后创建了一个File对象,创建文件名的规格是把key平均分成两部分,分别计算
// hash然后拼接在一起,感觉是为了提高不重复的概率
File file = getFileForKey(key);
try {
CountingInputStream cis =
new CountingInputStream(
new BufferedInputStream(createInputStream(file)), file.length());
try {
// 再读一遍防止文件名与缓存内容对不上
CacheHeader entryOnDisk = CacheHeader.readHeader(cis);
// 不相等的情况有两个缓存文件互相名字交换等情况
if (!TextUtils.equals(key, entryOnDisk.key)) {
// 从Map中移除,返回null
removeEntry(key);
return null;
}
// 读取响应体
byte[] data = streamToBytes(cis, cis.bytesRemaining());
// 将响应头与响应体进行组合返回一个Entry
return entry.toCacheEntry(data);
} finally {
cis.close();
}
} catch (IOException e) {
// 读取失败也从Map中移除,并且将缓存文件删除,返回null
remove(key);
return null;
}
}
4.1.1.1 getFileForKey 根据缓存key创建File对象 getFilenameForKey根据缓存key获取缓存文件名
public File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
4.1.1.2 entry.toCacheEntry 将缓存的响应头与缓存的响应体进行组合
Entry toCacheEntry(byte[] data) {
Entry e = new Entry();
e.data = data;
e.etag = etag;
e.serverDate = serverDate;
e.lastModified = lastModified;
e.ttl = ttl;
e.softTtl = softTtl;
e.responseHeaders = HttpHeaderParser.toHeaderMap(allResponseHeaders);
e.allResponseHeaders = Collections.unmodifiableList(allResponseHeaders);
return e;
}
4.1.1.2.1 HttpHeaderParser.toHeaderMap
static Map<String, String> toHeaderMap(List<Header> allHeaders) {
// 创建一个大小写不敏感的TreeMap也就是忽略大小写,比如Expired与expired会被认为相同
Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
// 后面的响应头具有高优先级
for (Header header : allHeaders) {
headers.put(header.getName(), header.getValue());
}
return headers;
}
4.1.2 request.parseNetworkResponse 来看看StringRequest是怎么执行的
// 从请求头中读出charset然后把响应体转化为一个字符串
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
4.1.3 HttpHeaderParser.parseCacheHeaders 该方法会返回一个Cache.Entry,如果调用parseNetworkResponse的是NetworkDispatcher的话会把这个Cache.Entry写入缓存文件,如果是CacheDispatcher调用的话那么基本没什么用
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0;
long lastModified = 0;
long serverExpires = 0;
long softExpire = 0;
long finalExpire = 0;
long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag = null;
String headerValue;
headerValue = headers.get("Date");
if (headerValue != null) {
// 将服务器返回的处理请求的时间转化为long
serverDate = parseDateAsEpoch(headerValue);
}
headerValue = headers.get("Cache-Control");
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",", 0);
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i].trim();
// 不使用缓存直接返回null
if (token.equals("no-cache") || token.equals("no-store")) {
return null;
} else if (token.startsWith("max-age=")) {
try {
// 缓存的内容将在maxAge秒后失效
maxAge = Long.parseLong(token.substring(8));
} catch (Exception e) {
}
// 在staleWhileRevalidate内可以直接把缓存内容返回
} else if (token.startsWith("stale-while-revalidate=")) {
try {
staleWhileRevalidate = Long.parseLong(token.substring(23));
} catch (Exception e) {
}
// 本地副本过期前,可以使用本地副本;本地副本一旦过期,必须服务器进行有效性校验。
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
mustRevalidate = true;
}
}
}
headerValue = headers.get("Expires");
if (headerValue != null) {
// 获取服务器设置的缓存过期时间
serverExpires = parseDateAsEpoch(headerValue);
}
headerValue = headers.get("Last-Modified");
if (headerValue != null) {
// 记录一下服务端该资源的最后修改时间
lastModified = parseDateAsEpoch(headerValue);
}
// 获取ETag
serverEtag = headers.get("ETag");
// 缓存控制头优先于Expires,因为前者限制性更强
if (hasCacheControl) {
// 软过期,不怎么理解怎么把缓存当做当前响应返回时间了
softExpire = now + maxAge * 1000;
// 该响应的最终过期时间
finalExpire = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
// 如果没有缓存控制头那么根据expires决定
softExpire = now + (serverExpires - serverDate);
finalExpire = softExpire;
}
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = finalExpire;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
entry.allResponseHeaders = response.allHeaders;
return entry;
}
4.1.4 mWaitingRequestManager.maybeAddToWaitingRequests mWaitingRequestManager为WaitingRequestManager的实例
private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {
String cacheKey = request.getCacheKey();
// 如果已经有相同缓存key的请求了并且该请求还没收到响应那么加入到List后面,防止相同缓存key的请求一个还没得到响应下一个就又发出请求了
if (mWaitingRequests.containsKey(cacheKey)) {
List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new ArrayList<>();
}
request.addMarker("waiting-for-response");
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
return true;
} else {
// 插入null,标志着当前的request正在等待响应
mWaitingRequests.put(cacheKey, null);
// 设置监听以便在网络请求完成后把Request从mWaitingRequest中移除
request.setNetworkRequestCompleteListener(this);
if (VolleyLog.DEBUG) {
VolleyLog.d("new request, sending to network %s", cacheKey);
}
return false;
}
}
4.1.3 mDelivery.postResponse mDelivery是一个ExecutorDelivery实例,如果是SoftTtl过期Ttl没过期则可能会分发两次响应,一次属于缓存,一个属于网络请求,但是如果网络请求是304则只会分发属于缓存的那次响应,续文会讲到
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
// 也就是在主线程调用ResponseDeliveryRunnable.run
public void run() {
// 取消了就不分发了直接结束
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 成功分发成功,失败分发失败
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// softTtl过期Ttl不过期那么还需要执行mRunnable其实就是把Request加入到网络请求队列中去
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
if (mRunnable != null) {
mRunnable.run();
}
}
4.1.3.1 mRequest.deliverResponse 该方法是一个抽象方法 用于分发响应 看看StringRequest怎么实现的,内部就是直接回调Listener.onResponse
protected void deliverResponse(String response) {
Response.Listener<String> listener;
synchronized (mLock) {
listener = mListener;
}
if (listener != null) {
listener.onResponse(response);
}
}
4.1.3.2 mRequest.deliverError StringRequest没有重写该方法,内部就是调用ErrorListener.onErrorResponse()
public void deliverError(VolleyError error) {
Response.ErrorListener listener;
synchronized (mLock) {
listener = mErrorListener;
}
if (listener != null) {
listener.onErrorResponse(error);
}
}