网络请求之旅之OkHttp

OkHttp网络请求框架源码解析已经是一个老生长谈的问题了,很多公司在面试的时候也会问到,之前我都是看别人的解析流程,总感觉印象都不太深刻,所以我决定自己跟着源码走一遍,如有不对的地方还请大佬指出。话不多说,癞死狗!

请求入口

网络请求一般有同步和异步两种,通常我们只会用到异步请求,发送请求时,首先调用newCall方法返回一个Call对象,然后调用Call对象的enqueue方法,就像下面的代码一样。之后的事情就都交给OkHttp去做了。那么OkHttp内部究竟是怎么做的呢?我们就从enqueue方法一步步深入。

Call call = mOkHttpClient.newCall(request);
call.enqueue(new CommonJsonCallBack(handle));

RealCall

public interface Call extends Cloneable

查看一下Call的源码,发现它只是一个接口,而真正实现的是RealCall这个类,所以enqueue方法的具体实现自然就在RealCall中啦,进入enqueue方法

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

AsyncCall

写在前面,线程池相关,Executor通过execute方法发送线程到池中

 void execute(Runnable command);

RealCall.enqueue中最后一行,最终enqueue了一个AsyncCall对象,来看一下这个AsyncCall
这里建议呢不了解线程池的可以去先看看ThreadPoolExecutor的一些知识,免得跟我一样一脸懵x。
N天之后。。。。好啦,我看完Executor的相关知识啦。
从execute方法看到,它接收一个runnable对象,那我们回去看下OkHttp里线程池的execute方法传入的是谁,其实从一开始它就已经出现了,只是我们没有管它,回到RealCall的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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

这个NameRunnable就是一个带有线程名字的Runnable

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

可以看到,在它的run方法里执行了execute方法,当然啦,它真正的实现是在AsyncCall中,主要进行一些状态的判断以及接口的回调,在finally中调用Dispatcher的finished方法结束请求,有一行比较巧妙

Response response = getResponseWithInterceptorChain();

这行代码在同步请求中也会出现,点进去看下

 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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

可以看到,它主要是添加了许多拦截器,最终形成了一个链,每个拦截器处理自己可以处理的事情,不能处理的就交给下一级去处理,这不就是典型的责任链模式嘛!我也是看了别人的分析才知道的,秒啊,妙啊!
回到AsyncCall的execute方法,接下来可能会有点绕,可能我对线程池用的太少了,这里理解的时候有点头晕,不过慢慢理一理还是可以搞明白的。
上面说到异步请求都是在线程池中执行的,我们通过Executor的exexute方法将AsyncCall对象发送到线程池中执行,而在Executor中会执行我们传进去的runnable的run方法,所以通过Dispatcher的enqueue方法,最终就会执行到AsyncCall的execute方法中,最后再调用Dispatcher的finished方法从而完成了一次请求,之后就是在UI线程中处理得到的response对象。

Dispatcher 千呼万唤使出来

前面多次提到了Dispatcher,这个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<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<>();

看!注释的明明白白的。其实啊,类如其名,它就是一个分发器,负责网络请求队列的请求任务状态的管理与转换,当然还有执行,不过执行是在线程池中做的,注意最后三个双端队列
readyAsyncCalls:已经准备好的异步请求,在线程池有资源的情况下此队列里的请求会"升级"到runningAsyncCalls队列中
runningAsyncCalls:正在执行的异步请求,如果线程池有资源则会执行此队列里的请求,否则会将请求添加到readyAsyncCalls队列中
runningSyncCalls :正在执行的同步请求,这个是同步请求才会用到的队列,原理都差不多,这里不在赘述。
回到 client.dispatcher().enqueue(new AsyncCall(responseCallback));我们进到Dispatcher的enqueue方法中瞧一瞧

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

瞧!这就是那两个异步请求队列里数据的添加逻辑,和我上面说的差不多,最终的执行都是在线程池中执行的。有好事的小伙伴就要问了,你刚刚说的"升级"操作呢?这里看着它俩根本就没关系啊!没错,这里它俩确实没关系,但是,此处没关系不代表它俩一直没关系!前面说到AsyncCall的execute方法中无论如何都会调用finished方法,点进去看下

 /** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

调用了私有的finished方法,再点进去看

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

注意有一行if (promoteCalls) 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的事了呗,重点是小于的时候的for循环,对readyAsyncCalls进行遍历,如果当前请求主机数小于最大请求主机数,就意味着可以"升级"啦,readyAsyncCalls就会把这个请求remove掉,当然它会加入到runningAsyncCalls中,之后就是在线程池中执行请求操作啦。这就又回到了AsyncCall的execute方法中,绕了一圈终于结束了,之后就在UI线程中处理得到的数据就行啦。

完结撒花[手动撒花]

至此呢,异步请求就分析完了,同步请求比这个稍微简单点我就没有分析了,如果有需要可以去搜下其他博客,网上有很多关于okhttp源码的解析。感觉写下来也不是特别难,最开始接触的时候觉得这都是什么啊,太复杂了,不过后来看的多了,觉得还是蛮简单的,所以以后还是要多多实践,纸上得来终觉浅啊!
最最最后,打一个小广告,Kotlin+Mvp+Retrofit+RxJava,仿开眼App短视频应用,这是我边学kotlin边写的一个小demo,里面的视频真的挺不错的,有兴趣的可以看下哈,当然给个star就更好啦

网络请求之旅之OkHttp_第1张图片

你可能感兴趣的:(网络请求之旅之OkHttp)