Volley深度完全解析
来源:互联网 发布:想做淘宝刷单hao360 编辑:程序博客网 时间:2024/06/15 12:01
注:转载或修改转载请在文章开头标明本文地址,笔者重在分享禁止用于其他用途
微信公众号:
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
我意在分享模块化不分享碎片内容,如果有错误的地方大家可以指出,可以在评论多提建议
- Volley深度完全解析
- Android Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- volley完全解析
- Android Volley完全解析
- Volley完全解析
- Android Volley 完全解析
- Android Volley完全解析
- Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- Android Volley完全解析
- 小白写基金——点滴积累
- JS之函数理解
- Swust OJ 001 Satellite Photographs
- hihocoder #1224 : 赛车 dfs
- 做项目开发后目前有的域名
- Volley深度完全解析
- Android中如何使用WIFI来连接ADB
- node.js http
- Redis 该选择哪种持久化配置
- Linux kernel 4+ version with QEMU
- Android系统源码与内核下载编译及刷机讲解
- Android Studio --“Cannot resolve symbol” 解决办法
- Linux设备模型(3)_Uevent
- 系统各种跳转