OkHttp源码分析

OkHttp源码分析

分析版本:3.9.x
众所周知OkHttp请求有四步:

  1. 创建OkHttpClient对象
  2. 创建Request对象
  3. 通过newCall方法创建Call对象
  4. 通过Call对象的excute()方法发送同步请求,enqueue()方法发送异步请求

OkHttpClient

可以查看源码对于OkHttpClient的注释:

Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their
responses.

简而言之就是Call作为发起一个网络请求和接受服务器响应的存在,OkHttpClient则是Call的工厂类,查看Call这个类发现它是有个接口,实际上发送请求的是RealCall,这个后面会讲到.使用Okhttp最开始要做的就是创建一个OkHttpClient对象,源码提供了两种方法,一种直接new OkHttpClient(),另一种是通过OkHttpClient.Builder(),Builder是OkHttpClient的一个静态内部类,通过建造者模式链式调用来配置一些参数,如请求超时时间,读写超时时间以及各种Interceptor等,简单样式如下:

OkHttpClient client = new OkHttpClient
			.Builder()
			.addInterceptor(interceptor);
			.retryOnConnectionFailure(true)
			.callTimeout(20, TimeUnit.SECONDS)
			.connectTimeout(20, TimeUnit.SECONDS)
       	 	.readTimeout(20, TimeUnit.SECONDS)
        	.writeTimeout(20, TimeUnit.SECONDS)
        	.build();

而第一种直接new OkHttpClient()其实内部也是调用了Builder的构造方法,而在其无参构造方法中对各个参数做了默认配置.两种方式指向了同一个方法build()

public OkHttpClient build() {
      return new OkHttpClient(this);
    }

Request

先看一下Requst内部:

public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Object tag;

  private volatile CacheControl cacheControl; // Lazily initialized.
 }

可以看到Request封装了url,请求头请求体等信息,内部同样有一个Builder类可以通过构造者模式来构建一个Request对象

Request request = new Request.Builder()
        .url(url)
        .get()
        .build();

Call

要获取Call对象需要通过OkHttpClient的newCall方法

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

可以看到newCall方法内部返回的实际是Call接口的实现类RealCall

Call.excute()&enqueue()

获取到Call对象后就可以调用excute或enqueue方法来发送请求,首先我们看源码

同步请求excute:

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

可以发现我们最想要的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);
  }

这里其实OkHttp使用责任链设计模式,将所有Interceptor添加到一个list中,做是否是WebSocket连接判断,然后将这个list作为参数构建RealInterceptorChain对象,最后调用其proceed()方法.
我们先暂时放下proceed方法,来了解一下什么是责任链模式:

  • 责任链模式(Chain of Responsibility Pattern),有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求.但是发出者并不清楚到底哪个对象会处理该请求,所以,责任链模式可以实现在隐藏客户端的情况下,对系统进行动态的调整.

实际在OkHttp中采用的是不纯的责任链模式,我们知道纯责任链模式是直到某一对象决定处理该请求,针对不同的请求做不同的操作,类似if-else,而不纯责任链模式根据一个请求每个对象都会处理该请求的一部分,处理完成随机将请求给下一个对象

结合代码来看,我们点进proceed方法(省略部分代码)

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    calls++;
    // 索引加一,创建调用下一个Interceptor的RealInterceptorChain对象.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    //获取当前Interceptor对象
    Interceptor interceptor = interceptors.get(index);
    //intercept方法内部调用下一个chain对象的proceed方法形成责任链
    Response response = interceptor.intercept(next);
    return response;
  }

可以看到proceed方法内部会获取当前index的Interceptor,调用intercept方法将同时创建的下一个Interceptor调用的chain对象作为参数传入,而在intercept方法内部除了对请求会做一些配置和处理外会继续调用传入chain对象的proceed方法,这样就使多个interceptor串联成了责任链,而在最后的CallServerInterceptor没有调用proceed方法,这样在它处理完成后response就会层层返回直到最开始的getResponseWithInterceptorChain()方法.这样整个请求完成我们就得到了最终的response.

现在我们来看下这整个Interceptor责任链的调用顺序及各个Interceptor的作用

  • RetryAndFollowUpInterceptor:处理请求错误并根据需要进行重定向
  • BridgeInterceptor:通过设置内容长度,编码,添加cookie,设置请求头,gzip压缩等来将客户端请求转化为服务器能读取的请求,再讲服务器返回的response转化为客户端能读取的response
  • CacheInterceptor:处理缓存
  • ConnectInterceptor:给目标打开一个链接
  • CallServerInterceptor:最后一个拦截器,对服务器进行访问

在这里插入图片描述

异步请求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));
  }

看一看到方法内部调用了dispatcher().enqueue(new AsyncCall(responseCallback)),这里我们点进dispatcher类先看下他的几个变量:

public final class Dispatcher {
  //最大并发请求数
  private int maxRequests = 64;
  //单个域名最大请求数
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  /** 内部线程池用于处理异步请求 */
  private @Nullable ExecutorService executorService;

  /** 就绪异步请求队列 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

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

  /** 正在执行的同步请求队列 */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

 }

其中runningSyncCalls我们在同步请求excute方法中其实看到有dispatcher.excute()和finish()方法,内部就是将同步请求添加到runningSyncCalls中进行管理,具体的请求及获取响应还是要走getResponseWithInterceptorChain(),所以我们关注点放在剩下两个请求队列和线程池,他们在源码中怎么使用我们接下来看enqueue方法:

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

这里会判断最大请求数和最大单个域名请求数,如果超过就会加入到readyAsyncCalls,否则加入runningAsyncCalls,并调用线程池execute方法,这个线程池设置只有非核心线程,最大线程数为Integer.MAX_VALUE,超时时长60s,其任务队列相当于一个空集合,所以任何任务都会立即执行,看代码其实Dispatcher内部线程池和Java的CacheThreadPool一模一样.
回到excute方法,他传入了AsyncCall参数,AsyncCall是RealCall的内部类,继承NamedRunnable抽象类,而NamedRunnable又实现了Runnable接口,看代码:

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

NamedRunnable重写run方法内部调用了抽象方法execute,而AsyncCall又重写了execute方法,现在经过一系列包装异步请求走到了AsyncCall的execute方法,可以看到内部还是调用了getResponseWithInterceptorChain()来获取response.

文章参考:
https://my.oschina.net/wangzhenchao/blog/745471/
https://blog.csdn.net/longyulu/article/details/9159589/
https://www.jianshu.com/p/e6fccf55ca01/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值