大家都已经对okhttp很熟悉了吧,今天我把我学习okhttp源码的过程记录一下. okhttp是一个轻量级的网络请求框架,可用于java和android,支持同步请求和异步请求两种方式.
同步GET请求源码分析 1 2 3 4 5 6 7 8 9 10 OkhttpClient client = new OkhttpClient.Builder().build(); Request request = new Request.Builder().url('http://www.baidu.com' ).build(); new Thread( public void run () { Response response = client.newCall(request).execute(); } ).start();
以上是简单的OkHttp同步GET请求方式,接下来咱们来分析一下源码.
1.实例化OkHttpClient 首先需要获取一个OkHttpClient的实例,可以通过直接new,也可通过new OkHttpClient内部的静态内部类Builder,通过调用build方法获取.直接new OkHttpClient对象,其实也是通过new OkHttpClient.Builder()
1 2 3 public OkHttpClient () { this (new Builder()); }
OkHttp.Builder属于建造者模式,这样写的好处可以使代码写的更优雅一些,链式配置属性是不是很舒服.
可以看到在new OkHttpClient.Builder()时,创建了Dispatcher对象和ConnectionPool对象,Dispatcher主要是用来网络分发的(用来处理同步请求线程和异步请求线程),ConnectionPool是用来http请求的连接池.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public Builder () { dispatcher = new Dispatcher(); protocols = DEFAULT_PROTOCOLS; connectionSpecs = DEFAULT_CONNECTION_SPECS; eventListenerFactory = EventListener.factory(EventListener.NONE); proxySelector = ProxySelector.getDefault(); 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 ; connectTimeout = 10_000 ; readTimeout = 10_000 ; writeTimeout = 10_000 ; pingInterval = 0 ; }
在Dispatcher中维护着一个用来管理异步请求线程的线程池,ConnectionPool中也维护着一个连接http请求的线程池,这也是为什么我们在网络请求中只用一个OkHttpClient的原因,或者用单例封装起来.如果创建多个OkHttpClient对象,那么DisPatcher和ConnectionPool的线程池也会多次创建,这样会消耗CPU资源和内存资源.
这是Dispatcher的线程池
1 2 3 4 5 6 7 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; }
ConnectionPool的线程池
1 private static final Executor executor = new ThreadPoolExecutor(0 ,Integer.MAX_VALUE , 60L , TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool" , true ));
2.创建Request,调用OkHttpClient的newCall方法 根据不同的http请求方式创建不同的Request对象,这个我们先不研究,后面再说.先看创建完Request对象后,调用client.newCall(request)的源码.
1 2 3 4 5 6 @Override public Call newCall (Request request) { return RealCall.newRealCall(this , request, false ); }
我们看到client.newCall(request)方法实现中,其实是调用了RealCall对象的newRealCall的静态方法,那么我们接下来看看newRealCall方法里面是进行了什么操作.
1 2 3 4 5 6 static RealCall newRealCall (OkHttpClient client, Request originalRequest, boolean forWebSocket) { RealCall call = new RealCall(client, originalRequest, forWebSocket); call.eventListener = client.eventListenerFactory().create(call); return call; }
RealCall中的newRealCall方法中实际上就是new了一个RealCall实例,并且创建了一个网络请求监听事件的Listener.接下来,我们将要分析的是同步请求,RealCall的execute方法.
3.同步请求execute方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public Response execute () throws IOException { synchronized (this ) { if (executed) throw new IllegalStateException("Already Executed" ); executed = true ; } captureCallStackTrace(); eventListener.callStart(this ); try { client.dispatcher().executed(this ); Response result = getResponseWithInterceptorChain(); if (result == null ) throw new IOException("Canceled" ); return result; } catch (IOException e) { eventListener.callFailed(this , e); throw e; } finally { client.dispatcher().finished(this ); } }
可见execute方法最后返回一个Response对象.咱们捡关键的先看:client.dispatcher().executed(this);Response result = getResponseWithInterceptorChain();这两个方法是实现的关键部分.先分析第一个方法,client.dispatcher().executed(this)是调用了Dispatcher对象中的executed方法,那我们再看看它的实现.
1 2 3 4 synchronized void executed (RealCall call) { runningSyncCalls.add(call); }
可见executed方法只是将该请求加入到了一个同步的队列中.
1 2 3 4 5 6 7 8 9 10 11 private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
这是Dispatcher中的三个请求队列:runningSyncCalls是个同步的请求队列,runningAsyncCalls是在运行当中的异步请求队列,readyAsyncCalls是在等待中的异步请求队列.(异步请求是有最大请求数的,后面说)
接下来我们看看OkHttp中网络请求核心部分getResponseWithInterceptorChain().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Response getResponseWithInterceptorChain () throws IOException { 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); }
可见该方法是创建了一个拦截器队列,使用了责任链模式来一层一层的对Request和Response进行操作,最终返回一个Response对象. 这里是OkHttp责任链模式的简写方式:责任链模式
以上就是OkHttp同步网络请求的大致流程.
OkHttp异步网络请求 异步请求和同步请求的最大区别是:同步请求需要自己创建线程管理线程,而异步请求则是用OkHttp Dispatcher中的线程进行请求.client.enqueue(request)实际上是调用的RealCall的enqueue方法,我们看看RealCall的enqueue.
1 2 3 4 5 6 7 8 9 @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)); }
可见RealCall的enqueue方法实际调用的是Dispatcher的enqueue方法,我们继续看Dispatcher的enqueue方法.
1 2 3 4 5 6 7 8 9 synchronized void enqueue (AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
如果运行异步队列 小于maxRequests and 异步请求的host的个数小于maxRequestsPerHost,则将该次请求加入到运行异步队列 然后运行该线程;否则,将该请求队列加入到待请求的异步队列 .AysncCall这是一个定制的线程,接下来看看该线程的run方法怎么执行的.
1 final class AsyncCall extends NamedRunnable
看到AsynCall实际继承的是NamedRunnable,NamedRunnable是个抽象类,我们再看看NamedRunnable的run方法
1 2 3 4 5 6 7 8 9 10 11 @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 () ;
看到execute这个方法是个钩子方法,execute方法在run方法中被调用,execute是个抽象方法,子类必须实现该方法,因此,我们看看AysnCall的execute方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @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) { 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 ); } }
看到AysnCall中execute方法,调用了与同步请求的相同的getResponseWithInterceptorChain的方法,getResponseWithInterceptorChain是具体处理网络请求返回Response的方法.在获取到reseponse后,finally中又调用了Dispatcher的finished方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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(); } }
finished方法主要是将执行完后的线程从运行请求队列 中移除,promoteCalls为true表示执行下一个请求,promoteCalls方法被调用.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void promoteCalls () { if (runningAsyncCalls.size() >= maxRequests) return ; if (readyAsyncCalls.isEmpty()) return ; 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 ; } }
promoteCalls方法是将待请求队列 中的线程移动到运行中队列 .
OKHttp Intercept 这里是对okhttp拦截器的总结 |拦截器|作用| |—-|—-| |client.interceptors()|开发者自定义的拦截器| |retryAndFollowUpInterceptor|失败重定向拦截器(内部实现还未研究明白)| |BridgeInterceptor|主要用作生成或者解析http的报文格式| |CacheInterceptor|响应体的缓存| |ConnectInterceptor|连接拦截器| |client.networkInterceptors()|开发者自定的网络拦截器| |CallServerInterceptor|请求服务器的拦截器|