Okhttp是Android开发常用的一个网络请求框架,下面将按照自己的理解将okhttp分为三条主线进行分析。
OkHttpClient okHttpClient= new OkHttpClient.Builder().build();
Request request=new Request.Builder().url("").build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
}
});
首先先看call.enqueue方法,在call接口中
/**
* Schedules the request to be executed at some point in the future.
*
* The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
* immediately unless there are several other requests currently being executed.
*
*
This client will later call back {@code responseCallback} with either an HTTP response or a
* failure exception.
*
* @throws IllegalStateException when the call has already been executed.
*/
void enqueue(Callback responseCallback);
再看一下call接口的实现类
@Override 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));
}
在进去看一下client.dispatcher().enqueue(new AsyncCall(responseCallback))方法
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以发现在dispatcher类中有两个队列,一个是runningSyncCalls,一个是readyAyncCalls,也就是运行中的队列和等待中的队列。
并且有判断条件,当运行中队列请求数小于64并且访问同一目标机器的数量小于5,将请求放入运行中的队列,不然放入等待队列。
但是你会发现一个问题,假设我们有84个请求,那么我们会将64条请求放入运行中队列,剩下的86-64条放到等待中的队列,那么等待中的队列要等到什么时候进行处理呢?如果两个队列中都有数据再来20条数据又要怎么办?这个时候我们再看第二条主线。
点进行看executorService().execute(call)
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
可以发现重点在与我们传入的call,回到
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
再观察AsyncCall
final class AsyncCall extends NamedRunnable{
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
再点进去看一下继承类NamedRunnable
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();
}
发现run()方法中会执行execute()方法,且这里的execute方法是抽象的
那么也就表示我们在执行 executorService().execute(call);的时候其实会调用AsyncCall的execute()方法。说明我们把请求放在运行中队列的时候会立马放入线程池执行。
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
这里的代码也就是代表是请求怎么访问服务器的。点进行看一下getResponseWithInterceptorChain,getResponseWithInterceptorChain是处理请求的方法。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
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, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
这一块运用到了责任链模式,假设客户端的请求到达服务端,中间有3个节点要求,需要满足个节点的要求才能到达服务器,如果你第1个节点需要验证的东西你通不过,那么就到达不了第2个节点,在第1个节点就被拦截了。使用这种设计模式的话,可以节省很多无用功,做到优化。
还有可以添加自己定义的拦截器,增加框架的拓展性。
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
在execute()方法中点进去 client.dispatcher().finished(this);
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
再进来
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
有个promoteCalls()方法
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
如果运行中的队列个数已经大于等于maxRequests64,则直接返回,如果等待中的队列为空,那么也直接返回。不然的话就去循环等待中的队列,把等待中的队列移除并将其放入运行中的队列,再和上次请求消费完一样,放入线程池进行处理。
说明了当某个请求被消费掉之后,会回来检查等待队列中是否有数据,如果有数据,则将数据从等待中移除,然后放入运行中,再直接交给线程池进行处理。
下面是几个常见的问题,如果有错误欢迎指正!
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new HeaderInterceptor())
.addInterceptor(new LogInterceptor())
.addInterceptor(new HttpLoggingInterceptor(logger)
......
.readTimeout(30, TimeUnit.SECONDS)
.cache(cache)
.build();
OkHttp中最直接的建造者模式的使用就是XXBuilder的使用。在OkHttp中的OkHttpClient、Request、Response、HttpUrl、Headers、MultipartBody等大量使用了类似的建造者模式。
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
......
public static final class Builder {
Dispatcher dispatcher;
@Nullable Proxy proxy;
int callTimeout;
int connectTimeout;
......
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
}
public class Headers {
......
public static final class Builder {
final List<String> namesAndValues = new ArrayList<>(20);
......
public Headers build() {
return new Headers(this);
}
}
}
public class Request {
......
public static class Builder {
@Nullable HttpUrl url;
String method;
Headers.Builder headers;
@Nullable RequestBody body;
......
public Request build() {
return new Request(this);
}
}
}
将对象的创建与表示相分离,Builder负责组装各项配置参数,并且生成对象,目标对象则对外提供接口,符合类的单一原则。
工厂模式相对于建创者模式的生成对象的过程更复杂,侧重于对象的生成过程,比如
public interface Call extends Cloneable {
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
Call clone();
//创建Call实现对象的工厂
interface Factory {
//创建新的Call,里面包含了Request对象。
Call newCall(Request request);
}
}
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
}
final class RealCall implements Call {
......
}
在Call接口中,有一个内部工厂Factory接口。这样只要像下面这样就可以了:
实现Call接口,现实相应的功能,RealCall;
使用某个类(OkHttpClient)实现Call.Factory接口,在newCall中返回RealCall对象,就可以了。
而这里同步请求各异步请求又略有不同
同步请求
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
因为同步请求不需要线程池,也不存在任何限制。所以分发器仅做一下记录。后续按照加入队列的顺序同步请求即可
异步请求
synchronized void enqueue(AsyncCall call) {
//请求数最大不超过64,同一Host请求不能超过5个
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
当正在执行的任务未超过最大限制64,同时同一Host的请求不超过5个,则会添加到正在执行队列,同时提交给线程池。否则先加入等待队列。
每个任务完成后,都会调用分发器的finished方法,这里面会取出等待队列中的任务继续执行