查看原文
其他

OkHttp源码解析

开了肯 开了肯 2022-08-26

OkHttp源码解析

现在OkHttp在安卓的网络请求框架已经占据了绝对的领导地位,今天我们就从源码层次一点点拨开云雾,来看看它具体的实现原理。

我们先讲解几个重点类:

OkHttpClient :我们的入口类,里面持有了分发器,拦截器等等。能够将分发器和拦截器等进行统一的管理。通过Builder模式来进行创建。Dispatcher:分发器,也是我们的请求最终执行的地方,里面有一个线程池,能够将我们的网络请求进行分发处理。Interceptor:拦截器,通过责任链模式,一层层的将请求处理交给具体的类进行处理

首先我们来看一下具体的调用

OkHttpClient.Builder builder = new OkHttpClient.Builder(); //设置超时时间 builder.connectTimeout(TIMEOUT_CONNECT, TimeUnit.MILLISECONDS); builder.writeTimeout(TIMEOUT_WRITES, TimeUnit.MILLISECONDS); builder.readTimeout(TIMEOUT_READ, TimeUnit.MILLISECONDS); okHttpClient = builder.build();

Builder 中进行了一些数据的初始化工作,我们在这里可以进行一些自己的设置,比如代理,超时时间,线程池,SSL认证等。

public Builder() { dispatcher = new Dispatcher(); protocols = DEFAULT_PROTOCOLS; connectionSpecs = DEFAULT_CONNECTION_SPECS; eventListenerFactory = EventListener.factory(EventListener.NONE); proxySelector = ProxySelector.getDefault(); if (proxySelector == null) { proxySelector = new NullProxySelector(); } cookieJar = CookieJar.NO_COOKIES; socketFactory = SocketFactory.getDefault(); hostnameVerifier = OkHostnameVerifier.INSTANCE; certificatePinner = CertificatePinner.DEFAULT; proxyAuthenticator = Authenticator.NONE; authenticator = Authenticator.NONE; connectionPool = new ConnectionPool(); dns = Dns.SYSTEM; followSslRedirects = true; followRedirects = true; retryOnConnectionFailure = true; callTimeout = 0; connectTimeout = 10_000; readTimeout = 10_000; writeTimeout = 10_000; pingInterval = 0; }

当初始化完成以后,我们就可以执行我们的网络请求了。

String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build(); client.newCall(request).enqueue(new Callback(){ @Override public void onResponse(Call call, final Response response) throws IOException { } @Override public void onFailure(Call call, final IOException e) { } }); }

先调用了 newCall(request) 方法,我们继续跟踪

@Override public Call newCall(Request request) { //生成了一个RealCall类 return RealCall.newRealCall(this, request, false /* for web socket */); }

所以,enqueue 的具体实现,是由 RealCall 这个类来实现的。

@Override public void enqueue(Callback responseCallback) { synchronized (this) { //线程安全,为了保证方法不能重复执行 if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); //将我们的responseCallback进行了封装,然后将最后的网络请求任务交给了Dispatcher来处理 client.dispatcher().enqueue(new AsyncCall(responseCallback)); }

我们看下一 Dispatcher 这个类的具体操作

//线程安全方法,保证多线程执行时,该方法不会有线程安全问题 synchronized void enqueue(AsyncCall call) { //如果现在正在执行的请求数小于允许的最大请求数,而且host数据 也小于允许的host数据,则执行方法 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { //将call请求放到正在执行的异步请求队列中 runningAsyncCalls.add(call); //执行call请求 executorService().execute(call); } else { //将请求放到等待队列中 readyAsyncCalls.add(call); } }

我们知道,OkHttp对于请求的处理,是可以多线程并发处理的,那么可以想到,其实最后的执行肯定是通过线程池来处理。我们看一下 executorService() 这个方法

//线程安全,防止创建多个线程池 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; }

可以看到,其实最后返回的是一个线程池,然后通过线程池的 execute() 方法来执行 AsyncCall 里面的 run() 方法。

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 { //OkHttp最神秘的地方,通过责任链模式,返回了所需要的网络请求信息 //这里说所需要的,是因为我们可以在这里进行一系列操作,包括加密解密等,来进行统一的处理后,将数据展示给我们 Response response = getResponseWithInterceptorChain(); //下面就是根据response返回的信息,来回调我们定义的Callback接口了 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 { responseCallback.onFailure(RealCall.this, e); } } finally { //执行完毕后,要进行资源的释放,队列的移除等操作 client.dispatcher().finished(this); } } } 对应的父类: 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(); }

最神秘的 getResponseWithInterceptorChain() 方法我们稍后再讲,现在我们看看最后的资源释放 finished(this) 方法

void finished(AsyncCall call) { //方法复用 finished(runningAsyncCalls, call, true); } //calls:正在执行的请求队列信息 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) { //如果Host并没有超过最大限制数,将任务从待执行队列删除,并放到正在执行队列中,然后通过线程池的execute()方法执行任务 i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } //如果正在执行的任务已经到达上限,则直接返回,否则继续从待执行队列中获取任务并放到执行队列 if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }

上面就是Okhttp的网络请求的一个整体的过程。我们还遗留了一个最重要的 getResponseWithInterceptorChain() 这个方法还没有解析。之前我分析过一篇 Mybatis Plugin插件源码分析 ,其实两个有异曲同工之妙,都通过责任链模式来实现对于拦截器的处理,只是责任链的实现方式有所区别。

Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor> interceptors = new ArrayList<>(); //获取我们自定义的拦截器信息 interceptors.addAll(client.interceptors()); //OkHttp内部使用的拦截器,包括缓存,连接等等 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); //执行责任链的proceed()方法 return chain.proceed(originalRequest); }

我们看看 proceed() 这个方法的执行。

public final class RealInterceptorChain implements Interceptor.Chain { private final List<Interceptor> interceptors;//记录了所有的拦截器 private final StreamAllocation streamAllocation;// private final HttpCodec httpCodec; private final Connection connection;//连接 private final int index;//当前拦截器所处的层数 private final Request request;//原始的request请求 private int calls; public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, Connection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); calls++; ... //生成一个新的链,链对应拦截器的层数+1, RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpCodec, connection, index + 1, request); //根据index和拦截器列表获取到当前拦截器 Interceptor interceptor = interceptors.get(index); //将新生成的链作为参数,执行当前拦截器的方法。并返回Response Response response = interceptor.intercept(next); ... //将拦截器的返回值Response作为当前了链的proceed()的返回值返回 return response; }

可能这里有些迷惑,那我们来个图来解释一下


此图来源于https://www.jianshu.com/p/cb444f49a777

每次调用拦截器的时候,都会生成一个责任链,责任链中保存了当前拦截器的pos位置信息,然后在拦截器中去调用下一个下一个责任链的方法(其实核心是拦截器中的方法intecepter),通过层层调用,最后通过 CallServerInterceptor 拦截器实现网络的请求调用。然后将返回值层层返回。
到此,我们的Okhttp的源码解析结束了。。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存