OkHttp执行流程源码分析
一、概述
先说下老生常谈的一个问题,我们为什么要学习OkHttp框架?在做技术选型的选型的时候一般看中技术的四项特点。1.是否稳定 、是否高效 2.使用是否方便。3.可扩展性是否够强
a.OkHttp已经被造出来好多年了,其应用范围非常的广泛,特别是这几年大大小小的公司都在使用,就连Google也把HttpClient替换成了OkHttp,可见其稳定性有多强。
b.OkHttp支持Http1.1和Http2.0协议。Http1.1是基于文本传输的,而Http2.0是基于二进制流传输的,二进制传输的单位是帧和流,帧组成了流,而流又有流的ID。就是因为有了流ID,所以同一个Http请求实现多个Http请求成为了可能,ps:可以通过流id标记哪个流从而定位到哪个http请求。Http2.0还支持头部gzip压缩和compress压缩后再发送,用来提升发送效率。同时客户端和服务端同时维护了一张表头表。每次传输的时候只需要传输索引的id即可实现查找id对应的表头信息,从而减少http表头数据传输的大小。http2.0还支持在没有经过客户端允许的情况下,服务端主动向客户端发送消息。
c.okhttp在数据传输时使用的okio,其是一个非常高效的io(比:比传统io和nio都要高效的多),这进一步提升了okhttp的性能。
d.okhttp使用起来非常方便,只需要创建一个OkHttpClient并newCall(request).enqueue就可以发起网络请求。如果需要使用Okhttp的一些自定义的功能可以使用OkHttpClient的Builder对所需要自定义的参数进行添加,如:可以设置写入超时时间,读取超时时间,链接超时时间以及各种拦截器等等。
e.在Okhttp中有一个类叫Dispatcher,他是OkHttp的任务队列,其中里面维护了一个线程池。当用户发起网络请求的时候会Dispatcher会首先检测是否有空闲的线程,如果有就直接使用没有就创建一个并使用。用线程池代替普通的线程可以避免线程的频繁创建和销毁所带来的系统开销,从而节省其性能。
f.OkHttp内部维护的有一个链接池,它会统一管理所有的Socket链接,当用户发起一个网络请求的时候,会首先检测连接池中是否有符合要求的链接,如果有就直接用该链接发起请求,如果没有就创建一个链接然后再发起请求。连接池的设计可以极大的减少请求的延时,从而提高整体的请求+响应的速度。
g.OkHttp底层维护的有缓存机制,程序员在开发app的时候可以根据需要来设置是否缓存网络请求的内容。如:断网的情况下走缓存,有网络的情况下走网络数据。这样在一定程度上可以给用户呈现一种更加友好的界面体验。
h.在可扩展性方面,因为OkHttp没有提供请求结果自动切换到主线程中执行的能力,所以可以使用用RxJava和Retrofit结合使用,来屏蔽直接使用Handler切换线程的逻辑复杂性。
二、使用Okhttp发起一个网络请求
使用OkHttp发起一个网络请求非常的简单,只需要三部即可完成,例如:我们发起一个get请求只需要:1.创建一个OkHttpClient对象。2.new一个Call并把构建的Request对象传进去。3.使用call.enqueue(callback)发起网络调用即可。使用post请求方式发起网络请和get请求非常的类似,不再赘述,直接贴代码:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | private void requestOkHttp() { //创建OKHttpClient OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder() .url( "http://www.jinzhibro.com/" ).get().build(); Call call = okHttpClient.newCall(request); call.enqueue( new Callback() { //请求失败 @Override public void onFailure(Call call, IOException e) { } //请求成功 @Override public void onResponse(Call call, Response response) throws IOException { } }); //创建一个RequestBody传给Request的builder对象,并最终赋值给Request的post方法。 RequestBody requestBody = MultipartBody.create(MediaType.parse( "application/json; charset=utf-8" ), "json" ); Request request1 = new Request.Builder() .url( "http://www.jinzhibro.com/" )//请求主路径 .post(requestBody) //设置请求方式为post .build(); //创建一个request对象 Call call1 = okHttpClient.newCall(request1); call1.enqueue( new Callback() { //网络请求失败 @Override public void onFailure(Call call, IOException e) { } //网络请求成功 @Override public void onResponse(Call call, Response response) throws IOException { } }); } |
以上就是OKHttp的简单实用过程,当然上面的构建并不是唯一的,例如:还可以通过OkHttpClient的Builder.build()进行构建。这种构建方式可以方便的添加往外的参数。如:超时、拦截器等。
三、执行流程分析
1.OkHttpClient okHttpClient = new OkHttpClient();我们先来看下这段代码都干了写什么事情。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | //构造函数<br>public OkHttpClient() {<br> //创建一个Builder对象 this ( new Builder()); } OkHttpClient(Builder builder) {<br> //任务队列,里面维护了一个线程池,通过其execute方法来调度任务 this .dispatcher = builder.dispatcher; this .proxy = builder.proxy;<br> //OkHttp支持的协议,有Http1.1和Http2.0 this .protocols = builder.protocols; this .connectionSpecs = builder.connectionSpecs;<br> //连接器集合 this .interceptors = Util.immutableList(builder.interceptors);<br> //网络连接器集合 this .networkInterceptors = Util.immutableList(builder.networkInterceptors); this .eventListenerFactory = builder.eventListenerFactory; this .proxySelector = builder.proxySelector;<br> //cookie相关 this .cookieJar = builder.cookieJar; this .cache = builder.cache;<br> //缓存相关 this .internalCache = builder.internalCache;<br> //socket工厂,用来生产socket发起网络请求 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;<br> //权限校验 this .authenticator = builder.authenticator;<br> //连接池 this .connectionPool = builder.connectionPool;<br> //dns域名解析 this .dns = builder.dns; this .followSslRedirects = builder.followSslRedirects; this .followRedirects = builder.followRedirects; this .retryOnConnectionFailure = builder.retryOnConnectionFailure;<br> //各种超时时间的设置 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); } } |
根据以上的代码可知我们的new OkHttpClient最主要的是创建这个对象并做一些参数的初始化工作。
2.我们看下第二段代码,构建一个Request对象。
1 2 | Request request = new Request.Builder() .url( "http://www.jinzhibro.com/" ).get().build(); |
从上面的结构我们可以看出Request使用的是建造者模式,用来初始化我们的请求主机地址url、请求方法get、post等,请求头headers,请求实体requestBody,总体上来讲就是做初始化的。我们通过下面的代码就可以看出来,非常简单就不做过多的赘述,我们本节主要关注其执行流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public final class Request {<br> //请求地址 final HttpUrl url;<br> //请求方法 final String method;<br> //请求头部 final Headers headers;<br> //请求实体 final @Nullable RequestBody body; final Map<Class<?>, Object> tags; private volatile @Nullable CacheControl cacheControl; // Lazily initialized. //把builder中构建的参数一个个传递给Request Request(Builder builder) { this .url = builder.url; this .method = builder.method; this .headers = builder.headers.build(); this .body = builder.body; this .tags = Util.immutableMap(builder.tags); } |
3.接下来看下Call的创建过程
1 | Call call = okHttpClient.newCall(request); |
我们先来看下okHttpClient.newCall(request);是如何创建Call的。
1 2 3 | @Override public Call newCall(Request request) { return RealCall.newRealCall( this , request, false /* for web socket */ ); } |
原来创建Call的方法是通过RealCall.newRealCall(okHttpclient,request,false) 来创建的,其中RealCall实现了Call接口,我们看下Call和RealCall的源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public interface Call extends Cloneable { Request request(); //同步网络请求方法 Response execute() throws IOException; //异步网络请求方法 void enqueue(Callback responseCallback); void cancel(); boolean isExecuted(); boolean isCanceled(); Timeout timeout(); Call clone(); //Call工厂,通过newCall生成一个工厂,我们在OkHttpClient之所以能够调用newCall(request)方法就是因为,OkHttpClient实现了Call.Factory接口,并实现了其newCall方法<br> interface Factory { Call newCall(Request request); } } |
OkHttpClient.newCall
1 2 3 4 5 6 | static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { // 实例化一个RealCall RealCall call = new RealCall(client, originalRequest, forWebSocket); call.eventListener = client.eventListenerFactory().create(call); return call; } |
通过以上代码可以看出其就做了一件事情就是实实例化一个RealCall并返回。到此Call的创建就结束了。
4.下面看下Call的.enqueue(callback)的异步执行流程是怎样的。
实例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 | call.enqueue( new Callback() { //请求失败 @Override public void onFailure(Call call, IOException e) { } //请求成功 @Override public void onResponse(Call call, Response response) throws IOException { } }); |
我们进入到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 );<br> //client指OkHttpClient,client.dispatcher就是前面我们提到过的任务调度器,里面维护了一个线程池<br> //AsyncCall是RealCall的一个内部类,其继承了NamedRunnable,NamedRunnable又继承了Runnable接口 <br> client.dispatcher().enqueue( new AsyncCall(responseCallback)); } |
我们先来看下dispatcher这个类,并且看下他的enqueue是如何执行的
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 26 27 28 29 | public final class Dispatcher {<br> //最大请求数 private int maxRequests = 64 ;<br> //最多配置可以配置的主机地址数 private int maxRequestsPerHost = 5 ; private @Nullable Runnable idleCallback; //线程池 private @Nullable ExecutorService executorService; //准备异步执行的任务队列 private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); //正在执行的异步任务队列 private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); //正在执行的同步队列 private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); public synchronized ExecutorService executorService() {<br> //如果没有配置线程池就默认配置一个,通过ThreadPoolExecutor,需要指出的是java提供的四大线程池都是通过ThreadPoolThread来直接或者间接配置的。 if (executorService == null ) {<br> //这里解释一下里面的参数:0代表核心线程数,这里指没有和新线程,Integer.MAX_VALUE代表线程池所能容纳的最大线程数,这里配置了一个极大值,代表线程可以容纳非常多的线程,TimeUnit.SECONDS非<br>//核心线程的闲置超时时间,new SynchromousQueue代表线程池中的任务队列,通过线程池的execute提交的Runnable对象会存储在这个参数中,Util.theradFactory代表线程工厂,提供创建新线程的能力<br>//所以我们通过ThreadPoolExecutor可以配置任意类型的线程池 executorService = new ThreadPoolExecutor( 0 , Integer.MAX_VALUE, 60 , TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory( "OkHttp Dispatcher" , false )); } return executorService; } //这个方法就是线面我们调用的dispatcher.enqueue(AsyncAll call)方法 void enqueue(AsyncCall call) { synchronized ( this ) {<br> //先把AsyncCall加入准备执行的异步队列 readyAsyncCalls.add(call); }<br> //然后执行promoteAndExecute()方法,从准备执行的任务队列中取出AsyncCall,并将其封装在List<AsyncCall> executableCalls中,然后循环遍历executableCalls取出AsyncCall并执行AsyncCall的<br>//executeOn(executeService)方法。 promoteAndExecute(); } private boolean promoteAndExecute() {<br> assert (!Thread.holdsLock( this ));<br><br> List<AsyncCall> executableCalls = new ArrayList<>();<br> boolean isRunning;<br> synchronized ( this ) {<br> for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {<br> AsyncCall asyncCall = i.next();<br><br> if (runningAsyncCalls.size() >= maxRequests) break ; // Max capacity.<br> if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.<br><br> i.remove();<br> executableCalls.add(asyncCall);<br> runningAsyncCalls.add(asyncCall);<br> }<br> isRunning = runningCallsCount() > 0;<br> }<br><br> for (int i = 0, size = executableCalls.size(); i < size; i++) {<br> AsyncCall asyncCall = executableCalls.get(i);<br> asyncCall.executeOn(executorService());<br> }<br><br> return isRunning;<br>}} |
通过上面的代码我们总结下来:dispatcher通过enqueue方法把AsyncCall加入readyAsyncCalls队列,然后通过执行promoteAndExecute方法从readyAsyncCall中取出AsyncCall并将其放入runningAsyncCall队列和executableCalls集合,然后循环遍历executableCalls集合从中取出AsyncCall并执行AsyncCall的executeOn方法,并把线程池传递进去。
接下来看下execueOn(executeService)都干了些啥事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | void executeOn(ExecutorService executorService) { assert (!Thread.holdsLock(client.dispatcher())); boolean success = false ; try {<br> //把AsyncCall放入线程池,并执行其run方法 executorService.execute( this ); success = true ; } catch (RejectedExecutionException e) { InterruptedIOException ioException = new InterruptedIOException( "executor rejected" ); ioException.initCause(e); eventListener.callFailed(RealCall. this , ioException); responseCallback.onFailure(RealCall. this , ioException); } finally { if (!success) { client.dispatcher().finished( this ); // This call is no longer running! } } } |
在AsyncCall的executeOn方法中就一行核心代码,就是executorService.execute(this),其中this就代表AsyncCall,其整体含义是吧AsyncCall放入线程池中执行。在执行时会调用AsyncCall的run方法。由于AsyncCall继承了NamedRunnable抽象类,并且实现了Runnable方法,在NamedRunnable类中的run方法会调用其自身的execute方法,所以run方法执行后会紧接着调用NamedRunnable方法的execute方法。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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(); } |
所以此时的逻辑就切换到了NamedRunnable类的execue()方法中了。又由于NamedRunnable是一个抽象类其实现类是AsyncCall,所以只需要看AsyncCall中的execute方法就行了
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 26 | @Override protected void execute() { boolean signalledCallback = false ; timeout.enter(); try {<br> //通过getResponseWithInterceptorChain()方法获取请求结果返回的response Response response = getResponseWithInterceptorChain(); if (retryAndFollowUpInterceptor.isCanceled()) { signalledCallback = true ;<br> //网络请求获取失败 responseCallback.onFailure(RealCall. this , new IOException( "Canceled" )); } else { signalledCallback = true ;<br> //成功获取网络请求的结果,这里的responseCallback指的就是我们在调用call.enqueue时传入的CallBack对象。 responseCallback.onResponse(RealCall. this , response); } } catch (IOException e) { e = timeoutExit(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 ); } } } |
代码分析到这里整个执行流程就结算了,以为我们从HttpClient的创建到创建一个Call,并通过Call.enqueue发起网络请求并成功回调到了网络请求的返回结果。
再总结一下这个流程:
1.通过new HttpClient创建一个对象。
2.通过okHttpClient创建一个Call,实际上是new RealCall
3.通过new RealCall().enqueue(callback)泛起网络请求,上面两步都非常的简单,最主要的第三步,我们拆分开来看
a.RealCall.enqueue(callback)实际上就做了一件事,其调用dispatcher的enqueue(new AsyncCall(callback))
b.在dispatcher的enqueue方法内部会把AsyncCall加入到任务队列readAsyncCalls中,然后执行dispatcher的promoteAndExecute()方法从readyAsyncCalls队列中取出AsyncCall并把AsyncCall重新加入到runningAsyncCalls队列和executableCalls集合。然后遍历executableCalls集合取出AsyncCall并执行AsyncCall的executeOn方法。并把线程池当参数传进去
c.AsyncCall的executeOn方法会调用dispatcher的线程池executeService.execute(this)把AsyncCall放入线程池中执行。
d.线程池最终会执行AsyncCall的run方法,由于AsyncCall集成了NamedRunnable抽象类,NamedRunnable实现了Runnable接口,并定义了execute抽象方法,并在其自身的run方法中执行了execute这个抽象方法。因为execute这个抽象方法的实现类是AsyncCall的execute抽象方法,所以最终会执行AsyncCall的execute方法。
e.在AsyncCall的execute方法中会调用AsyncCall的getResponseWithInterceptorChain()方法,返回网络请求的执行结果Response。并调用Call.enqueue(callback)设置的callback设置callback.onResponse(response)来完成最终的回调。
虽然以上五步已经算是把OkHttp的执行流程分析完了,但是总觉得还是缺少了点啥。对了,我们在整个执行流程中并没有发现网络请求和响应结果的具体步骤。下面就来分析这一块的内容的具体实现步骤,这里面会涉及到各种拦截器组中的责任链。
OkHttp如何实现网络请求以及如何响应结果?
具体分析:通过上面的分析我们知道执行AsyncCall的getResponseWithInterceptorChain()方法就能获取到一个Response对象。我们主要看getResponseWithInterceptorChain()方法的逻辑就行
下面贴出这部分的源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Response getResponseWithInterceptorChain() throws IOException { // 拦截器集合 List<Interceptor> interceptors = new ArrayList<>();<br> //应用拦截器,就是我们自己设置的拦截器 interceptors.addAll(client.interceptors());<br> //在网络请求失败后重试或者重定向后发起玩过请求的拦截器 interceptors.add(retryAndFollowUpInterceptor);<br> //对网络请求数据和响应数据进行解析组装的拦截器 interceptors.add( new BridgeInterceptor(client.cookieJar()));<br> //缓存拦截器,创建,写入更新缓存等 interceptors.add( new CacheInterceptor(client.internalCache()));<br> //客户端连接服务端的拦截器,为请求找到合适的链接,复用已有链接或者重新创建链接,由连接池所决定的。 interceptors.add( new ConnectInterceptor(client)); if (!forWebSocket) {<br> //网络拦截器,可以在这里做一些日志输出啥的工作 interceptors.addAll(client.networkInterceptors()); }<br> //向服务器发起真正请求的拦截器 interceptors.add( new CallServerInterceptor(forWebSocket)); //拦截链和具体的网络请求响应都在这里执行 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null , null , null , 0 , originalRequest, this , eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); //执行返回结果,即Response return chain.proceed(originalRequest); } |
以上代码的主要功能是配置拦截器,发起网络调用,并返回网络调用结果,我们通过RealInterceptorChain来看看拦截链是如何起作用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call, EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {<br> //拦截器结合 this .interceptors = interceptors;<br> //连接池 this .connection = connection;<br> //实际的物理socket请求 this .streamAllocation = streamAllocation; this .httpCodec = httpCodec; this .index = index;<br> //请求参数对象 this .request = request;<br> //请求的call,本节指的是AsyncCall对象 this .call = call; this .eventListener = eventListener;<br> //链接超时 this .connectTimeout = connectTimeout;<br> //读取超时 this .readTimeout = readTimeout;<br> //写入超时 this .writeTimeout = writeTimeout; } |
RealInterceptorChain.proceed(originalRequest)源码
1 2 3 4 5 6 7 8 9 10 11 12 13 | public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); calls++; // 此处会执行调用链 RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1 , request, call, eventListener, connectTimeout, readTimeout, writeTimeout);<br> //开始执行是初始换第一个Interceptor Interceptor interceptor = interceptors.get(index);<br> //执行调用链,返回实际的请求结果 Response response = interceptor.intercept(next); //返回请求结果 return response; } |
上面的一段代码是RealInterceptorChain的核心代码,其会循环执行连接器,直到把集合中的拦截器执行完毕后会发起网络请求并最终返回请求结果。
其执行过程是这样的:
1.在RealInterceptorChain中实例化自身,并初始化标记位Index。
2.调用根据index的索引获取Interceptor,并执行Interceptor.intercept(next),把RealInterceptorChain传进去
3.在intercept方法内部会调用chain.proceed(requestBuilder.build()),而这个chain就是刚刚传递进来的RealInterceptorChain。所以执行chain.proceed其实就是执行RealInterceptorChain.proceed。所以调用关系又会到了RealInterceptorChain中,此时index++会执行第二个拦截器中的代码,直到所有的拦截器都执行完成。
上面的三步就是拦截器调用链的执行过程,我们看下拦截器的intercept方法都干了些啥
@Override public Response intercept(Chain chain) throws IOException {
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <em id= "__mceDel" > //获取请求的request对象 Request userRequest = chain.request();<br> //构建一个Builder Request.Builder requestBuilder = userRequest.newBuilder(); //获取请求实体 RequestBody body = userRequest.body(); if (body != null ) {<br> //获取实体类型 MediaType contentType = body.contentType(); if (contentType != null ) { requestBuilder.header( "Content-Type" , contentType.toString()); } long contentLength = body.contentLength(); if (contentLength != - 1 ) {<br> //添加头部信息 requestBuilder.header( "Content-Length" , Long.toString(contentLength)); requestBuilder.removeHeader( "Transfer-Encoding" ); } else { requestBuilder.header( "Transfer-Encoding" , "chunked" ); requestBuilder.removeHeader( "Content-Length" ); } } //添加头部信息 if (userRequest.header( "Host" ) == null ) { requestBuilder.header( "Host" , hostHeader(userRequest.url(), false )); } if (userRequest.header( "Connection" ) == null ) { requestBuilder.header( "Connection" , "Keep-Alive" ); } boolean transparentGzip = false ; if (userRequest.header( "Accept-Encoding" ) == null && userRequest.header( "Range" ) == null ) { transparentGzip = true ;<br> //设置头部参数 requestBuilder.header( "Accept-Encoding" , "gzip" ); } //设置cookie List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url()); if (!cookies.isEmpty()) { requestBuilder.header( "Cookie" , cookieHeader(cookies)); } if (userRequest.header( "User-Agent" ) == null ) { requestBuilder.header( "User-Agent" , Version.userAgent()); } //循环执行RealInterceptorChain.proceed并传入构建参数,一个个的调用拦截器,一个个的构建Request,最终会把所有的拦截器执行一遍,并把所有拦截器携带的参数方法Request中,<br>//最终会通过CallServerInterceptor发起这正的网络请求<br> Response networkResponse = chain.proceed(requestBuilder.build()); HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); Response.Builder responseBuilder = networkResponse.newBuilder() .request(userRequest); if (transparentGzip && "gzip" .equalsIgnoreCase(networkResponse.header( "Content-Encoding" )) && HttpHeaders.hasBody(networkResponse)) {<br> //这部分是okio相关的内容,相当于传统io的inputStream和outputstream GzipSource responseBody = new GzipSource(networkResponse.body().source()); Headers strippedHeaders = networkResponse.headers().newBuilder() .removeAll( "Content-Encoding" ) .removeAll( "Content-Length" ) .build(); responseBuilder.headers(strippedHeaders); String contentType = networkResponse.header( "Content-Type" ); responseBuilder.body( new RealResponseBody(contentType, -1L, Okio.buffer(responseBody))); } return responseBuilder.build(); } </em> |
通过以上代码我们知道RealInterceptorChain.proceed循环的执行拦截器并最终通过chain.proceed(request)返回响应结果,
那么具体的调用是如何实现的呢?其实是在上面我们提到过的CallServerInterceptor的intercept方法中执行的,我们看下其具体的实现
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | @Override public Response intercept(Chain chain) throws IOException {<br> //获取到RealInterceptorChain对象并从中获取到StreamAllocation和HttpCodec RealInterceptorChain realChain = (RealInterceptorChain) chain; HttpCodec httpCodec = realChain.httpStream(); StreamAllocation streamAllocation = realChain.streamAllocation();<br> //获取一个Socket链接 RealConnection connection = (RealConnection) realChain.connection();<br> //获取请求参数,包括头部和实体 Request request = realChain.request(); ....省略了不关紧的代码<br> //初始化一个ResponseBuilder用来存储builder信息 Response.Builder responseBuilder = null ; if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null ) { if ( "100-continue" .equalsIgnoreCase(request.header( "Expect" ))) { httpCodec.flushRequest(); realChain.eventListener().responseHeadersStart(realChain.call()); responseBuilder = httpCodec.readResponseHeaders( true ); } if (responseBuilder == null ) { realChain.eventListener().requestBodyStart(realChain.call()); long contentLength = request.body().contentLength(); CountingSink requestBodyOut = new CountingSink(httpCodec.createRequestBody(request, contentLength));<br> //次数是okio的实现,BufferedSink相当于OutputStream.write。下面这段意思是就是真正发起网络调用的,通过流写入 BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); realChain.eventListener() .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount); } else if (!connection.isMultiplexed()) { streamAllocation.noNewStreams(); } } httpCodec.finishRequest(); //结束请求等待响应 if (responseBuilder == null ) { realChain.eventListener().responseHeadersStart(realChain.call()); responseBuilder = httpCodec.readResponseHeaders( false ); } Response response = responseBuilder .request(request) .handshake(streamAllocation.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); int code = response.code(); if (code == 100 ) { responseBuilder = httpCodec.readResponseHeaders( false ); response = responseBuilder .request(request) .handshake(streamAllocation.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); code = response.code(); } realChain.eventListener() .responseHeadersEnd(realChain.call(), 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(httpCodec.openResponseBody(response)) .build(); } return response; } |
由上面的源码可以看出发起真正网络请求和响应网络请求结果的地方确实是在CallServerInterceptor.intercept中,通过okio将流写入和读取网络数据。
到了这里网络请求和响应的也分析完成了。
总结一下吧:
1.AsyncCall会调用其自身的getResponseWithInterceptorChain()方法来获取网络的响应结果。在getResponseWithInterceptorChain()方法内部会初始化各种拦截器,如:自定义拦截器还有框架内置的拦截器(CallServerInterceptor、BridgeInterceptor、NetWorkInterceptor、CacheInterceptor、RetryAndFlowUpInterceptor、ConnectInterceptor)。并最终把拦截器集合赋值给RealInterceptorChain,并调用其proceed方法返回response对象。
2.在RealInterceptor内部会调用proceed方法,其会实例化自身并根据index标记位获取拦截器,并执行interceptor.proceed(next)方法,next代表RealInterceptorChain。而在拦截器的intercept方法中又会执行RealInterceptorChain的proceed方法。如此循环往复把调用拦截器的调用链执行完成
3.在拦截链中,ConnectInterceptor会建立鱼服务端的链接,并通过CallServerInterceptor发起真正的请求,并根据请求返回请求响应的结果,即Response。
4.请求响应调用过程结束。
截止到这里,Okhttp的网络请求的执行流程的大框架算是分析完了,当然还有一些细节的问题,如:连接池、缓存、Socket、Http和Https、复用链接会在下一节集中说下。
重要的事情要多说几遍,我们再把OkHttp的执行流程通过流程图画一下并贴出来。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探