OKHttp源码 - 流程简单解析

本文章基于OKHttp3.12.0分析

compile 'com.squareup.okhttp3:okhttp:3.12.0'

首先看我们调用OKHttp执行网络的一般代码:

private static void initOKHttp() {
        client = new OkHttpClient.Builder().build();
    }
    private static void OKGetWeather() {
        Request request = new Request.Builder()
                .url("https://restapi.amap.com/v3/weather/weatherInfo?key=5326a9f59587393b549f3cffefa0459b&city=110000&output=json&extensions=base")
                .tag("")
                .build();
        client.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) { }
            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {}
        });
    }

调用的时候是一个简洁明了的Builder模式,使用builder模式来against参数特别多的情况。简单的网络调用就这么几步

1. 构造OKHttpClient
2. 构造Request
3. 得到一个Call
4. 同步或异步执行Call

一步步来,我们先看OKHttpClient的代码,这里搬运一下OKHttpClient这个类的注释。包括一些简单的对OKHttpClient的介绍。
OKHttpClient是用来发送Http请求和解析Http回应的。 OKHttpClient应该是共享的。它的正确用法应该是创建一个单例并且为所有的Http请求所用。因为每个Client都会维持自己的连接池和线程池。重用连接和线程为了降低时延并且节省内存。所以,为每个Request创建一个Client会浪费资源,因为池子总是在空闲状态。
使用new OkHttpClient()去创建使用默认设置的一个共享的实例

        // The singleton HTTP client.
       public final OkHttpClient client = new OkHttpClient();

或者使用new OkHttpClient.Builder()去创建一个使用自定义设置的实例

   // The singleton HTTP client.
   public final OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new HttpLoggingInterceptor())
            .cache(new Cache(cacheDir, cacheSize))
            .build();

你还可以用newBuilder()定制一个共享的OkHttpClient实例,这样创建的Client会共享相同的连接池、线程池和配置使用builder方法去配置 衍生的client或者特殊的意图。
这个例子展示了一个使用了500ms超时时间的call

OkHttpClient eagerClient = client.newBuilder()
            .readTimeout(500, TimeUnit.MILLISECONDS)
            .build();
Response response = eagerClient.newCall(request).execute();

Shutdown不是必要的,Client持有的线程和连接如果一直空闲会自动被释放。但是如果你的application中需要强制释放掉不需要的资源你可以这样做

          client.dispatcher().executorService().shutdown();

使用evictAll()清空连接池,注意连接池进程可能不会立刻退出

         client.connectionPool().evictAll();

我们去看OKHttpClient的源码,其实就是一个大的入口类,其中静态内部类Builder的源码占了3/5的样子。都是关于其中属性的Builder方法调用,参数太多了,我们删减一下看

public Builder newBuilder() {
    return new Builder(this);
  }
public static final class Builder {
    Dispatcher dispatcher;
    @Nullable Proxy proxy;

    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      ...
      readTimeout = 10_000;
    }

    Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
     ...
    }

public Builder connectTimeout(long timeout, TimeUnit unit) {
      connectTimeout = checkDuration("timeout", timeout, unit);
      return this;
    }
public Builder readTimeout(long timeout, TimeUnit unit) {
      readTimeout = checkDuration("timeout", timeout, unit);
      return this;
    }
 public OkHttpClient build() {
      return new OkHttpClient(this);
    }
  }

我们调用builder的这些方法的时候会把我们的配置存储到builder里面,然后build的时候会把这些属性赋值到创建出来的OKHttpClient里面。
这里我们也看到了newBuilder()方法。上面的介绍中说如果想定制某一个属性但是还想共享OKhttpClient的其他属性的话可以用这个方法。我们看到用newBuilder的时候会把之前的OKHttpClient的方法再复制给builder,然后加上你自己修改的builder的方法重新new一个OKHttpClient出来,当然OKHttpClient只是算个书包,平时我们用书包背着工作生活用的电脑手机钱包,newBuider new出来的OKHttpClient算是我们换了个书包,但真正我们生活工作的电脑钱包并没有变,所以还会共享之前的配置。
OKHttpClient Call的入口是这里

public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

这个方法会返回一个RealCall,我们看RealCall

final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
  final AsyncTimeout timeout;
  private @Nullable EventListener eventListener;
...

  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
    //实现Timeout,如果超过了时间就cancel掉
    this.timeout = (AsyncTimeout) timeOut() -> { cancel(); };
    //设置了给定的timeout时间
    this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
  }

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

  @Override public Response execute() throws IOException {
    ...
    timeout.enter();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      ...
    } finally {
      client.dispatcher().finished(this);
    }
  }
@Override public void enqueue(Callback responseCallback) {
    ...
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
final class AsyncCall extends NamedRunnable {
  }

RealCall实现了Call方法,看过Retrofit的源码我们知道,Retrofit有个OkHttpCall实现了Retrofit的Call接口,Retrofit的Call几乎是拷贝了OKHttp的Call接口,这里面就是OKhttp的核心请求方法。RealCall是Call的实现类。
我们注意到还有一个AsyncCall,继承自NamedRunnable,NamedRunnable是个简单包装了线程名字的Runnable,不再铺开,可以当做AsyncCall只是实现了Runnable接口
同步execute传的是RealCall,异步enqueue传的是AsyncCall。
同步方法execute,核心方法是

client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();

异步核心方法:

    client.dispatcher().enqueue(new AsyncCall(responseCallback));

我们看到同步比异步多了一句getResponseWithInterceptorChain(),这里先放一下,后面呢会说到,可以记做①。
client是传进来的OKHttpClient,得到Client里面的Dispatcher,先简单看这个Dispatcher

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;
  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;
  /** Ready async calls in the order they'll be run. */
  private final Deque readyAsyncCalls = new ArrayDeque<>();
  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque runningAsyncCalls = new ArrayDeque<>();
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque runningSyncCalls = new ArrayDeque<>();

我们看它的参数就能猜到,这玩意儿是个Pool,接受两种Call,分别有对应的集合存储:
1、是RealCall,也就是我们现在看的RealCall
2、是AsyncCall,也就是上面异步方法的AsyncCall
回来看execute和enqueue,

 synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);
    }
    promoteAndExecute();
  }

我们看到executed只是把RealCall放到了集合里面,而enqueue放到集合里面后又执行了promoteAndExecute()

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

其实就是把所有readAsyncCall中的Runnable都拿出来扔到executorService中执行了。看下面的代码知道 executorService就是个ThreadPoolExecutor,其实就是单纯的是个线程池了。

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

扔到线程池中执行Runnable(也就是AsyncCall)的executeOn方法.

void executeOn(ExecutorService executorService) {
      ...
      try {
        executorService.execute(this);
      } ...} finally {
          client.dispatcher().finished(this); // This call is no longer running!
      }
    }
...
    @Override protected void execute() {
...
        Response response = getResponseWithInterceptorChain();
        ...
          responseCallback.onResponse(RealCall.this, response);
        }
      }...} finally {
        client.dispatcher().finished(this);
      }
    }

我们看executeOn就是再线程中执行自己,其实就是调用了execute()方法。
execute()中执行了就是getResponseWithInterceptorChain()了,我们记起来我们之前同步的时候还放了一个①。所以绕来绕去同步异步又回到了同一个起点,都到了同一个入口。


大头getResponseWithInterceptorChain(),加个分割线庆祝一下。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List 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);
  }

OKHttp的精髓,责任链模式
简单来说就是链式结构所有的处理器都是一个Chain。前面处理完的处理结果丢给后面,前面处理不了或者不想处理的无脑丢给后面。
我们看上面代码,list添加了如下的interceptor

1. addAll 用户自定义的interceptors
2. add retryAndFollowUpInterceptor
3. add BridgeInterceptor
4. add CacheInterceptor
5. add ConnectInterceptor
6. add networkInterceptors
7. add CallServerInterceptor

我们后面依次讲这些Interceptor的作用,先把流程顺下去。然后新建了一个Chain-RealInterceptorChain,然后调用了chain的proceed方法。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
   ...
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
...

    return response;
  }

刨掉if-else和try-catch代码就上面几句,得到list中的Interceptor,得到list在index位置的Interceptor并调用它的intercept,把Chain传进去。这里只是调用了第1个interceptor,所以需要在每个interceptor中调用Chain.proceed(),这样Chain就串起来了。
所以如果我们自定义了一个interceptor叫做MyInterceptor的话,这个MyInterceptor会被在

interceptors.addAll(client.interceptors());

这里被add到list里面,会被第一个就调用,我们必须要在它里面写

//处理request
Response response = chain.proceed(request);
//处理response

以便让Chain串起来,如果我们想处理request就需要在这句之前写,处理response就在这句之后写。所以处理之前之后的数据都可以。就看写在chain.proceed(request);的哪个位置。
这样网络连接就串起来了。
大致流程图如下:

请求流程图
另起一篇写interceptor吧~

你可能感兴趣的:(OKHttp源码 - 流程简单解析)