OkHttp源码探索

之前一直在使用volley作为网络请求模块,但发现volley已经过时(源码还是很值得反复品味的),现在基本是RetrofitOkHttp的天下了,于是把OkHttp拿来学习一下。
首先,看一个OkHttp的简单示例:

        Request.Builder builder = new Request.Builder().url(url).get();
        Request okRequest = builder.build();
        Call call = mOkHttpClient.newCall(okRequest);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onFetchFinished(response.body().string());
                    }
                });
            }
        });

该类框架都比较类似,创建一个请求及回调,然后加入请求队列,后面就由框架来进行任务调度,和结果回调。具体的细节,后面使用时再慢慢看,现在先探索一下OkHttp的主路线。

首先, 把请求加入到任务队列:

//okhttp3.RealCall.java

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

此方法就为避免单个请求多次添加,

// Dispatcher.java

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

  /** Returns the number of running calls that share a host with {@code call}. */
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

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

这个方法比较有意思,很明显有两个任务队列,一个是正在执行的队列(runningAsyncCalls)和一个等待队列(readyAsyncCalls);不仅限制了最大并发请求的数量,还对同一个域名的并发请求做了限制,当然单个应用可能请求的域名都是一样的,所以这个就得看实际情况了。
当前并发请求数量小于限制时,就把请求直接丢到线程池中执行;否则就把请求加入到等待队列。在代码中搜索readyAsyncCalls,看看等待队列中的任务是什么时候被执行的。看到下面的方法:

// Dispatcher.java

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

搜索这个方法是在哪里被调用的:
这里写图片描述
可以看到,在任务结束、改变最大并发请求和改变域名最大并发请求时会调用该方法,这些肯定是在意料之中的。

接下来就跟踪一下任务是如何执行的,我们就从队列中的请求任务入手。
AsyncCall的继承关系如下:

abstract class NamedRunnable implements Runnable
final class AsyncCall extends NamedRunnable
/**
 * Runnable implementation which always sets its thread name.
 */
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();
}

任务的执行放在了execute()中执行。我们来看看AsyncCall的execute()方法:

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

可以看出来,主要的内容是在getResponseWithInterceptorChain()里面,因为这个方法直接返回了Response,也就是请求结果。

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

这个方法的主要内容是得到一个列表interceptors,从字面上看,这个列表是用于请求拦截。通过断点跟踪后发现,这个列表确实重要,并且顺序也是非常的重要,从下面的代码慢慢分析。

//RealInterceptorChain.java

  @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();
    、、、
    // Call the next interceptor in the chain. 
    //注意这里的index+1了
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpStream, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    、、、
    return response;
  }

省略一些异常情况处理后,很清晰的看到,从interceptors列表的第一项(index初始化为0)开始执行interceptor.intercept(next),并且参数是RealInterceptorChain链条的下一个节点。

  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()));

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        、、、
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder().body(null).build())
            .build();
      }
      Request followUp = followUpRequest(response);
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());
      、、、
      request = followUp;
      priorResponse = response;
    } // end while
  }

这个方法的主体是一个死循环,有两种方式退出循环:一个是throw new IOException(“Canceled”),另一个是return response。也就是说,当请求被取消,或者得到结果了就结束返回了。再看看response是什么时候被赋值的:

response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

又回到刚才的proceed(request)里面了,而proceed(request)里面用到了index+1,也就是这时会使用interceptors列表中的第二个interceptor来进行intercept(next),酱紫看来,这是一个递归的过程。只有当进行到chain中的某一个节点,能够得response而不是继续向下传递时,递归开始反转退栈。
跟踪代码看到interceptor的顺序为RetryAndFollowUpInterceptor——>BridgeInterceptor——>CacheInterceptor——>ConnectInterceptor——>CallServerInterceptor. 至于为何需要这么几层,还不是很清楚,还需要继续学习。

至此,okhttp的请求主流程基本介绍完毕,后面还有大量的东西需要去学习。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章