1. HttpClient与HttpUrlConnection的区别
简而言之:就是Volley的的请求方式(API2.3之前用的httpClient 2.3之后用的HttpUrlconnection)
httpclient 和httpUrlConnection都支持https协议, 都是以流的形式进行传输数据.支持IPv6以及连接池等功能.
httpclient拥有很多API 保证它的兼容性进行拓展很难,google在6.0的时候废弃Httpclient AS想用这个类可以使用:org.apach.http.legacy
HttpUrlConnection:轻量级,API较少,易拓展,满足大部分的android数据传输 .例如Volley框架
2. OKHttp源码分析
-
使用步骤: 首先创建OKhttpClient对象, 再创建一个 request请求 ,给request添加url,header请求方式等参数, 用OKhttpclient.newCall方法返回一个call对象.同步方法: call.execute()来请求数据,异步:call.euque();
-
dispatcher任务调度器,
- 它定义了三个双向任务队列,二个异步队列:runningAsyncCalls和readyASyncCalls和一个运行在同步请求的runningSyncCalls队列
- 一个标准线程池 executorServic 无界线程池,60s回收,用于大量耗时较短的异步任务
-
Request请求
Request的builder方法默认请求方式为GET
-
通过OkHttpclient和request构造一个call对象,然后把这个call封装到RealCall对象中
public Call newCall(Request request) { return RealCall.newRealCall(this, request, false /* for web socket */); } static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket){ // Safely publish the Call instance to the EventListener. RealCall call = new RealCall(client, originalRequest, forWebSocket); call.eventListener = client.eventListenerFactory().create(call); return call; } private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { this.client = client; this.originalRequest = originalRequest; this.forWebSocket = forWebSocket; this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); }
在realCall中构造了一个RetryAndFollowUpInterceptor拦截器用于处理请求错误和重定向等,这个是Okhttp框架精髓 interceptor chain中的一环, 默认情况下的第一个拦截器,除非调用OKhttpclient.builder.addInterceptor来添加全局拦截器,在RealCall.getResponseWithInterceptorChain()中添加默认的5个拦截器
-
RealCall
-
enque(callBack方法)
// RealCall.java public void enqueue(Callback responseCallback) { synchronized (this) { //每个请求只能之执行一次 if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); eventListener.callStart(this); client.dispatcher().enqueue(new AsyncCall(responseCallback)); }
-
一个call只能执行一次,否则会出现异常,这里创建一个AsyncCall并将CallBack穿入,接着交给任务分发器Dispatcher来处理
// dispatcher.java synchronized void enqueue(AsyncCall call) { //正在执行的任务数量小于最大值(64),并且此任务所属主机的正在执行任务小于最大值(5) if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
从dispatcher的enqueue方法看出,对于入队做了限制,dispatcher类定义了请求数量的最大值为64个,请求的主机正再执行任务小于5台,满足以上要求的线程就可以加入队列,通过线程池执行该任务,否则加入readyAsyncCalls线程池中等待.
-
-
AsyncCall
-
它继承于NameRunnable 而NameRunnable实现runnable接口. 作用1. 采用模板方法的设计模式,让子类具体操作放在execute()中, 作用2. 给线程指定一个名称
@Override protected void execute() { boolean signalledCallback = false; try { //调用 getResponseWithInterceptorChain()获得响应内容 Response response = getResponseWithInterceptorChain(); if (retryAndFollowUpInterceptor.isCanceled()) { //这个标记为主要是避免异常时2次回调 signalledCallback = true; //回调Callback告知失败 responseCallback.onFailure(RealCall.this, new IOException("Canceled")); } else { signalledCallback = true; //回调Callback,将响应内容传回去 responseCallback.onResponse(RealCall.this, response); } } catch (IOException e) { if (signalledCallback) { // Do not signal the callback twice! Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e); } else { eventListener.callFailed(RealCall.this, e); responseCallback.onFailure(RealCall.this, e); } } finally { //不管请求成功与否,都进行finished()操作 client.dispatcher().finished(this); } }
-
client.dispatcher().finished(this)方法
private void promoteCalls() { if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. //若条件允许,将readyAsyncCalls中的任务移动到runningAsyncCalls中,并交给线程池执行 for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } //当runningAsyncCalls满了,直接退出迭代 if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }
promoteCalls方法推动了下个任务的执行,逻辑: 判断正在请求的队列是否大于64个,判断正在准备的队列是否为空,若条件满足, readyAsyncCall的任务移动到runningAsyncCalls中,并且交给线程池去执行
-
-
interceptorChain
-
重点就是interceptors这个集合,是将前面用户自己创建的拦截器,OkHttp自带的拦截器组合成一个拦截器链
错误重定向拦截器,(retryAndFollowUpInterceptor)
桥接拦截器 (BridgeInterceptor)
缓存拦截器(CacheInterceptor)
连接拦截器(ConnectInterceptor)
-
网络拦截器 (networkInterceptors)
最后通过RealInterceptorChain#proceed(Request)来执行interceptor chain
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor> interceptors = new ArrayList<>(); //这是一个List,是有序的 interceptors.addAll(client.interceptors());//首先添加的是用户添加的全局拦截器 interceptors.add(retryAndFollowUpInterceptor); //错误、重定向拦截器 //桥接拦截器,桥接应用层与网络层,添加必要的头、 interceptors.add(new BridgeInterceptor(client.cookieJar())); //缓存处理,Last-Modified、ETag、DiskLruCache等 interceptors.add(new CacheInterceptor(client.internalCache())); //连接拦截器 interceptors.add(new ConnectInterceptor(client)); //从这就知道,通过okHttpClient.Builder#addNetworkInterceptor()传进来的拦截器只对非网页的请求生效 if (!forWebSocket) { interceptors.addAll(client.networkInterceptors()); } //真正访问服务器的拦截器 interceptors.add(new CallServerInterceptor(forWebSocket)); Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); return chain.proceed(originalRequest); }
-
RealInterceptorChain#proceed()
public Response proceed(Request request) throws IOException { return proceed(request, streamAllocation, httpCodec, connection); } public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, //...异常处理 // Call the next interceptor in the chain. RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); //...异常处理 return response; } }
-
是按照interceptors集合的顺序,逐个往下调用拦截器的intercept方法,所以最先的拦截器RetryAndFollowUpInterceptor 被调用
// RetryAndFollowUpInterceptor .java public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RealInterceptorChain realChain = (RealInterceptorChain) chain; Call call = realChain.call(); EventListener eventListener = realChain.eventListener(); //创建一个StreamAllocation StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()), call, eventListener, callStackTrace); this.streamAllocation = streamAllocation; //统计重定向次数,不能大于20 int followUpCount = 0; Response priorResponse = null; while (true) { if (canceled) { streamAllocation.release(); throw new IOException("Canceled"); } Response response; boolean releaseConnection = true; try { //调用下一个interceptor的来获得响应内容 response = realChain.proceed(request, streamAllocation, null, null); releaseConnection = false; } catch (RouteException e) { // The attempt to connect via a route failed. The request will not have been sent. if (!recover(e.getLastConnectException(), streamAllocation, false, request)) { throw e.getLastConnectException(); } releaseConnection = false; continue; } catch (IOException e) { // An attempt to communicate with a server failed. The request may have been sent. boolean requestSendStarted = !(e instanceof ConnectionShutdownException); if (!recover(e, streamAllocation, requestSendStarted, request)) throw e; releaseConnection = false; continue; } finally { // We're throwing an unchecked exception. Release any resources. if (releaseConnection) { streamAllocation.streamFailed(null); streamAllocation.release(); } } // Attach the prior response if it exists. Such responses never have a body. if (priorResponse != null) { response = response.newBuilder() .priorResponse(priorResponse.newBuilder() .body(null) .build()) .build(); } //重定向处理 Request followUp = followUpRequest(response, streamAllocation.route()); if (followUp == null) { if (!forWebSocket) { streamAllocation.release(); } return response; } closeQuietly(response.body()); if (++followUpCount > MAX_FOLLOW_UPS) { streamAllocation.release(); throw new ProtocolException("Too many follow-up requests: " + followUpCount); } if (followUp.body() instanceof UnrepeatableRequestBody) { streamAllocation.release(); throw new HttpRetryException("Cannot retry streamed HTTP body", response.code()); } if (!sameConnection(response, followUp.url())) { streamAllocation.release(); streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(followUp.url()), call, eventListener, callStackTrace); this.streamAllocation = streamAllocation; } else if (streamAllocation.codec() != null) { throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?"); } request = followUp; priorResponse = response; } }
-
这个拦截器主要用于错误处理和重定向等问题,
总结:
-
3. OKHttp架构分析
-
异步请求线程池, Dispather
public synchronized ExecutorService executorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }
- 类似于线程池的newCacheThreadPool
- 无上限,60s自动回收线程,用于大量耗时较短的任务
- dispatcher提供修改最大异步任务数和最多主机的接口.默认是64个任务5台主机
- 通过双端队列来维护准备执行的任务和正在执行的任务,readyAsyncCalls,runningAsyncCalls
- 在每个任务结束后,都会检查readyAsyncCalls是否有任务,在满足的情况下,按照先进先出的原则将任务移动到runningAsyncCalls队列中,并在线程池中执行
-
连接池清理线程池- ConnectionPool
该线程池是用来清理长时间闲置的和泄露的连接
该线程池本身无上限,闲置60s回收
-
虽然无限制,但是通过clearRunning标记来控制只有一个线程在运行,当连接池中没有连接时才会重新设置为fasle
void put(RealConnection connection) { assert (Thread.holdsLock(this)); if (!cleanupRunning) { cleanupRunning = true; executor.execute(cleanupRunnable); } connections.add(connection); }
工作线程会不断地被清理,当清理一遍完成后,根据线程池中的空闲超时连接计算出一个阻塞时间并阻塞,直到线程池中没有任何连接才结束,并将clearRunning设置为fasle
-
每次有连接加入线程池中,如果当前没有清理任务运行,会加入一个清理任务到线程池中运行,
void put(RealConnection connection) { assert (Thread.holdsLock(this)); if (!cleanupRunning) { cleanupRunning = true; executor.execute(cleanupRunnable); } connections.add(connection); }
- 缓存整理线程池 DisLruCache
- 该线程池用于整理本地请求缓存数据
- 缓存的整理包含,达到阈值大小的文件,删除最近最少使用的记录,在有关操作达到一定数量以后对记录进行重建
- 最大运行数量为1,无需考虑线程安全问题,自动回收闲置60s的线程
- Http2异步事务线程池, http2Connection
4. OKHTTP内部缓存
-
使用场景: 数据更新不频繁的查询操作,客户端缓存可以减少服务器的访问次数,无网络时候也可以显示历史数据,
max-age:这个参数告诉浏览器将页面缓存多长时间,超过这个时间后才再次向服务器发起请求检查页面是否有更新。对于静态的页面,比如图片、CSS、Javascript,一般都不大变更,因此通常我们将存储这些内容的时间设置为较长的时间,这样浏览器会不会向浏览器反复发起请求,也不会去检查是否更新了。 s-maxage:这个参数告诉缓存服务器(proxy,如Squid)的缓存页面的时间。如果不单独指定,缓存服务器将使用max-age。对于动态内容(比如文档的查看页面),我们可告诉浏览器很快就过时了(max-age=0),并告诉缓存服务器(Squid)保留内容一段时间(比如,s-maxage=7200)。一旦我们更新文档,我们将告诉Squid清除老的缓存版本。 must-revalidate:这告诉浏览器,一旦缓存的内容过期,一定要向服务器询问是否有新版本。 proxy-revalidate:proxy上的缓存一旦过期,一定要向服务器询问是否有新版本。 no-cache:不做缓存。 no-store:数据不在硬盘中临时保存,这对需要保密的内容比较重要。 public:告诉缓存服务器, 即便是对于不该缓存的内容也缓存起来,比如当用户已经认证的时候。所有的静态内容(图片、Javascript、CSS等)应该是public的。 private:告诉proxy不要缓存,但是浏览器可使用private cache进行缓存。一般登录后的个性化页面是private的。 no-transform: 告诉proxy不进行转换,比如告诉手机浏览器不要下载某些图片。 max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
-
okhttp的缓存设置 设置缓存的目录
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); File cacheFile = new File(content.getExternalCacheDir(),"tangnuer"); Cache cache = new Cache(cacheFile,1024*1024*50); //使用 httpClientBuilder .cache(cache) .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS)
-
源码分析:
-
源码入口CacheInterceptor#intercept
public Response intercept(Chain chain) throws IOException { //判断是否设置cache Response cacheCandidate = cache != null ? cache.get(chain.request()) : null; long now = System.currentTimeMillis(); CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); Request networkRequest = strategy.networkRequest; Response cacheResponse = strategy.cacheResponse; //如果cache 不为null,从strategy中追踪Response //主要是对networkRequest 或cacheResponse 进行计数 if (cache != null) { cache.trackResponse(strategy); } //如果缓存不适用,则关闭IO流 if (cacheCandidate != null && cacheResponse == null) { closeQuietly(cacheCandidate.body()); } // 如果网络被禁止并且无缓存,则返回失败504 if (networkRequest == null && cacheResponse == null) { return new Response.Builder() .request(chain.request()) .protocol(Protocol.HTTP_1_1) .code(504) .message("Unsatisfiable Request (only-if-cached)") .body(Util.EMPTY_RESPONSE) .sentRequestAtMillis(-1L) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); } // 在无网络时从缓存中获取 if (networkRequest == null) { return cacheResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build(); } Response networkResponse = null; try { //调用下一个拦截器,访问网络 networkResponse = chain.proceed(networkRequest); } finally { // If we're crashing on I/O or otherwise, don't leak the cache body. if (networkResponse == null && cacheCandidate != null) { closeQuietly(cacheCandidate.body()); } } // 如果缓存中已经存在对应的Response的处理 if (cacheResponse != null) { //表示数据未做修改 if (networkResponse.code() == HTTP_NOT_MODIFIED) { Response response = cacheResponse.newBuilder() .headers(combine(cacheResponse.headers(), networkResponse.headers())) .sentRequestAtMillis(networkResponse.sentRequestAtMillis()) .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis()) .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); networkResponse.body().close(); //主要是更新response头部数据 cache.trackConditionalCacheHit(); cache.update(cacheResponse, response); return response; } else { closeQuietly(cacheResponse.body()); } } Response response = networkResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); //写入缓存 if (cache != null) { if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) { // Offer this request to the cache. CacheRequest cacheRequest = cache.put(response); return cacheWritingResponse(cacheRequest, response); } //移除networkRequest if (HttpMethod.invalidatesCache(networkRequest.method())) { try { cache.remove(networkRequest); } catch (IOException ignored) { // The cache cannot be written. } } } return response; }
- CacheStrategy内部分装了网络请求对象newWorkRequest和CacheResponse(开始获取的候选缓存对象CacheCandie),它是Okhttp的缓存策略核心.
- 回到代码, 首先判断是否已设置了cache,如果已经设置,根据chain.request()返回的request查找cache中对应的response对象,然后创建一个CacheStrategy对象strategy,
- 再通过对网络状态的判断和缓存状态的判断,如果是网络获取未缓存的,得到response对象后,会更新写入到缓存中,再返回, 而上述代码就是对cache的操作(get,put,update,remove)
-
Cache的产生
cache是internalCache类型的对象,internalCache是okhttp的内部缓存接口,
-
Cache类的构造方法
public Cache(File directory, long maxSize) { this(directory, maxSize, FileSystem.SYSTEM); } Cache(File directory, long maxSize, FileSystem fileSystem) { this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize); }
创建了DiskLruCache类型的cache实例,这里的FileSystem.SYSTEM是FileSystem的实现,内部是基于OKio的singk/source对缓存文件进行流的操作,DiskLRUCache.entry内部维护了二个数组,保存每个Url请求对应文件的引用.然后通过DiskLruCache.editor操作数组,并为Cache.entry提供Sink/source,对文件流进行操作,
-
-
Cache的操作
-
put操作
CacheRequest put(Response response) { //获取请求方法 String requestMethod = response.request().method(); if (HttpMethod.invalidatesCache(response.request().method())) { try { remove(response.request()); } catch (IOException ignored) { // The cache cannot be written. } return null; } //如果不是GET请求时返回的response,则不进行缓存 if (!requestMethod.equals("GET")) { return null; } if (HttpHeaders.hasVaryAll(response)) { return null; } //把response封装在Cache.Entry中,调用DiskLruCache的edit()返回editor Entry entry = new Entry(response); DiskLruCache.Editor editor = null; try { //把url进行 md5(),并转换成十六进制格式 //将转换后的key作为DiskLruCache内部LinkHashMap的键值 editor = cache.edit(key(response.request().url())); if (editor == null) { return null; } //用editor提供的Okio的sink对文件进行写入 entry.writeTo(editor); //利用CacheRequestImpl写入body return new CacheRequestImpl(editor); } catch (IOException e) { abortQuietly(editor); return null; } }
OkHttp只针对Get请求时返回的数据进行缓存,
官方解释:非Get请求返回的Response也可以进行缓存,但是这样做复杂性搞高,且效益低
DiskLruCache.editor获取editor对象后,调用writeTo吧url,请求方法,响应头部字段等写入缓存,返回一个CacheRequestImpl实例,public @Nullable Editor edit(String key) throws IOException { return edit(key, ANY_SEQUENCE_NUMBER); } synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { //内部主要是利用FileSystem处理文件,如果这里出现了异常, //在最后会构建新的日志文件,如果文件已存在,则替换 initialize(); //检测缓存是否已关闭 checkNotClosed(); //检测是否为有效key validateKey(key); //lruEntries是LinkHashMap的实例,先查找lruEntries是否存在 Entry entry = lruEntries.get(key); if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) { return null; // Snapshot is stale. } //如果有Editor在操作entry,返回null if (entry != null && entry.currentEditor != null) { return null; } //如果需要,进行clean操作 if (mostRecentTrimFailed || mostRecentRebuildFailed) { executor.execute(cleanupRunnable); return null; } // 把当前key在对应文件中标记DIRTY状态,表示正在修改, //清空日志缓冲区,防止泄露 journalWriter.writeUtf8(DIRTY).writeByte(' ').writeUtf8(key).writeByte('\n'); journalWriter.flush(); if (hasJournalErrors) { return null; // 如果日志文件不能编辑 } //为请求的url创建一个新的DiskLruCache.Entry实例 //并放入lruEntries中 if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } Editor editor = new Editor(entry); entry.currentEditor = editor; return editor; }
最后的到DiskLruCache.Entry实例,这个实例主要维护Key对应的文件列表,并且内部currentEditor不为null,表示当前实例处于编译状态,返回得到editor后,调用Cache.Entry的writeTo对editor进行操作
public void writeTo(DiskLruCache.Editor editor) throws IOException { BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA)); sink.writeUtf8(url) .writeByte('\n'); sink.writeUtf8(requestMethod) .writeByte('\n'); sink.writeDecimalLong(varyHeaders.size()) .writeByte('\n'); //... ...省略,都是利用sink进行写入操作 sink.close(); }
editor.newSink为上层Cache.entry提供一个sink,然后进行文件写入操作,这里只是写入url,请求方法,头部字段等,并未写入body部分,body部分的写入在CacheInterceptor.intercept方法的内部调用CacheWritingResponse写入body
@Override public Response intercept(Chain chain) throws IOException { ... ... //省略代码 if (cache != null) { if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) { // Offer this request to the cache. CacheRequest cacheRequest = cache.put(response); //在内部实现了response的body的写入 return cacheWritingResponse(cacheRequest, response); } ... ... //省略代码 )
-
get操作
@Nullable Response get(Request request) { //把url转换成key String key = key(request.url()); DiskLruCache.Snapshot snapshot; Entry entry; try { //通过DiskLruCache的get()根据具体的key获取DiskLruCache.Snapshot实例 snapshot = cache.get(key); if (snapshot == null) { return null; } } catch (IOException e) { // Give up because the cache cannot be read. return null; } try { //通过snapshot.getSource()获取一个Okio的Source entry = new Entry(snapshot.getSource(ENTRY_METADATA)); } catch (IOException e) { Util.closeQuietly(snapshot); return null; } //根据snapshot获取缓存中的response Response response = entry.response(snapshot); if (!entry.matches(request, response)) { Util.closeQuietly(response.body()); return null; } return response; }
snapshor是DiskLruCache.Entry的一个快照,内部封装了DiskLruCache.entry对应文件的source,简单来说,根据条件从DiskLruCache.entry找到对应的缓存文件,并生成source文件,封装在snapshot内部,通过snapshort.getsource获取source,对文件进行读取操作
//DiskLruCache # get() public synchronized Snapshot get(String key) throws IOException { initialize(); checkNotClosed(); validateKey(key); //从lruEntries查找entry, Entry entry = lruEntries.get(key); if (entry == null || !entry.readable) return null; //得到Entry的快照值snapshot Snapshot snapshot = entry.snapshot(); if (snapshot == null) return null; redundantOpCount++; journalWriter.writeUtf8(READ).writeByte(' ').writeUtf8(key).writeByte('\n'); //如果redundantOpCount超过2000,且超过lruEntries的大小时,进行清理操作 if (journalRebuildRequired()) { executor.execute(cleanupRunnable); } return snapshot; } //DiskLruCache.Entry # snapshot() Snapshot snapshot() { if (!Thread.holdsLock(DiskLruCache.this)) throw new AssertionError(); Source[] sources = new Source[valueCount]; // Defensive copy since these can be zeroed out. long[] lengths = this.lengths.clone(); try { //遍历已缓存的文件,生成相应的sources for (int i = 0; i < valueCount; i++) { sources[i] = fileSystem.source(cleanFiles[i]); } //创建Snapshot并返回 return new Snapshot(key, sequenceNumber, sources, lengths); } catch (FileNotFoundException e) { // A file must have been deleted manually! for (int i = 0; i < valueCount; i++) { if (sources[i] != null) { Util.closeQuietly(sources[i]); } else { break; } } // Since the entry is no longer valid, remove it so the metadata is accurate (i.e. the cache // size.) try { removeEntry(this); } catch (IOException ignored) { } return null; } }
Cache.Entry # response()
public Response response(DiskLruCache.Snapshot snapshot) { String contentType = responseHeaders.get("Content-Type"); String contentLength = responseHeaders.get("Content-Length"); Request cacheRequest = new Request.Builder() .url(url) .method(requestMethod, null) .headers(varyHeaders) .build(); return new Response.Builder() .request(cacheRequest) .protocol(protocol) .code(code) .message(message) .headers(responseHeaders) .body(new CacheResponseBody(snapshot, contentType, contentLength)) .handshake(handshake) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(receivedResponseMillis) .build(); }
-
-
总结 : Cache只是一个上层的执行者,内部真正的缓存是由DiskLruCache实现的,在DiskLruCache里面通过FileStem,基于okio的sink/source对文件进行流的操作.
福利
由于篇幅有限很多细节无法具体分析 如想了解更多OkHttp有关的知识, 我推荐一部详细的开源框架视频教程 视频地址在youtub上 当然本人也是小白一个 只能做资源的搬运..