注:转载或修改转载请在文章开头标明本文地址,笔者重在分享禁止用于其他用途
微信公众号:
volley介绍
volley是Google在2013年IO大会上提出的一个开源项目,Google针对请求开发不合理的情况,对网络请求,图片异步加载,图片三级缓存防止OOM,做了一套规范操作。
Volley具有以下优点:
- 网络请求自动调度
- 多并发请求网络
- 通过高速缓存一致性透明磁盘和内存一致性
- 支持请求优先级
- 支持取消请求
- 容易扩展定制,如重试和回退
- 具有调试和跟踪工具
Volley不适合大文件下载,因为Volley会在解析的过程中在内存中保存所有的响应,如果有这些需求,官方推荐可以使用DownLoadManager
可在gradle中增加依赖
dependencies {
...
compile 'com.android.volley:volley:1.0.0'
}
也可以自行下载Volley设置为library
volley官方地址 https://developer.android.com/training/volley/index.html
配置好编译环境的github https://github.com/THtianhao/android-volley.git
google 2013 IO大会volley介绍
https://www.youtube.com/watch?v=yhv8l9F44qo&feature=player_embedded
Volley原理
Volley的原理
想利用volley发一条请求的流程
创建一条请求,然后将请求加入到RequestQueue中,RequestQueue会将请求分派到缓存线程中,缓存线程会从缓存中去取这条request,如果在缓存中找到就交付Response,如果没有则放入到网络线程中去请求,请求返回后放入缓存并交付Response。
Volley的使用
最基本的使用官方已经做了示例,笔者不再过多描述,笔者强烈建议没有用过Volley的人先看一下Volley的示例
参照:
https://developer.android.com/training/volley/simple.html
https://developer.android.com/training/volley/requestqueue.html
https://developer.android.com/training/volley/request.html
https://developer.android.com/training/volley/request-custom.html
Volley源码解析
笔者花了一些时间做了一副Volley的脑图,从图中大家体会一下Volley的整体流程
Request
根据Volley流程首先我们需要一条Request
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}
以上是构造方法,我们会传递过来一个ErrorListener来传递响应失败的回调
setRetryPolicy主要用于设置一些重试的策略,如失败重试几次
mDefaultTrafficStatsTag根据URL生成用于Request类的重写toString方法来表示一个唯一的request
构造方法传递进来的method表示网络请求类型在Request类中定义
/**
* Supported request methods.
*/
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;
}
Request类中的另一个重要的方法
/**
* Notifies the request queue that this request has finished (successfully or with error).
*
* <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);
}
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
// If we finish marking off of the main thread, we need to
// actually do it on the main thread to ensure correct ordering.
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());
}
}
这个方法主要是通知请求结束,交付response
主要操作是mRequestQueue.finish(this);其他的操作主要是为了打一些log
作用是告诉RequestQueue当前的Request请求结束了
再看一下Reqeus的定义
public abstract class Request<T> implements Comparable<Request<T>>
Request是一个抽象类,我们要用Request类必须继承它,并将表明Request的范型(范型的主要作用是提高代码复用率,相比于Object的好处是减少了强制类型转化,防止编译器报错)
要Request必须实现Request的两个抽象方法
abstract protected void deliverResponse(T response);
以上会在ResponseDeliveryRunnable中提及
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
以上会在Dispacther中会提及,主要用作解析返回的网络请求
RequestQueue
首先看一下Volley类里面默认创建的RequestQueue,里面的东西会在RequestQueue中讲到
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
RequestQueue是Volley的核心,基本上所有与Volley主要的类都会在它当中出现
首先来看构造方法
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
Cache用于存储所有的Request,当然细心的人会发现,每一条request可以设置shouldCache()方法,来表明这一条Request是否需要在Cache里面进行存储。
NetWork 用与网络请求,也是需要在开始的时候创建并传入RequestQueue
threadPoolSize是Volley的网络请求线程数量,Volley默认四条:
/** Number of network request dispatcher threads to start. */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
ResponseDelivery 是网络请求的交付类,如不指定,在另一条构造方法会创建
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
以上是构造方法的简介
Request的start方法
public void start() {
stop();
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();
}
}
start方法是新建RequestQueue后必须执行的方法,里面创建了两种Dispather,一种是Cache(一个),一种是NetWork(默认四个),分别用于存储和网络请求。他们都继承于Thread所以在创建的时候就将这四个线程start
前面都是简单的介绍类,下面我们顺着一条请求来理解接下来的方法
新建一条Request,新建一个RequestQueue,然后将请求add到RequestQueue中
RequestQueue的Add方法
public <T> Request<T> add(Request<T> request) {
// 标记当前request的
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// 标记reqeustQueue的number,自增长(++)
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 如果request是不可存储的,则跳过Cache queue加入到Net WorkQueue
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// 如果请求早已在请求状态中,则将它们放入到waittingQueue中
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// 插入null到waitingRequest中,并放入CacheQueu中去请求
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
add方法比较长,我加入了中文注释
主要的作用是将请求放入到各种集合里面,如果不需要存储就放入mNetworkQueue.add(request);中
这里提到几个合集
主要是mWaitingRequests和mCacheQueue
waitingRequest主要用于处理已经在请求状态的请求,可以看到最后几行如果waiting中没有这条request,则放一个null的进去,那么下一次再add的时候waitingRequests中就有了这条request。
mCacheQueue的作用用于网络请求那么既然是网络请求为什么叫mCacheQueue呢?这就要说到上面所讲到start的CacheDispatcher线程了。
CacheDispatcher
再次说明这是一条线程:
public class CacheDispatcher extends Thread
那么有线程就有Run
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mCache.initialize();
while (true) {
try {
// 从cache中取一条请求
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果请求cacel()则直接交付
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 试图从请求中取出数据,如果没有则放入网络队列
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
//如果超时,则放入到网络队列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
//我们的缓存命中了,将请求的数据解析交付到Request
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// 不完全过期,直接交付
mDelivery.postResponse(request, response);
} else {
// 缓存软过期,我们直接交付
// 但是我们也需要放入网络中去请求
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
}
}
});
}
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
}
}
结合翻译后的注视可以看出来,其实就是从mCacheQueue中取这条请求,如果缓存中有这条请求,就去交付,如果没有或过期就放倒mNetworkQueue中去,那么mNetworkQueue里面的东西在哪处理?还记得开头新建的四个线程吗?
对,就是NetworkDispatcher
NetworkDispatcher
public BasicNetwork(HttpStack httpStack) {
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
构造方法里面传入了一个httsStack 这个类是具体操作网络请求的类
在Volley类里面,默认会创建一个HurlStack
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 从mQueue中拿一条Request出来(mQueue就是NetWorkQueue)
request = mQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 如果request设置了cancel那么则直接交付
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// 真正进行请求网络
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果返回304或者已经交付了的话就直接finish
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
//网络请求解析后的地方
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果不设置shouldCache,默认的shouldCache为true写入到cache中
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 将请求交付
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
首先先从需要进行网络请求的Request拿出来,然后进行请求,并存入cache中。
在这里可以看到Volley的网络请求是在mNetwork.performRequest(request);进行的。请求后会执行的request.parseNetworkResponse(networkResponse);这个方法在之前说过,是继承Reqeust必须重载的方法,在里面做一些自定义的解析。
mNetWrok是什么?就是创建RequestQueue的时候传递进来的Network,在Volley中,有两个Network供人们使用。当然你也可以自定义
前面说的Network是:
BasicNetwork
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// 网络请求并返回返回数据
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
//如果response是304则直接返回
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
//Http返回值为304不包含所有的头,我们需要从加上cache里面的头生成一个新的头去返回
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Some responses such as 204s do not have content. We must check.
//一些response没有上下文例如204,我们需要检查一下
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
responseContents = new byte[0];
}
// if the request is slow, log it.
//如果请求很慢,打个日志
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
//返回网络请求
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
//处理反返回码异常的重试,由于整体是循环,所以失败后会重试
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else if (statusCode >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException("server",
request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
以上主要是做了一些对http请求返回后是否需要重试的处理,如果需要重试,则会抛出异常来继续循环请求如果不需要重试则return new NetworkResponse
我们看到Response是在
httpResponse = mHttpStack.performRequest(request, headers);返回的
那么构造方法里面的传入HurlStack的performRequest这里面做了什么?
HurlStack
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
//通过url和request的一些超时数据放入connect
HttpURLConnection connection = openConnection(parsedUrl, request);
//将头放入connect
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
//设置connent的Method如果没有指定,并且有body则是post
//设置connent的Method如果没有指定,并且没有body则是get
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
//从HttpURLConnection初始化httpResponse和数据
//设置协议版本
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// 如果response不能被取回,则返回-1
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
//http的类型不是head并且除去特定的错误码,将connection的数据存放到response里面
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
//将http的头加入到response中
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
//返回response
return response;
}
看到这里大家就会发现,Volley默认使用的是HttpURLConnection来进行网络请求的。
这个方法里面主要做的操作就是根据传递过来的url和head来进行网络请求
其他的地方加注释
看到这里的时候大家基本对Volley的网络请求有了一定的了解,那么请求过后的response是如何交付的呢?可以回到cache和network的Dispatcher里面看看,里面都有网络的交付,我们从NetworkDispatcher中去看:
mDelivery.postResponse(request, response);
请求回来后会通过mDelivery来交付
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
NetworkDispatcher的构造方法里面会传入ResponseDelivery再往回看:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
我们找到了Request的构造方法,可以看到在里面创建了一个ExecutorDelivery并传入了主线程的looper
ExecutorDelivery
那么我们用于请求交付的地方就是这里了
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
构造方法里面传递进一个handler,从前面可以看到post到了主线程用于交付
下面是我们在dispatcher中看到的postResponse
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
这里对标记为已经delivered并执行了一个Runnable,里面的东西:
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
//如果request被标记为cancel状态,则直接finish
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 如果成功则交付到request的deliverResponse
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// 如果request是intermediate状态,打一个log如果不是则finish
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// 如果我们有一个runnable可能是postdelay,执行它
if (mRunnable != null) {
mRunnable.run();
}
}
}
那么总而言之,response通过层层传递,又回到了reqeust中并且交付后执行了request的finish方法,不知是否还记得request的finish方法调用了RequestQueue的finish方法,里面又做了什么呢:
RequestQueue中的finish方法
<T> void finish(Request<T> request) {
// 从mCurrentRequests中移除(在add的时候会添加,所以只要没有finish的request都会存在里面)
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
//通知mFinishedListeners(这个接口也是可以在创建request的时候对其进行设置的,通过流程可以知道,如果我们有业务逻辑需要在请求finish后执行的话,可以进行设置此监听)
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
mCacheQueue.addAll(waitingRequests);
}
}
}
}
前面的东西很好理解,后面的呢?
从RequestQueue的add方法中我们知道mWaitingRequests,相同url进行请求的request会放入mWaitingRequests中,这里的操作是将mWaitingRequests中所有相同url(key)的request取出,然后全部放入mCacheQueue中,为什么要这么做?因为这时候第一条request已经请求完成,所以在cache中已经存在,所以可以直接取出直接交付(忘记的同学可以翻一翻前面)。
以上就是进行Volley进行网络请求的所有流程了
----------------------------------
深究一下,mWaitingRequests中存放的健值对的key是url,实际存储到cache里面的也是url吗?分析一下chache,volley默认使用的是DiskBasedCache类
DiskBasedCache
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
public DiskBasedCache(File rootDirectory) {
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}
DiskBasedCache默认有一个5M的空间
我们主要关心cache的存取
public synchronized void put(String key, Entry entry) {
//首先判断加入后缓存是否过大,如果过大则随机删除一些数据,知道缓存够用
pruneIfNeeded(entry.data.length);
//
File file = getFileForKey(key);
try {
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
CacheHeader e = new CacheHeader(key, entry);
boolean success = e.writeHeader(fos);
if (!success) {
fos.close();
VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
throw new IOException();
}
fos.write(entry.data);
fos.close();
putEntry(key, e);
return;
} catch (IOException e) {
}
boolean deleted = file.delete();
if (!deleted) {
VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
}
}
在这个同步方法里面,首先传入的是是将url和response返回的数据,然后根据url生成一个文件,并将url和Entry传入到CacheHeader中,把CacheHeader写入到文件。那么文件名是不是就是url
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;
}
我反复的强调想必大家也猜到了,文件名不是url是根据一套规则(前半段和后半段)生成的一个hashcode。这就是存在我们Cache中的文件了
关于Volley的图片缓存在另一篇博客中写
至此Volley的解析也就完成了,从每个流程都对volley进行了分析,如果大家还有不明白的可以下载源码进行分析,有一些细节我没有讲,可以自己多看看,我将配置好的环境放到了git上面大家可以直接下载
https://github.com/THtianhao/android-volley.git
我意在分享模块化不分享碎片内容,如果有错误的地方大家可以指出,可以在评论多提建议