Android 高级进阶Android 源码分析Android网络请求开源库

OkHttp 源码解析

2018-04-23  本文已影响37人  Code猎人

前言

OkHttp可以说是最主流的网络请求框架了,很多项目是直接使用Retrofit 2.0提供的接口进行网络请求,Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。想了解Retrofit请移步Retrofit 2.0 源码分析,retrofit是负责接口封装,okhttp才是真正的网络请求,今天我们就一起探究整个okhttp请求的过程,本章节并不会着重讲怎么使用,主要阅读源码了解内部的部分机制以及一些核心类的作用。

初始化OkHttpClient

OkHttpClient mOkHttpClient = new OkHttpClient();  

OkHttpClient实例是通过建造者模式通过Builder类进行创建的

 public OkHttpClient() {  
 this(new Builder());  
}  

我们看一下builder初始化了哪些参数

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,也是通过建造者模式创建的

Request request = new Request.Builder()    
.url(url)    
.build(); 

Request类是HTTP请求,它携带了请求地址、请求方法、请求头部、请求体以及其他信息。它也是通过Builder模式创建的。

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();

看一下 OkHttpClient.newCall(request),做了什么,RealCall类实现了Call接口,下面展示RealCall()方法的代码

protected RealCall(OkHttpClient client, Request originalRequest) {  
this.client = client;  
this.originalRequest = originalRequest;  
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);  
}  

接着是RealCall的execute()方法。execute()方法是同步方法,即一直等待http请求, 直到返回了响应. 在这之间会阻塞进程, 所以通过同步方法不能在Android的主线程中执行, 否则会报错。

OKHttp提供了execute(同步方法)和enqueue(异步方法),下面我们先看看execute(同步方法)

@Override public Response execute() throws IOException {
synchronized (this) {
  if (executed) throw new IllegalStateException("Already Executed");
  executed = true;
}
captureCallStackTrace();
try {
  client.dispatcher().executed(this);
  Response result = getResponseWithInterceptorChain();
  if (result == null) throw new IOException("Canceled");
  return result;
} finally {
  client.dispatcher().finished(this);
}
}

execute()方法,首先判断是否已执行过,如果已经执行过,则抛出异常信息,也就是说一次Call实例只能调用一次execute()方法,和我们之前说的一样。

如果未执行,则调用Dispatcher类的executed()方法将该Call加入到一个双端队列中

private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//双端队列  
/** Used by {@code Call#execute} to signal it is in-flight. */  
synchronized void executed(RealCall call) {  
runningSyncCalls.add(call);  
} 

接着调用getResponseWithInterceptorChain()方法返回Response对象,最后finally中,调用Dispatcher的finished()方法,在从已经执行的双端队列中移除本次Call。

void finished(RealCall call) {  
finished(runningSyncCalls, call, false);  
}  

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();  
}  
}  

通过上面分析,真正发出网络请求,返回结果应该是getResponseWithInterceptorChain()方法,那么接下来,主要看看这个方法

private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
  interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
    retryAndFollowUpInterceptor.isForWebSocket()));

Interceptor.Chain chain = new RealInterceptorChain(
    interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}

该方法是组装各种拦截器为一个拦截器链,最后调用RealInterceptorChainproceed()方法,来处理这个请求

@Override public Response proceed(Request request) throws IOException {  
 return proceed(request, streamAllocation, httpStream, connection);  
 }  

public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,  
 Connection connection) throws IOException {  
 if (index >= interceptors.size()) throw new AssertionError();  

  calls++;  

 // If we already have a stream, confirm that the incoming request will use it.  
 if (this.httpStream != null && !sameConnection(request.url())) {  
 throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)  
     + " must retain the same host and port");  
}  

// If we already have a stream, confirm that this is the only call to chain.proceed().  
if (this.httpStream != null && calls > 1) {  
 throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)  
      + " must call proceed() exactly once");  
}  

// Call the next interceptor in the chain.  
RealInterceptorChain next = new RealInterceptorChain(  
   interceptors, streamAllocation, httpStream, connection, index + 1, request);  
Interceptor interceptor = interceptors.get(index);  
Response response = interceptor.intercept(next);  

// Confirm that the next interceptor made its required call to chain.proceed().  
if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {  
 throw new IllegalStateException("network interceptor " + interceptor  
     + " must call proceed() exactly once");  
}  

 // Confirm that the intercepted response isn't null.  
 if (response == null) {  
 throw new NullPointerException("interceptor " + interceptor + " returned null");  
 }  

return response;  
}  

拦截器Interceptor和拦截器链Chain都是接口

public interface Interceptor {  
Response intercept(Chain chain) throws IOException;  

interface Chain {  
Request request();  

Response proceed(Request request) throws IOException;  

Connection connection();  
 }  
}  

下面用一张流程图来说明拦截器链递归从拦截器中返回Response

下面再看看enqueue()方法(异步方法),指在另外的工作线程中执行http请求, 请求时不会阻塞当前的线程, 所以可以在Android主线程中使用。

@Override public void enqueue(Callback responseCallback) {  
synchronized (this) {  
if (executed) throw new IllegalStateException("Already Executed");  
executed = true;  
}  
client.dispatcher().enqueue(new AsyncCall(responseCallback));  
}  

首先都校验这个Call是否已经被执行,如果执行过,就报异常。如果为执行,则调用Dispatcher分发器的enqueue()方法。看下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 {
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}
}

NamedRunnable是实现了Runnable接口,AsyncCall–实际上是一个Runnable,在run()方法中调用了execute()方法,在该方法中调用getResponseWithInterceptorChain()

Response response = getResponseWithInterceptorChain();  

拿到请求结果,接下来看一下client.dispatcher().finished(this)做了什么

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()方法先从runningAsyncCalls(异步请求队列)删除已经执行的异步请求,然后接着调用了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.  
}  
}  

首先runningAsyncCalls(异步请求队列) readyAsyncCalls和异步调用准备任务)两部判断,接着循环readyAsyncCalls),将call加入到runningAsyncCalls中,并在readyAsyncCalls删除掉该call,接着线程池执行call

继续看Dispatcher分发器的enqueue()方法

synchronized void enqueue(AsyncCall call) {  
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {  
 runningAsyncCalls.add(call);  
 executorService().execute(call);  
} else {  
 readyAsyncCalls.add(call);  
}  
}  

如果runningAsyncCalls的大小小于最大请求数量(最大线程数量、并发数量)并且call小于最大主机请求限
制,那么将call 加入到runningAsyncCalls中,接着线程池执行call;否则,将call加入到readyAsyncCalls(异步调用准备任务)。
PS: runningCallsForHost()方法,循环判断cal的host和runningAsyncCalls的中的call的host相同的数量。同一请求是否超过想过请求同时存在的最大值

private int runningCallsForHost(AsyncCall call) {  

int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.host().equals(call.host())) result++;
}
return result;
}

接下来看一下Dispatcher类,这个是核心类,Dispatcher是异步请求的策略

public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;

/** Executes calls. Created lazily. */
private ExecutorService executorService;

/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
...
}

int maxRequests = 64: 最大并发请求数为64
int maxRequestsPerHost = 5: 每个主机最大请求数为5
Runnable idleCallback:Runnable对象,在删除任务时执行
ExecutorService executorService:消费者池(也就是线程池)
Deque<AsyncCall> readyAsyncCalls:缓存,异步调用准备执行任务
Deque<AsyncCall> runningAsyncCalls:正在运行的异步任务,包含取消尚未完成的调用
Deque<RealCall> runningSyncCalls: 正在运行的同步任务,包含取消尚未完成的调用

最后看一下整个请求的流程图

整个OkHttp源码分析请求过程就结束了

总结

基本上 OkHttp 的请求响应的流程就讲完了,内容有点多。本章节只是分析了主要部分的源码,先对整个结构要有一个系统性的了解,还有很多细节没有展开,比如连接池、缓存策略等等一些机制的实现,有兴趣可以单独深度去了解。


点赞加关注是给我最大的鼓励!

上一篇下一篇

猜你喜欢

热点阅读