当前位置: 代码迷 >> 综合 >> OkHttp3.0 拦截器、调度器源码解析
  详细解决方案

OkHttp3.0 拦截器、调度器源码解析

热度:75   发布时间:2024-01-06 17:35:35.0

聊一个框架的源码,我们先从它的用法说起,通过OkHttp来请求网络一般不外乎通过以下两种方式 :
1、异步回调网络请求

OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url("https://github.com/hongyangAndroid").build();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.e(TAG, e.toString());}@Overridepublic void onResponse(Call call, Response response) throws IOException {Log.e(TAG, response.body().string());}});}

2、同步网络请求

OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url("https://github.com/hongyangAndroid").build();Call call = client.newCall(request);try {call.execute();} catch (IOException e) {e.printStackTrace();}

我们根据代码的执行顺序来一个个类分析,首先查看 OkHttpClient 的构造方法

public OkHttpClient() {this(new Builder());}OkHttpClient(Builder builder) {this.dispatcher = builder.dispatcher;this.proxy = builder.proxy;this.protocols = builder.protocols;this.connectionSpecs = builder.connectionSpecs;this.interceptors = Util.immutableList(builder.interceptors);this.networkInterceptors = Util.immutableList(builder.networkInterceptors);this.eventListenerFactory = builder.eventListenerFactory;this.proxySelector = builder.proxySelector;this.cookieJar = builder.cookieJar;this.cache = builder.cache;this.internalCache = builder.internalCache;this.socketFactory = builder.socketFactory;boolean isTLS = false;for (ConnectionSpec spec : connectionSpecs) {isTLS = isTLS || spec.isTls();}if (builder.sslSocketFactory != null || !isTLS) {this.sslSocketFactory = builder.sslSocketFactory;this.certificateChainCleaner = builder.certificateChainCleaner;} else {X509TrustManager trustManager = Util.platformTrustManager();this.sslSocketFactory = newSslSocketFactory(trustManager);this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);}if (sslSocketFactory != null) {Platform.get().configureSslSocketFactory(sslSocketFactory);}this.hostnameVerifier = builder.hostnameVerifier;this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(certificateChainCleaner);this.proxyAuthenticator = builder.proxyAuthenticator;this.authenticator = builder.authenticator;this.connectionPool = builder.connectionPool;this.dns = builder.dns;this.followSslRedirects = builder.followSslRedirects;this.followRedirects = builder.followRedirects;this.retryOnConnectionFailure = builder.retryOnConnectionFailure;this.callTimeout = builder.callTimeout;this.connectTimeout = builder.connectTimeout;this.readTimeout = builder.readTimeout;this.writeTimeout = builder.writeTimeout;this.pingInterval = builder.pingInterval;if (interceptors.contains(null)) {throw new IllegalStateException("Null interceptor: " + interceptors);}if (networkInterceptors.contains(null)) {throw new IllegalStateException("Null network interceptor: " + networkInterceptors);}}

OkHttpClient 的构造方法初始化了一系列的网络相关参数,相信英语不是太差的朋友应该能看明白这些初始化的变量含义,这里不是核心就不过多介绍,咱们接着看 new Request.Builder() 这个内部类

public Builder() {this.method = "GET";this.headers = new Headers.Builder();}

由上面的代码我们可以得知,如果没有设置网络的请求方法,那默认的就是 GET 请求,接着查看 url()方法

/*** Sets the URL target of this request.** @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this* exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs.*/public Builder url(String url) {if (url == null) throw new NullPointerException("url == null");// Silently replace web socket URLs with HTTP URLs.if (url.regionMatches(true, 0, "ws:", 0, 3)) {url = "http:" + url.substring(3);} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {url = "https:" + url.substring(4);}return url(HttpUrl.get(url));}

build()方法

public Request build() {if (url == null) throw new IllegalStateException("url == null");return new Request(this);}

很明显,我们的网络请求通过 Request.Builder 这个内部类采用了 Build 设计模式来设置,我们接着往下看
client.newCall(request),client 是OkHttpClient的实例变量,我们进入OkHttpClient的newCall()方法

/*** Prepares the {@code request} to be executed at some point in the future.*/@Override public Call newCall(Request request) {return RealCall.newRealCall(this, request, false /* for web socket */);}

ok,跟着源码,我们进入 RealCall 这个类

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {this.client = client;this.originalRequest = originalRequest;this.forWebSocket = forWebSocket;}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.transmitter = new Transmitter(client, call);return call;}

好,基本清晰了,我们回过头跟着代码接着看同步调用 call.execute(); 这个方法的执行流程,点击进入RealCall的execute()方法

@Override public Response execute() throws IOException {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}transmitter.timeoutEnter();transmitter.callStart();try {client.dispatcher().executed(this);return getResponseWithInterceptorChain();} finally {client.dispatcher().finished(this);}}

client.dispatcher() 返回的是一个 Dispatcher 实例对象,这个是OkHttp的调度器,这个咱们后面的代码再讨论它的作用,接着看.executed(this)

/** Executes calls. Created lazily. */private @Nullable ExecutorService executorService;/** Ready async calls in the order they'll be run. */private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();/** Running synchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();public Dispatcher(ExecutorService executorService) {this.executorService = executorService;}/** Used by {@code Call#execute} to signal it is in-flight. */synchronized void executed(RealCall call) {runningSyncCalls.add(call);}

RealCall 实现了 Call 接口,executed 方法就是把当前的任务加入到运行中任务双端队列,Deque 属于先入先出的数据结构,接着看 getResponseWithInterceptorChain() 方法,这个方法是核心中的核心,关键中的关键,咱们通常说的拦截器就是在这个方法中初始化和调度,看这个方法名我们也能知道它的作用,根据拦截器链来获取响应

Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors.List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());interceptors.add(new RetryAndFollowUpInterceptor(client));interceptors.add(new BridgeInterceptor(client.cookieJar()));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, transmitter, null, 0,originalRequest, this, client.connectTimeoutMillis(),client.readTimeoutMillis(), client.writeTimeoutMillis());boolean calledNoMoreExchanges = false;try {Response response = chain.proceed(originalRequest);if (transmitter.isCanceled()) {closeQuietly(response);throw new IOException("Canceled");}return response;} catch (IOException e) {calledNoMoreExchanges = true;throw transmitter.noMoreExchanges(e);} finally {if (!calledNoMoreExchanges) {transmitter.noMoreExchanges(null);}}}

client.interceptors() 属于开发者自定义的拦截器,它会首先被添加进拦截器队列,依次往下分别是重定向拦截器、桥拦截器、缓存拦截器、连接拦截器、网络连接拦截器以及最后的CallServerInterceptor负责最终的服务器连接,其中的 if 判断中的forWebSocket 变量在非 Socket请求时为false,也就是说长连接是不会添加网络拦截器的,代码接着往下走,咱们查看 RealInterceptorChain 构造方法

public RealInterceptorChain(List<Interceptor> interceptors, Transmitter transmitter,@Nullable Exchange exchange, int index, Request request, Call call,int connectTimeout, int readTimeout, int writeTimeout) {this.interceptors = interceptors;this.transmitter = transmitter;this.exchange = exchange;this.index = index;this.request = request;this.call = call;this.connectTimeout = connectTimeout;this.readTimeout = readTimeout;this.writeTimeout = writeTimeout;}

很好,初始化了一些参数,接着看 chain.proceed(originalRequest) ,也就是 RealInterceptorChain 类中的proceed 方法

@Override public Response proceed(Request request) throws IOException {return proceed(request, transmitter, exchange);}public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)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.exchange != null && !this.exchange.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.exchange != null && calls > 1) {throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)+ " must call proceed() exactly once");}// Call the next interceptor in the chain.RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,index + 1, request, call, connectTimeout, readTimeout, writeTimeout);Interceptor interceptor = interceptors.get(index);Response response = interceptor.intercept(next);// Confirm that the next interceptor made its required call to chain.proceed().if (exchange != 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");}if (response.body() == null) {throw new IllegalStateException("interceptor " + interceptor + " returned a response with no body");}return response;}

重点查看 interceptor.intercept(next) 方法,OkHttp 的拦截器采用的是责任链设计模式,原理就是符合规则依次调用,这里的规则就是当前的拦截器将下一个拦截器通过intercept()方法传递,然后与此同时 index + 1,我们依次点开这些拦截器的 intercept 做了什么,还记得之前咱们看的拦截器添加顺序吗,代码进入RetryAndFollowUpInterceptor拦截器的intercept ()方法

@Override public Response intercept(Chain chain) throws IOException {Request request = chain.request();RealInterceptorChain realChain = (RealInterceptorChain) chain;Transmitter transmitter = realChain.transmitter();int followUpCount = 0;Response priorResponse = null;while (true) {transmitter.prepareToConnect(request);if (transmitter.isCanceled()) {throw new IOException("Canceled");}Response response;boolean success = false;try {response = realChain.proceed(request, transmitter, null);success = true;} catch (RouteException e) {// The attempt to connect via a route failed. The request will not have been sent.if (!recover(e.getLastConnectException(), transmitter, false, request)) {throw e.getFirstConnectException();}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, transmitter, requestSendStarted, request)) throw e;continue;} finally {// The network call threw an exception. Release any resources.if (!success) {transmitter.exchangeDoneDueToException();}}// 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();}Exchange exchange = Internal.instance.exchange(response);Route route = exchange != null ? exchange.connection().route() : null;Request followUp = followUpRequest(response, route);if (followUp == null) {if (exchange != null && exchange.isDuplex()) {transmitter.timeoutEarlyExit();}return response;}RequestBody followUpBody = followUp.body();if (followUpBody != null && followUpBody.isOneShot()) {return response;}closeQuietly(response.body());if (transmitter.hasExchange()) {exchange.detachWithViolence();}if (++followUpCount > MAX_FOLLOW_UPS) {throw new ProtocolException("Too many follow-up requests: " + followUpCount);}request = followUp;priorResponse = response;}}

这里的Chain 参数就是咱们传进来的拦截器,它就是 BridgeInterceptor 拦截器,然后根据realChain.proceed()方法接着看,realChain是RealInterceptorChain类型,但是它的实例化对象实际上是RealInterceptorChain,实际上,咱们又再一次调用了proceed()方法,接下来相信大家都能理解了,遍历整个队列直到最后一个访问服务器拦截器,咱们点进CallServerInterceptor去看看

@Override public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;Exchange exchange = realChain.exchange();Request request = realChain.request();long sentRequestMillis = System.currentTimeMillis();exchange.writeRequestHeaders(request);boolean responseHeadersStarted = false;Response.Builder responseBuilder = null;if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100// Continue" response before transmitting the request body. If we don't get that, return// what we did get (such as a 4xx response) without ever transmitting the request body.if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {exchange.flushRequest();responseHeadersStarted = true;exchange.responseHeadersStart();responseBuilder = exchange.readResponseHeaders(true);}if (responseBuilder == null) {if (request.body().isDuplex()) {// Prepare a duplex body so that the application can send a request body later.exchange.flushRequest();BufferedSink bufferedRequestBody = Okio.buffer(exchange.createRequestBody(request, true));request.body().writeTo(bufferedRequestBody);} else {// Write the request body if the "Expect: 100-continue" expectation was met.BufferedSink bufferedRequestBody = Okio.buffer(exchange.createRequestBody(request, false));request.body().writeTo(bufferedRequestBody);bufferedRequestBody.close();}} else {exchange.noRequestBody();if (!exchange.connection().isMultiplexed()) {// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection// from being reused. Otherwise we're still obligated to transmit the request body to// leave the connection in a consistent state.exchange.noNewExchangesOnConnection();}}} else {exchange.noRequestBody();}if (request.body() == null || !request.body().isDuplex()) {exchange.finishRequest();}if (!responseHeadersStarted) {exchange.responseHeadersStart();}if (responseBuilder == null) {responseBuilder = exchange.readResponseHeaders(false);}Response response = responseBuilder.request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();int code = response.code();if (code == 100) {// server sent a 100-continue even though we did not request one.// try again to read the actual responseresponse = exchange.readResponseHeaders(false).request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();code = response.code();}exchange.responseHeadersEnd(response);if (forWebSocket && code == 101) {// Connection is upgrading, but we need to ensure interceptors see a non-null response body.response = response.newBuilder().body(Util.EMPTY_RESPONSE).build();} else {response = response.newBuilder().body(exchange.openResponseBody(response)).build();}if ("close".equalsIgnoreCase(response.request().header("Connection"))|| "close".equalsIgnoreCase(response.header("Connection"))) {exchange.noNewExchangesOnConnection();}if ((code == 204 || code == 205) && response.body().contentLength() > 0) {throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());}return response;}

这个类做了写入请求头、写入请求体、读取响应头、读取响应体,对返回状态码进行了统一处理,最后返回了 Response,然后返回到 RealInterceptorChain的proceed方法,最后再回到RealCall 的 execute()

@Overridepublic Response execute() throws IOException {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}transmitter.timeoutEnter();transmitter.callStart();try {client.dispatcher().executed(this);return getResponseWithInterceptorChain();} finally {client.dispatcher().finished(this);}}

最后调用client.dispatcher().finished(this) ,也就是咱们之前说过的Dispatcher调度器,咱们点开源码看看它是干嘛的

/** Used by {@code AsyncCall#run} to signal completion. */void finished(AsyncCall call) {call.callsPerHost().decrementAndGet();finished(runningAsyncCalls, call);}private <T> void finished(Deque<T> calls, T call) {Runnable idleCallback;synchronized (this) {if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");idleCallback = this.idleCallback;}boolean isRunning = promoteAndExecute();if (!isRunning && idleCallback != null) {idleCallback.run();}}private boolean promoteAndExecute() {assert (!Thread.holdsLock(this));List<AsyncCall> executableCalls = new ArrayList<>();boolean isRunning;synchronized (this) {for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {AsyncCall asyncCall = i.next();if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.i.remove();asyncCall.callsPerHost().incrementAndGet();executableCalls.add(asyncCall);runningAsyncCalls.add(asyncCall);}isRunning = runningCallsCount() > 0;}for (int i = 0, size = executableCalls.size(); i < size; i++) {AsyncCall asyncCall = executableCalls.get(i);asyncCall.executeOn(executorService());}return isRunning;}

promoteAndExecute() 方法中去遍历readyAsyncCalls(等待任务队列),当runningAsyncCalls(运行中任务队列)的长度大于等于maxRequests(默认64)就终止循环,asyncCall.callsPerHost().get() 表示当前主机名的请求数量,maxRequestsPerHost是最大请求数(默认5),当大于等于5就跳过本次循环,当满足以上两个判断才进入以下的逻辑,i.remove() 把任务从等待队列移除, runningAsyncCalls.add(asyncCall)添加到运行中队列,最后for循环,调用线程池执行任务,整个调度器的功能也差不多了,咱们分析了一遍同步调用的源码,再看看异步回调是如何处理的,进入 RealCall 的enqueue()方法

@Override 
public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}transmitter.callStart();client.dispatcher().enqueue(new AsyncCall(responseCallback));}

咱们看下AsyncCall类的继承关系,AsyncCall继承抽象类NamedRunnable,NamedRunnable继承Runnable,这个抽象类的作用就是在Runnable任务执行前设置线程名称然后执行完毕后再重新设置回之前的线程名

public abstract class NamedRunnable implements Runnable {protected final String name;public NamedRunnable(String format, Object... args) {this.name = Util.format(format, args);}@Override public final void run() {String oldName = Thread.currentThread().getName();Thread.currentThread().setName(name);try {execute();} finally {Thread.currentThread().setName(oldName);}}protected abstract void execute();
}

代码很清晰,任务执行后会触发AsyncCall的execute()方法

final class AsyncCall extends NamedRunnable {private final Callback responseCallback;private volatile AtomicInteger callsPerHost = new AtomicInteger(0);AsyncCall(Callback responseCallback) {super("OkHttp %s", redactedUrl());this.responseCallback = responseCallback;}AtomicInteger callsPerHost() {return callsPerHost;}void reuseCallsPerHostFrom(AsyncCall other) {this.callsPerHost = other.callsPerHost;}String host() {return originalRequest.url().host();}Request request() {return originalRequest;}RealCall get() {return RealCall.this;}/*** Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up* if the executor has been shut down by reporting the call as failed.*/void executeOn(ExecutorService executorService) {assert (!Thread.holdsLock(client.dispatcher()));boolean success = false;try {executorService.execute(this);success = true;} catch (RejectedExecutionException e) {InterruptedIOException ioException = new InterruptedIOException("executor rejected");ioException.initCause(e);transmitter.noMoreExchanges(ioException);responseCallback.onFailure(RealCall.this, ioException);} finally {if (!success) {client.dispatcher().finished(this); // This call is no longer running!}}}@Override protected void execute() {boolean signalledCallback = false;transmitter.timeoutEnter();try {Response response = getResponseWithInterceptorChain();signalledCallback = true;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 {responseCallback.onFailure(RealCall.this, e);}} finally {client.dispatcher().finished(this);}}}

代码已经非常明显了,接口回调,OK,源码解析完毕,如有遗误,请留言。