OkHttp源码阅读(四)拦截器、缓存拦截器实现、磁盘缓存

1.拦截器实现拦截,缓存拦截器实现,顺带了解下磁盘缓存(从源码可知默认只缓存GET请求的数据)

默认缓存设置的入口

okHttpClient.newCall(request).execute()
final class RealCall implements Call {
	@Override 
	public Response execute() throws IOException {
		synchronized (this) {
			if (executed) throw new IllegalStateException("Already Executed");
			executed = true;
		}
		captureCallStackTrace();
		try {//把请求塞入队列,在Dispatcher源码解析中有讲
			client.dispatcher().executed(this);
			//通过拦截器链获取Response
			Response result = getResponseWithInterceptorChain();
			if (result == null) throw new IOException("Canceled");
			return result;
		} finally {//通过dispatcher把当前请求从队列中移除
			client.dispatcher().finished(this);
		}
	}
	//通过拦截器链获取Response
	Response getResponseWithInterceptorChain() throws IOException {
		List<Interceptor> interceptors = new ArrayList<>();//所有拦截器都会放到这里
		interceptors.addAll(client.interceptors());//开发传入的拦截器
		interceptors.add(retryAndFollowUpInterceptor);//重试拦截器
		//把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应
		interceptors.add(new BridgeInterceptor(client.cookieJar()));
		//磁盘缓存调用就是从这个CacheInterceptor开始
		interceptors.add(new CacheInterceptor(client.internalCache()));//缓存处理拦截器(读取/更新)
		interceptors.add(new ConnectInterceptor(client));//连接拦截器(复用连接池)
		if (!forWebSocket) {
			interceptors.addAll(client.networkInterceptors());
		}
		//向服务器发送请求数据、从服务器读取响应数据
		interceptors.add(new CallServerInterceptor(forWebSocket));
		Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
		return chain.proceed(originalRequest);
	}
}
	
/**
 * RealInterceptorChain用来包装拦截器,RealInterceptorChain调用proceed()方法取出一个拦截器,执行拦截器的intercept方法,
 * 执行完之后取出下一个拦截器,包装成一个新的RealInterceptorChain对象调用proceed()...直到所有拦截器全部执行完
 */
public final class RealInterceptorChain implements Interceptor.Chain {
	public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
		if (index >= interceptors.size()) throw new AssertionError();
		calls++;//记录调用次数
		// If we already have a stream, confirm that the incoming request will use it.
		if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
			throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must retain the same host and port");
		}
		// If we already have a stream, confirm that this is the only call to chain.proceed().
		if (this.httpCodec != null && calls > 1) {
			throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must call proceed() exactly once");
		}
		//取出下一个拦截器,包装成新的RealInterceptorChain对象
		RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request);
		Interceptor interceptor = interceptors.get(index);//取出当前拦截器
		Response response = interceptor.intercept(next);//执行当前拦截器的intercept方法
		// Confirm that the next interceptor made its required call to chain.proceed().
		if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
			throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once");
		}
		// Confirm that the intercepted response isn't null.
		if (response == null) {
			throw new NullPointerException("interceptor " + interceptor + " returned null");
		}
		return response;
	}
}
/**缓存拦截器
 * 缓存的操作,读取、更新、删除,真正的缓存实现在 okhttp3.Cache.java
 */
public final class CacheInterceptor implements Interceptor {
	@Override 
	public Response intercept(Chain chain) throws IOException {
		Response cacheCandidate = cache != null 
			? cache.get(chain.request()) //根据request.url获取缓存
			: null;
		long now = System.currentTimeMillis();
		//缓存策略,解析request的header和缓存response的header,获取header中配置的缓存策略信息封装在strategy对象中
		CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
		Request networkRequest = strategy.networkRequest;//解析后更新request的header,生成的新的request对象
		Response cacheResponse = strategy.cacheResponse;//解析后更新response的header,生成的新的response对象
		if (cache != null) {
			cache.trackResponse(strategy);//跟踪缓存调用,记录调用次数
		}
		//根据缓存策略得到的缓存为空,说明缓存已失效,关闭从缓存读取的response
		if (cacheCandidate != null && cacheResponse == null) {
			closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
		}
		// If we're forbidden from using the network and the cache is insufficient, fail.
		//根据解析header缓存策略得到的request、response都为空,说明不允许使用缓存
		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 we don't need the network, we're done.
		if (networkRequest == null) {
			return cacheResponse.newBuilder()
					.cacheResponse(stripBody(cacheResponse))//去除cacheResponse的body
					.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());
			}
		}
		// If we have a cache response too, then we're doing a conditional get.
		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();
				// Update the cache after combining headers but before stripping the
				// Content-Encoding header (as performed by initContentStream()).
				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);
			}
			//删除POST、PATCH、PUT、DELETE、MOVE这些类型请求的缓存(GET请求没有删除诶)
                        if (HttpMethod.invalidatesCache(networkRequest.method())) {
				try {
					cache.remove(networkRequest);
				} catch (IOException ignored) {
					// The cache cannot be written.
				}
			}
		}
		return response;
	}
}
参考资料: https://www.jianshu.com/p/9cebbbd0eeab


磁盘缓存:OkHttp缓存控制类okhttp3.Cache.java
                 真正缓存读写是在okhttp3.internal.cache.DiskLruCache.java
    DiskLruCache的实现类似Glide的DiskLruCache实现

public final class Cache implements Closeable, Flushable {
	private static final int VERSION = 201105;
	private static final int ENTRY_METADATA = 0;
	private static final int ENTRY_BODY = 1;
	private static final int ENTRY_COUNT = 2;
	final InternalCache internalCache = new InternalCache() {//
		@Override
		public Response get(Request request) throws IOException {
			return okhttp3.Cache.this.get(request);
		}
		@Override
		public CacheRequest put(Response response) throws IOException {
			return okhttp3.Cache.this.put(response);
		}
		@Override
		public void remove(Request request) throws IOException {
			okhttp3.Cache.this.remove(request);
		}
		@Override
		public void update(Response cached, Response network) {
			okhttp3.Cache.this.update(cached, network);
		}
		...
	};
	final DiskLruCache cache;
	...
	Cache(File directory, long maxSize, FileSystem fileSystem) {
		this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);
	}
	public static String key(HttpUrl url) {//rul用md5加密后转二进制再用utf8编码作为key
		return ByteString.encodeUtf8(url.toString()).md5().hex();
	}
	@Nullable
	Response get(Request request) {//读取缓存
		String key = key(request.url());
		DiskLruCache.Snapshot snapshot;
		okhttp3.Cache.Entry entry;
		try {
			snapshot = cache.get(key);//调用DiskLruCache.get(key)
			if (snapshot == null) {
				return null;
			}
		} catch (IOException e) {// Give up because the cache cannot be read.
			return null;
		}
		...
	}
	@Nullable
	CacheRequest put(Response response) {//写入缓存
		String requestMethod = response.request().method();
		//不缓存POST、PATCH、PUT、DELETE、MOVE这些类型请求的数据
		if (HttpMethod.invalidatesCache(response.request().method())) {
			try {
				remove(response.request());
			} catch (IOException ignored) {
				// The cache cannot be written.
			}
			return null;
		}
		if (!requestMethod.equals("GET")) {//不缓存非GET请求的数据
			// Don't cache non-GET responses. We're technically allowed to cache
			// HEAD requests and some POST requests, but the complexity of doing
			// so is high and the benefit is low.
			return null;
		}
		if (HttpHeaders.hasVaryAll(response)) {
			return null;
		}
		//下面这块具体缓存的读写,与Glide的DiskLruCache非常像,原理是一样的
		okhttp3.Cache.Entry entry = new okhttp3.Cache.Entry(response);
		DiskLruCache.Editor editor = null;
		try {
			editor = cache.edit(key(response.request().url()));
			if (editor == null) {
				return null;
			}
			entry.writeTo(editor);
			return new okhttp3.Cache.CacheRequestImpl(editor);
		} catch (IOException e) {
			abortQuietly(editor);
			return null;
		}
	}

	void remove(Request request) throws IOException {//删除缓存
		cache.remove(key(request.url()));
	}

	void update(Response cached, Response network) {//更新缓存
		okhttp3.Cache.Entry entry = new okhttp3.Cache.Entry(network);
		DiskLruCache.Snapshot snapshot = ((okhttp3.Cache.CacheResponseBody) cached.body()).snapshot;
		DiskLruCache.Editor editor = null;
		try {
			editor = snapshot.edit(); // Returns null if snapshot is not current.
			if (editor != null) {
				entry.writeTo(editor);
				editor.commit();
			}
		} catch (IOException e) {
			abortQuietly(editor);
		}
	}
	...
}	


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值