Okhttp 源码解析

Okhttp请求实际使用了okio,当需要进行请求时,会进行一个socket连接,然后将请求报文解析成二进制流进行传输。其中okhttp的关键点就是Dispatcher(分发器)和Interceptor(拦截器)。

Dispatcher 分发器

请求的实际类为Call的实现类RealCall,会调用client.dispatcher()来进行请求。

同步请求 调用:client.dispatcher().executed(this)

​ 这里因为任务为Runnable所有不需要返回状态,直接进行请求

异步请求 调用:client.dispatcher().enqueue(new AsyncCall(responseCallback));

​ 这里使用了这里AsyncCall是Runnable的间接实现子类,需要传递的responseCallback是Callback的子类,这里方便回调请求的结果是成功还是失败。

Dispatcher 维护了三个队列和一个线程池

在这里插入图片描述

线程池 executorService

在这里插入图片描述

可以看到,如果不用构造器创建一个Executor时会在调用时创建一个,大概参数意思如下:

  1. 核心线程数:0
  2. 最大线程数:Integer.MAX_VALUE
  3. 空闲线程空闲时间:60
  4. 空闲时间单位:秒
  5. 阻塞队列:这个队列不保存数据,当用任务添加时会直接运行,如果没有空闲线程则新建一个线程执行
  6. 线程工厂类:给线程命名
异步请求

等待队列:

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

当请求任务提交时,会将任务提交到等待正在运行的队列中去,然后调用线程池中的线程去执行正在运行的请求队列中去,(请求完成和请求失败都算执行完成)执行完成后会使正在运行的队列remove出队任务,然后判断 runningAsyncCalls 队列是否添加 readyAsyncCalls 出队的任务,判断规则如下:

1、队列中数据长度是否 小于64
2、**readyAsyncCalls ** 出队的任务与 **runningAsyncCalls **队列中请求的相同域名是否小于5

异步请求流程:

调用call.enqueue()方法

​ 这里会判断锁上当前请求的RealCall防止异步,首先有个布尔类型的executed,这里默认为false,当这个RealCall进行一次请求后就改为ture,这样第二次请求时会抛出“Already Executed”不允许请求第二次,如果需要再次请求,则需要进行深克隆,因为RealCall间接实现了Cloneable,所有可以调用call.clone()来建立一个新的请求。

这里还有个EventListener监听器是提供给我们来监听网络请求状态的的,可以通过 OkHttpClient和call来创建。

EventListener eventListener = client.eventListenerFactory().create(call);

所以最终会调用事件分发器Dispatcher的enqueue方法来进行网络请求。

在这里插入图片描述
client.dispatcher().enqueue(new AsyncCall(responseCallback))方法

这里首先就会进行判断正在运行请求的队列里面是否满足以下要求,如果满足则添加到正在运行的队列中,否则进入等待队列中。然后调用线程池的execute(Runnable),线程池最终会调用任务AsyncCall中的run()方法。

1、队列中数据长度是否 小于64
2、**readyAsyncCalls **出队的任务与 **runningAsyncCalls **队列中请求的相同域名是否小于5

在这里插入图片描述

这里回到AsyncCall里面

我们会发现AsyncCall并没有run方法,那么我们先看看他的父类。很简单,在run方法中为线程给定了name,并且调用了子类实现的execute抽象方法。
在这里插入图片描述

我们回到AsyncCall,这里有个signalledCallback布尔,为了维持在拦截器中是否没有抛出异常。

  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 {
          //这里使用责任链模式经过5个拦截器后返回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) {
        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);
      }
    }
  }
同步请求

相对于异步请求就比较简单了,也是用的ReadCall这个类进行的请求,在Dispatcher中维护了一个专门用来同步请求的队列

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

当我们调用call.execute()方法时会调用RealCall的execute()方法,和异步一样,为了不允许用户重复调用使用了executed判断。然后就是请求状态返回的监听,调用evecuted方法会将任务添加到同步请求队列中,然后就直接在当前线程进行x责任链的拦截器进行网络请求

在这里插入图片描述


Interceptor 拦截器

在这里插入图片描述

重试重定向300-399拦截器:

返回一个Location字段:www.baidu.com(域名),然后重新请求,最多重定向20次,

桥接拦截器:

补全Request请求头

host:域名,
Connetion:Keep-Alive,
Accept-Encoding:gzip,(加密方式,默认为gzip)
Cookie:xxx,
User-Agent:xxx,
Content-Length:请求长度
Content-Type:数据类型
Transfer-Encoding

等等

缓存拦截器:

默认没有,需要OkHttpClient.Builder().cache(new Cache())去设置缓存拦截器

连接拦截器:

准备一个socket连接,准备好后将请求工作交给下一个拦截器

请求服务器拦截器:

将报文传输,接受到response后再将请求冲下往上,最终丢给重试重定义拦截器
等等

缓存拦截器:

默认没有,需要OkHttpClient.Builder().cache(new Cache())去设置缓存拦截器

连接拦截器:

准备一个socket连接,准备好后将请求工作交给下一个拦截器

请求服务器拦截器:

将报文传输,接受到response后再将请求冲下往上,最终丢给重试重定义拦截器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值