OkHttp源码分析

·  阅读 384

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

 大家好,我是小黑,一个还没秃头的程序员~~~

路是走出来的,而不是空想出来的。

相信大家找工作的时候都会被问及到Okhttp的原理以及源码分析,好记性不如烂笔头,所以这次我打算把它记录下来方便日后复习查看,也和大家分享一下,如果有什么不对之处还请大家多多指教!

这次的分析分为三个步骤:

  1. 网络请求发出去后到了哪里
  2. 请求是怎么被处理的
  3. 请求结束后又会做什么

(一)网络请求发出去后到了哪里

OkHttp发送请求有两种方式:enqueue/execute,在enqueue异步请求之前我们需要调用newCall()newCall() 返回的是RealCall对象

 /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }
复制代码

所以我们找到RealCall类中的enqueue()

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

可以看到最终是执行了dispatcher().enqueue() 来完成的,我们进入Dispatcher类中,Dispatcher类是用来实现任务调度的,主要有以下变量

  //最大并发请求数
  private int maxRequests = 64;
  //每个主机的最大请求数
  private int maxRequestsPerHost = 5;

  //消费者线程池
  private ExecutorService executorService;

  //等待中的请求队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  //正在运行的异步请求队列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  //正在运行的同步请求队列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
复制代码

Dispatcher类中,有两个构造函数,一个有传入线程池,一个没有传入线程池,没有传入线程池的话会在异步请求之前创建一个默认的线程池

 public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }

 //创建线程池,SynchronousQueue为一个没有容量的队列
 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;
  }
复制代码

前面讲到执行RealCallenqueue() 便会最终到Dispatcherenqueue() ,它的代码如下

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

当正在运行的异步请求队列中的数量小于64并且正在运行的请求主机数小于5时,把请求加载到runningAsyncCalls队列中中并在线程池中执行,否则就加入到readyAsyncCalls队列中进行等待。到此为止,第一个问题就解决了,请求最终会被发送到两个队列中,要么被执行要么等待。

(二)请求是怎么被处理的

任务被放进队列中后,任务是AsyncCall类,是继承于NamedRunnable的一个类,执行AsyncCallexecute() 方法,代码如下:

    @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,并在相应的回调中返回,这是处理请求的地方,这里会添加一个拦截器链,如是否重定向,缓存拦截器,自定义拦截器等,代码如下:

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

    //最后一个拦截器,会对服务器进行网络调用,在intercept()方法中构建头部以及body,并获取返回
    interceptors.add(new CallServerInterceptor(forWebSocket));
    
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
复制代码

接下来我们看代码最后面的proceed() 方法,这个方法是RealInterceptorChain这个类实现的,代码如下:

  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }
复制代码

我们接着点进去 ,在这里面会去做关于response的返回,在这里从拦截器列表中取出拦截器,并使用通过各个拦截器通过intercept()方法的实现来获取拦截器的调用返回,拦截器是按顺序取出来处理的,代码如下:

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
   ...

    // 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;
  }
复制代码

到这里,问题二已解决了,请求是在getResponseWithInterceptorChain() 获取返回的。

(三)请求结束后又会做什么

上面提到任务执行的时候会调用到AsyncCallexecute() 方法,方法最后会调用client.dispatcher().finished(this) ,我们点进finished() 方法看一下,代码如下:

  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
复制代码
  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();
    }
  }
复制代码

关键在于promoteCalls() 方法,代码如下:

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // 运行中的请求以及到了最大的请求数
    if (readyAsyncCalls.isEmpty()) return; // 没有等待中的请求了
    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.
    }
  }
复制代码

从代码中可以看出,在一个请求结束后,会去对运行中的请求以及等待中的请求数进行判断,并将等待队列中的请求移除一个并添加到线程池中进行处理,即进入运行队列中处理,这就是请求结束后的内容了,到此为止,OkHttp源码分析的三个步骤就介绍完毕,日后我会接着分享自己在阅读源码的体会与总结,最后,希望喜欢我文章的朋友们可以帮忙点赞、收藏、评论,也可以关注一下,如果有问题可以在评论区提出,谢谢大家的支持与阅读!

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改