本文共 10053 字,大约阅读时间需要 33 分钟。
类名 | 描述 |
---|---|
OkHttpClient | OkHttp请求客户端,Builder模式实现 |
Dispatcher | 本质是异步请求的调度器,负责调度异步请求的执行,控制最大请求并发数和单个主机的最大并发数,并持有有一个线程池负责执行异步请求,对同步请求只是作统计操作。 |
Request | 封装网络请求,就是构建请求参数(如url,header,请求方式,请求参数),Builder模式实现 |
Response | 网络请求对应的响应,Builder模式实现,真正的Response是通过RealCall.getResponseWithInterceptorChain()方法获取的。 |
Call | 是根据Request生成的一个具体的请求实例,且一个Call只能被执行一次。 |
ConnectionPool | 连接池 |
Interceptor | Interceptor可以说是OkHttp的核心功能,它就是通过Interceptor来完成监控管理,重写和重试请求的。 |
Cache | 可以自定义是否采用缓存,缓存形式是磁盘缓存,DiskLruCache。 |
OkHttp3,网络请求库,同步请求RealCall.execute()和异步请求RealCall.enqueue(),请求任务都是交给Dispatcher调度请求任务的处理,请求通过一条拦截链,每一个拦截器处理一部分工作,最后一个拦截器,完成获取请求任务的响应,会将响应沿着拦截链向上传递。
初始化一个OkHttpClient对象
OkHttpClient mOkHttpClient = new OkHttpClient();
public OkHttpClient() { this(new Builder()); }
Builder是OkHttpClient的一个内部类,目的是构造和封装数据
public Builder() { dispatcher = new Dispatcher(); protocols = DEFAULT_PROTOCOLS; connectionSpecs = DEFAULT_CONNECTION_SPECS; 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; }
创建一个请求
Request类是HTTP请求,它携带了请求地址、请求方法、请求头部、请求体以及其他信息。它也是通过Builder模式创建的。
Request request = new Request.Builder() .url(url) .build();
public Request build() { if (url == null) throw new IllegalStateException("url == null"); return new Request(this); }
private Request(Builder builder) { this.url = builder.url; this.method = builder.method; this.headers = builder.headers.build(); this.body = builder.body; this.tag = builder.tag != null ? builder.tag : this; }
接下来对应的就是请求,请求分为异步与同步,先看同步
Response response = mOkHttpClient.newCall(request).execute();
Response是HTTP响应,它继承自Closeable(Closeable继承自AutoCloseable,AutoCloseable资源自动关闭的接口),它携带了请求、网络请求协议、返回状态码、请求头、响应体等等,其中,网络请求协议是Protocol,Protocol是一个枚举类型,包含4中类型
public enum Protocol { HTTP_1_0("http/1.0"), HTTP_1_1("http/1.1"), SPDY_3("spdy/3.1"), HTTP_2("h2"); }
Call类是一个抽象类
OkHttpClient.newCall(request); @Override public Call newCall(Request request) { return new RealCall(this, request); //RealCall extends Call}protected RealCall(OkHttpClient client, Request originalRequest) { this.client = client; this.originalRequest = originalRequest; this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client); }
public interface Call { Request request(); Response execute() throws IOException; void enqueue(Callback responseCallback); void cancel(); boolean isExecuted(); boolean isCanceled(); interface Factory { Call newCall(Request request); } }
OKHttp提供了execute()方法(同步方法)和enqueue()方法(异步方法),下面我们先看看execute()方法(同步方法)
execute()方法,首先判断是否已执行过,如果已经执行过,则抛出异常信息,也就是说一次Call实例只能调用一次execute()方法。没有执行则通过分发器进行执行
@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); }
分发器将请求加入到队列
... private final DequerunningSyncCalls = new ArrayDeque<>();//双端队列 ... /** Used by {@code Call#execute} to signal it is in-flight. */ synchronized void executed(RealCall call) { runningSyncCalls.add(call); }
最后通过分发器的finished()方法结束请求
void finished(RealCall call) { finished(runningSyncCalls, call, false); } privatevoid finished(Deque 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(); } }
所以通过上面信息可知真正发送请求的地方是getResponseWithInterceptorChain()方法
组装各种拦截器,然后发送请求
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException { Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket); return chain.proceed(originalRequest);}
拦截器接口
public interface Interceptor { Response intercept(Chain chain) throws IOException; interface Chain { Request request(); Response proceed(Request request) throws IOException; Connection connection(); }}
在这个责任链模式中执行每一个连接器的proceed方法,然后我们逐个分析这些连接器
这里是责任链模式,由用户主动调用此方法,因此需要实现拦截的功能
这里如果有Intercepter实现已经添加,则为每一个拦截器创建一个ApplicationInterceptorChain然后递归直到将所有的链调用完成最后进入getResponse方法中。
@Override public Response proceed(Request request) throws IOException { //If there's another interceptor in the chain, call that. if (index < client.interceptors().size()) { //新建一个环节,并且将索引+1,这里的request可能不再是originalRequest了,因为在拦截器中可能被修改了 Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket); //获取到对应的拦截器 Interceptor interceptor = client.interceptors().get(index); //执行拦截器的intercept方法,参数是上面新建的环节,这个方法里面会调用chain.proceed(),递归了。 //在最深的一层调用getResponse之后,响应会一层层的往外传 Response interceptedResponse = interceptor.intercept(chain); if (interceptedResponse == null) { throw new NullPointerException("application interceptor " + interceptor + " returned null"); } // 返回这一层拦截器处理用的响应 return interceptedResponse; } //递归到了最深的一层,拦截器都进入过了,发送httpRequest,获取到response. return getResponse(request, forWebSocket);}
当执行完getResponse完成之后就一步步返回。
真正请求数据的方法
Response getResponse(Request request, boolean forWebSocket) throws IOException { // 复制请求头,并设置适合的属性。如果长度不为-1,则设置Content-Length,否则使用chunked方式传输。 // 如果对chunked不熟悉,请参考其他资料 RequestBody body = request.body(); if (body != null) { // 根据传进来的request创建新的Builder. Request.Builder requestBuilder = request.newBuilder(); // 设置Content-Type MediaType contentType = body.contentType(); if (contentType != null) { requestBuilder.header("Content-Type", contentType.toString()); } // 判断使用何种方式传输 long contentLength = body.contentLength(); // body长度不为-1,设置Content-Length if (contentLength != -1) { requestBuilder.header("Content-Length", Long.toString(contentLength)); requestBuilder.removeHeader("Transfer-Encoding"); } else { // body 长度为 -1 ,使用chunked传输 requestBuilder.header("Transfer-Encoding", "chunked"); requestBuilder.removeHeader("Content-Length"); } //创建新请求 request = requestBuilder.build(); } // 创建一个新的引擎,每个引擎代表一次请求/响应对 engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null); int followUpCount = 0; //重试次数 //死循环 出口: // 1. 请求被取消 // 2. 请求有问题 // 3. 捕获到异常,且尝试恢复失败 // 4. 获取到响应,且无需重定向 // 5. 重定向次数超过最大限制 while (true) { // 被取消的情况 if (canceled) { engine.releaseStreamAllocation(); throw new IOException("Canceled"); } boolean releaseConnection = true; try { // 发送请求 engine.sendRequest(); // 读取响应 engine.readResponse(); releaseConnection = false; } catch (RequestException e) { // 请求失败,请求本身有问题,或者是网络不通 throw e.getCause(); } catch (RouteException e) { // 连接到服务器的路由发生异常,请求还没被发送 // 通过上一次连接异常恢复引擎 HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null); // 如果恢复成功,将当前的引擎设置为这个恢复好的引擎 if (retryEngine != null) { releaseConnection = false; engine = retryEngine; continue; } // 没法恢复,抛出异常 throw e.getLastConnectException(); } catch (IOException e) { // 与服务器交互失败,这时,请求可能已经被发送 // 恢复引擎 HttpEngine retryEngine = engine.recover(e, null); //如果恢复成功,将当前的引擎设置为这个恢复好的引擎 if (retryEngine != null) { releaseConnection = false; engine = retryEngine; continue; } // 没法恢复,抛出异常 throw e; } finally { // 如果需要释放连接,则将连接释放 if (releaseConnection) { StreamAllocation streamAllocation = engine.close(); streamAllocation.release(); } } // 获取响应 Response response = engine.getResponse(); // 下一步的请求,如果存在,则需要重定向 Request followUp = engine.followUpRequest(); //如果不需要重定向 if (followUp == null) { if (!forWebSocket) { // 释放连接 engine.releaseStreamAllocation(); } //返回响应 return response; } // 如果需要重定向,关闭当前引擎 StreamAllocation streamAllocation = engine.close(); // 如果超过最大数,释放连接,并抛出异常 if (++followUpCount > MAX_FOLLOW_UPS) { // 释放连接 streamAllocation.release(); // 抛出异常 throw new ProtocolException("Too many follow-up requests: " + followUpCount); } // 如果重定向的地址和当前的地址一样,则不需要释放连接 if (!engine.sameConnection(followUp.url())) { streamAllocation.release(); streamAllocation = null; } // 使用重定向后的请求,重新实例一个引擎,开始下一次循环 request = followUp; engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null, response); } }
最后HttpEngine进行的操作与http协议很大相关,作者对此不感兴趣,本文只是对流程做大概分析
哥哥若是喜欢可移驾公众号:码老板