美文网首页
OkHttp 源码分析

OkHttp 源码分析

作者: CodeDuan | 来源:发表于2022-02-24 14:30 被阅读0次

一、概述

在日常开发中最常用的请求框架就是Okhttp了,本文将对okhttp的请求流程 由浅入深进行分析,由于我项目中使用的Okhttp版本为3.12.0,所以我们根据3.12.0的源码进行分析。

Okhttp特点:
OkHttp 是一个默认高效的 HTTP 客户端:

  • HTTP/2 支持允许对同一主机的所有请求共享一个套接字。
  • 连接池减少了请求延迟(如果 HTTP/2 不可用)。
  • 透明 GZIP 缩小了下载大小。
  • 响应缓存完全避免了网络重复请求。

二、源码分析

在源码分析之前,我们先看一下Okhttp的简单使用。

        //创建一个OkhttpClient
        OkHttpClient client = new OkHttpClient();
        //创建Request
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        //同步请求
        Response response = client.newCall(request).execute();

这样就很简单的实现了一个请求,我们一点一点来分析,先来看OkhttpClient

public OkHttpClient() {
    this(new Builder());
  }

OkHttpClient(Builder builder) {
    //省略部分代码
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    //省略部分代码
  }

可以看到在创建OkHttpClient时,使用的是建造者模式,最终创建出OkhttpClient,Builder的代码太长这里就不贴了,就是对一些配置的初始化,例如超时时间等,另外我们可以通过这个Builder.build()方法来构建出OkhttpClient。

接下来我们再来看Request是什么:

public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Map<Class<?>, Object> tags;

  private volatile @Nullable CacheControl cacheControl; // Lazily initialized.

  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tags = Util.immutableMap(builder.tags);
  }
    //省略部分代码
}

Request包含的就是请求的url,method,header,RequestBody等信息。同样是通过Builder来构建。

最后就是执行请求了,到这里之后就是重点了,好好看哦,

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

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

            }
        });

同样我们按照顺序,先来看client的newCall方法做了什么:

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

newCall方法返回值为Call,返回了一个RealCall(Call的实现类)。

既然newCall返回的是RealCall,那么enqueue方法就一定再RealCall里面,就是说我们的同步/异步请求最终都是通过这个RealCall来执行的。所以我们来看RealCall的enqueue()方法:

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
        //如果为true 直接抛出异常,说明这个请求已经执行了
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
      //事件监听回调
    eventListener.callStart(this);
      //重点
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

上面的最后一行代码又调用了client.dispatcher().enqueue()方法,就是Dispatcher类的enqueue(),我们来点进去看:

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

enqueue方法又将call加入了readyAsyncCalls,我们先来看看这是啥:

  /** 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<>();

在Dispatcher类中有三个双端队列:
readyAsyncCalls:准备队列,
runningAsyncCalls:运行时异步队列
runningSyncCalls:运行时同步队列

所以说上面的enqueue方法中,就是将call先放到了准备队列,然后调用了promoteAndExecute()方法,我们来看看这个方法:

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

    //用于保存要执行请求的队列
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

          //如果运行中队列大于maxRequests 64 终止执行
          //就是正在请求的数量 不能大于64
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
          //如果相同host同时请求大于maxRequestsPerHost 结束本次循环
          //就是对同一个主机的同时请求 不能大于5
        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;
  }

promoteAndExecute()方法的主要操作就是将符合执行条件的请求执行,最终调用的是asyncCall.executeOn(executorService())方法,下面我们先来看executorService()方法:

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

这个方法其实就是返回了一个线程池,所以说异步请求是通过线程池来处理的,我们再返回去看asyncCall.executeOn()方法:

    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        //通过线程池 执行请求 this = RealCall
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        //异常处理
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        eventListener.callFailed(RealCall.this, ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
            //
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

在上面的方法中,调用了线程池的execute(this)方法,就相当于调用了RealCall的execute,继续来看这个方法:

@Override protected void execute() {
      boolean signalledCallback = false;
      timeout.enter();
      try {
          //这里最终通过拦截器 来拿到response
        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) {
        e = timeoutExit(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 {
          //最终调用了finished
        client.dispatcher().finished(this);
      }
    }

RealCall的execut方法就是通过拦截器来拿到response,最终调用了client.dispatcher().finished(this)方法,拦截器我们先放在后面讲,先继续看client.dispatcher().finished(this):

//最终调用的这个方法
private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }

    //这里是重点 这里又调用了promoteAndExecute()
    boolean isRunning = promoteAndExecute();

    if (!isRunning && idleCallback != null) {
      idleCallback.run();
    }
  }

到这里是不是有一点豁然开朗了?这个方法里又调用了promoteAndExecute()方法,还记得刚开始的enqueue里面也是执行了这个方法嘛?所以说当一个请求结束后,又再次处理队列中的其他请求。

到这里整个请求的流程就结束了。我们继续来看一下上面提到的拦截器;

Response response = getResponseWithInterceptorChain();

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //声明一个list 存储拦截器
    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));
    //自定义的的network拦截器
    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中有五种拦截器:重试拦截器,桥接拦截器,缓存拦截器,连接拦截器,请求服务拦截器,另外还有用户自定义的拦截器。其中拦截器是责任链模式,通过层层调用最终返回我们想要的Response,拦截器暂时不深入介绍了,后面有时间再研究吧。

异步请求看完之后,接下来就剩同步请求了,其实同步请求更简单,

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    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) {
      e = timeoutExit(e);
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }
 synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

同步请求则直接将请求通过dispatcher加入了运行中同步队列中,其他处理与异步请求大致相同,这里就不做太多介绍了。

三、总结

至此我们已经分析完了Okhttp请求的处理流程,我们最后总结一下:

  1. 创建OkhttpClient
  2. 构建Request请求
  3. newCall得到RealCall
  4. 通过RealCall调用enqueue/execute
  5. 通过dispatcher进行任务分发
  6. 最终通过拦截器得到Response

最后画一张流程图仅供参考:


img.jpg

相关文章

网友评论

      本文标题:OkHttp 源码分析

      本文链接:https://www.haomeiwen.com/subject/qevzlrtx.html