OkHttp源码分析 (一)

332 阅读2分钟

请求方式

OkHttp支持同步和异步的Http请求

同步和异步请求的代码如下:

private final static OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(8, TimeUnit.SECONDS)
    .readTimeout(8,TimeUnit.SECONDS)
    .writeTimeout(8,TimeUnit.SECONDS)
    .build();

//同步的请求
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
    return response.body().string();
} else {
    throw new IOException("Unexpected code " + response);
}

//异步的请求
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        System.out.println(e.getMessage());
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
            return response.body().string();
        }
    }
});

OkHttpClient使用Builder模式来为其配置众多的参数

public static final class Builder {
    Dispatcher dispatcher;
    @Nullable Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable Cache cache;
    @Nullable InternalCache internalCache;
    SocketFactory socketFactory;
    @Nullable SSLSocketFactory sslSocketFactory;
    @Nullable CertificateChainCleaner certificateChainCleaner;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;
    ……

    //Builder的参数默认的给出一些参数的设置
    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;
    }

    public OkHttpClient build() {
      return new OkHttpClient(this);
    }
}

OkHttpClient通过build创建完实例后,就包含了众多的参数

创建请求

用户的请求是通过一个Request来描述的,它一般的构造形式如下:


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

public final class Request {
    ……
    public static class Builder {
        private HttpUrl url;//请求的URL
        private String method;//请求的方法 如GET POST等
        private Headers.Builder headers;//请求的HTTP头部
        private RequestBody body;//请求的body
        private Object tag;

        public Builder() {
        this.method = "GET";
        this.headers = new Headers.Builder();
        }
    }

    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
}

同样的Request也是通过Builder来创建构造实例的,它只是封装了HTTP请求需要的一些信息。Okhttp通过newCall来为我们的请求创建一个Call对象,这个对象负责为Request服务来完成一次Http请求过程

@Override public Call newCall(Request request) {
    return new RealCall(this, request);
}

发起请求

接着我们看看RealCall的实现

//同步的请求
@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(false);//执行请求过程
        if (result == null) throw new IOException("Canceled");
        return result;
    } finally {
        client.dispatcher().finished(this);
    }
}

//异步的请求
void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}

同步请求是通过RealCall的execute发起的,它的实现也很简单,首先置executed为true表示Call已经发起执行,若再次使用该Call发起请求会抛出Already Executed异常随后通过OkHttpClient配置的dispatcher执行executed,这个Dispatcher是请求的派发策略,说白了就是发起的请求,无论是异步的还是同步都要通过该Dispatcher进行一些设置。对于同步的请求,dispatcher会将该请求记录到一个同步请求队列中,请求完成后通过finished方法将其移除,而对于异步的请求Dispatcher会将该请求任务添加到异步请求队列中,并通过线程池来发起真正的请求。这里需要注意对于异步请求它的Call为AsyncCall。

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

  /** 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<>();//同步队列

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

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);//将请求添加到同步队列中
  }

  /** Used by {@code Call#execute} to signal completion. */
  //同步请求使用该方法
  synchronized void finished(Call call) {
    if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
  }

  synchronized void enqueue(AsyncCall call) {
    //将异步请求添加到异步队列中,可以看到OKhttp同时支持64个异步请求的执行,对同一个Host的请求最大同时支持5个 
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);//否则添加到等待队列中
    }
  }
  //异步请求调用
  synchronized void finished(AsyncCall call) {
    if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
    promoteCalls();//每次异步请求调用完成后会通过该方法将等待队列中的请求移到异步请求队列中发起请求
  }
  …… 
}

那么对于同步请求来说,在RealCall的execute方法中真正执行请求过程的必然是通过getResponseWithInterceptorChain来实现的,通过方法名称我们也能知道。而异步请求是在AysnCall的回调中进行的,我们看看AsynCall的实现

public abstract class NamedRunnable implements Runnable {
  protected final String name;
  ……  
  @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();
}
final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private final boolean forWebSocket;
    ……
    @Override protected void execute() {
        boolean signalledCallback = false;
        try {
            Response response = getResponseWithInterceptorChain(forWebSocket);
            if (canceled) {
                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!
                logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
            } else {
                responseCallback.onFailure(RealCall.this, e);
            }
        } finally {
            client.dispatcher().finished(this);
        }
    }
  }
}

对于异步请求来说,在ASyncCall的回调方法中同样是通过getResponseWithInterceptorChain(forWebSocket)来完成HTTP的请求过程,随后通过responseCallback将请求结果返回给上层应用最后通过dispatcher的finished方法将Call从异步队列中移除。

请求过程

前面我们知道了无论是同步还是异步的请求最终都是通过getResponseWithInterceptorChain方法来完成请求过程的,接下来我们将分析请求过程的具体实现

final class RealCall implements Call {
    ...
    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 (!forWebSocket) {
            interceptors.addAll(client.networkInterceptors());
        }
        interceptors.add(new CallServerInterceptor(forWebSocket));

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

}

从getResponseWithInterceptorChain中我们并没有看到实际的请求过程是如何进行的,它内部维护了一个拦截器列表,这个列表首先添加OkHttpClient配置的应用拦截器,然后添加内置的几个拦截器,最后添加网络拦截器和CallServerInterceptor拦截器,然后通过这个拦截器列表构造一个RealInterceptorChain并调用processd来执行请求过程。那么拦截器在这之间到底起到什么作用?事实上,一个拦截器可以看做是一个中间件,它负责拦截我们的请求和响应,并对请求和响应做一些额外的工作,比如为我们请求添加日志,寻找路由,制定连接失败的重试策略为,为响应执行缓存策略等等。总的来说是OkHttp加入拦截器的目的是为了丰富和拓展请求过程使其支持各式各样的需求。

拦截器

为了了解请求的整个过程,我们需要明白拦截器是如何工作的,首先所有的拦截器都是需要实现Interceptor接口的,

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

  interface Chain {//拦截器链的接口
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();
  }
}

为了了解拦截器的执行过程,我们需要看看RealInterceptroChain的实现

public final class RealInterceptorChain implements Interceptor.Chain {
    private final List<Interceptor> interceptors;//持有的拦截器链
    private final int index;//当前链中工作的拦截器索引

    @Override public Response proceed(Request request) throws IOException {
        return proceed(request, streamAllocation, httpCodec, connection);
    }
    //拦截器链的处理方法
    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
        RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec, connection, index + 1, request);
        Interceptor interceptor = interceptors.get(index);//得到当前的拦截器
        Response response = interceptor.intercept(next);//执行当前拦截器的方法,这里将下个拦截器传递给它
        ……

        return response;
    }
}

事实上拦截器链的请求过程类似于网络协议栈对于应用数据的处理过程,这里为了解析其工作原理,我们假设有拦截器链A->B->C->D这个链在OKHttp中的表示就是RealInterceptorChain 它实现了拦截器链的proceed方法,主要用来遍历执行拦截器链,开始我们拦截器链的索引为0,也就是从拦截器A开始调用其intercept方法执行拦截器,需要注意的是intercept方法的参数为拦截器链,在A的intercept方法中会通过传递的chain再调用proceed方法,这样每次进入proceed方法就可以inex+1指定链中的下一个拦截器。一直到拦截器D,D不需要再执行chain的proceed方法,因为它是最后一个拦截器,后面没有拦截器了。在OkHttp中拦截器链的最后一个拦截器是CallServerInterceptor,它是真正的执行http请求的地方。