OkHttp(一) — OkHttp 调用流程分析

一、概述

OkHttp 版本: 3.14.7
官方文档: OkHttp官方文档
Github: OkHttp

OkHttp 是默认情况下高效的HTTP客户端:

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

二、OkHttp 的简单使用

这部分详细的使用可以参考官方文档 OkHttp - Recipes

OkHttp 提供了同步和异步的请求方法,下面我们介绍一下常用的 GetPost 请求的使用方法。

1. Get 同步请求

private final OkHttpClient client = new OkHttpClient();

public void get() {
	Request request = new Request.Builder()
		.url("https://publicobject.com/helloworld.txt")
      	.build();
	Response response = client.newCall(request).execute()
	if (response.isSuccessful()) {
   		System.out.println(response.body().string());
	}
}

2. Get 异步请求

private final OkHttpClient client = new OkHttpClient();

public void asynGet() {
	Request request = new Request.Builder()
      	.url("http://publicobject.com/helloworld.txt")
      	.build();

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

    	@Override 
   		public void onResponse(Call call, Response response) throws IOException {
   			if (response.isSuccessful()) {
   				System.out.println(response.body().string());
			}
    	}
  });
}

3. Post 同步请求

MediaType JSON = MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
	RequestBody body = RequestBody.create(json, JSON);
  	Request request = new Request.Builder()
      	.url(url)
      	.post(body)
      	.build();
	Response response = client.newCall(request).execute()
	if (response.isSuccessful()) {
   		System.out.println(response.body().string());
	}
}

三、OkHttp 源码分析

下面主要介绍一下以下几个类:

  1. OkHttpClient
  2. Dispatcher
  3. RealCall
  4. 拦截器的调用:getResponseWithInterceptorChain()

1. OkHttpClient 类

  1. OkHttpClient 类相当于一个门面类 (外观模式),可以配置很多参数,而 OkHttpClient 是通过 Builder 模式构建的。
  2. OkHttpClient 中可以配置很多参数,我们常用的几种如下面代码所示。
  3. OkHttpClient.newCall() 返回的对象 Call 可以操作 任务的执行任务的取消
OkHttpClient(Builder builder) {
	this.dispatcher = builder.dispatcher; //任务的调度器
	this.proxy = builder.proxy; 
	this.protocols = builder.protocols; //支持的网络协议(Http1、Http2)
	this.interceptors = Util.immutableList(builder.interceptors); //自定义的拦截器
	this.networkInterceptors = Util.immutableList(builder.networkInterceptors); //自定义的拦截器
	this.cache = builder.cache; //自定义的缓存
	this.dns = builder.dns;  //dns
	this.callTimeout = builder.callTimeout;	
	this.connectTimeout = builder.connectTimeout; //连接超时
	this.readTimeout = builder.readTimeout; //超时时间
	this.writeTimeout = builder.writeTimeout; //超时时间
	this.pingInterval = builder.pingInterval;
	// ...代码省略...
}

2. Dispatcher 类

在介绍后面的执行流程前,先介绍以下 Dispatcher 类中的几个变量。

// Dispatcher.class
private int maxRequests = 64; //同时执行的任务数
private int maxRequestsPerHost = 5; //每个主机最大请求数为5

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

// 返回同时在执行的任务数(同步+异步)
public synchronized int runningCallsCount() {
  	return runningAsyncCalls.size() + runningSyncCalls.size();
}

3. RealCall 类

RealCallCall 的实现类,Call 接口定义了请求的 执行、取消 等相关操作。

RealCall 对象的构建

// OkHttpClient.class
public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}
// RealCall.class
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  	// Safely publish the Call instance to the EventListener.
  	RealCall call = new RealCall(client, originalRequest, forWebSocket);
  	call.transmitter = new Transmitter(client, call); //这里构造了一个Transmitter对象
  	return call; //可以操作请求的执行和取消。
}
同步执行任务
// RealCall.class
// 这里是同步执行任务
public Response execute() throws IOException {
	synchronized (this) {
		if (executed) throw new IllegalStateException("Already Executed");
		executed = true;
	}
	transmitter.timeoutEnter();
	transmitter.callStart();
	try {
		//步骤1:只是将当前同步执行的任务加入队列(不做其他处理)。
	  	client.dispatcher().executed(this); 
	  	//步骤2:步骤1只将任务加入队列,不做任何处理,真正的请求是这一步执行的。
	  	return getResponseWithInterceptorChain(); 
	} finally {
		//步骤3:步骤2任务完成后,需要将步骤1中加入同步队列的任务移除。
	  	client.dispatcher().finished(this);
	}
}

// Dispatcher.class
synchronized void executed(RealCall call) {
	//因为OkHttp会控制当前最大允许执行的任务数,把这个同步任务加入队列,是便于后面控制并发数。
  	runningSyncCalls.add(call);
}

// Dispatcher.class
// 将同步请求的call从同步队列移除
void finished(RealCall call) {
  	finished(runningSyncCalls, call); //这里的代码看异步请求
}
异步执行任务
// RealCall.class
// 异步执行任务,所以需要将任务加入任务队列
public void enqueue(Callback responseCallback) {
	synchronized (this) {
	  if (executed) throw new IllegalStateException("Already Executed");
	  executed = true;
	}
	transmitter.callStart();
	// 将任务加入执行队列,AsyncCall是真正执行异步任务的地方。
	client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

// Dispatcher.class
void enqueue(AsyncCall call) {
  	synchronized (this) {
   	 	readyAsyncCalls.add(call); //这个队列是异步任务未执行的队列

    	// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
    	// the same host.
    	if (!call.get().forWebSocket) {
    		// 这里根据host去异步执行队列和异步等待队列里面匹配call
      		AsyncCall existingCall = findExistingCallWithHost(call.host());
      		// 如果匹配到,则将当前的call复用之前的callsPerHost
      		if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
    	}
  	}
  	//这里执行请求
  	promoteAndExecute();
}

// Dispatcher.class
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 个数。
        	if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        	// 每个host 最大连接数
        	if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

        	i.remove();
        	// 执行异步任务时,将当前关联的host个数自增1.
        	asyncCall.callsPerHost().incrementAndGet();
        	// 将当前的任务添加到执行任务集合中。
        	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的方法,executorService()是一个线程池,所以AsyncCall最终会在这个线程池内执行任务。
      	asyncCall.executeOn(executorService());
    }

    return isRunning;
}

AsyncCall 类

/**
 * 在介绍 AsyncCall 前,先来看一下 NamedRunnable 类,NamedRunnable 实现了 Runnable 接口。
 * AsyncCall 继承 NamedRunnable
 */
// NamedRunnable.class
public final void run() {
	String oldName = Thread.currentThread().getName();
	Thread.currentThread().setName(name);
	try {
		execute();  //子类执行
	} finally {
	  	Thread.currentThread().setName(oldName);
	}
}

// AsyncCall.class
void executeOn(ExecutorService executorService) {
	assert (!Thread.holdsLock(client.dispatcher()));
  	boolean success = false;
  	try {
  		//外面传入的任务最终在这里被线程池执行。
  		//执行这个方法之后,会执行NamedRunnable.run()方法,最终执行AsyncCall.execute()方法
    	executorService.execute(this); 
    	success = true;
  	} catch (RejectedExecutionException e) {
    	InterruptedIOException ioException = new InterruptedIOException("executor rejected");
    	ioException.initCause(e);
    	transmitter.noMoreExchanges(ioException);
    	responseCallback.onFailure(RealCall.this, ioException);
  	} finally {
    	if (!success) {
      	client.dispatcher().finished(this); // This call is no longer running!
    	}
  	}
}

// AsyncCall.class
protected void execute() {
	boolean signalledCallback = false;
	transmitter.timeoutEnter();
	try {
		// 执行到这里,就跟同步方法里一样了,这里是最终执行网络请求的操作。
	  	Response response = getResponseWithInterceptorChain();
	  	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);
	  	}
	} catch (Throwable t) {
	  	cancel();
	  	if (!signalledCallback) {
	    	IOException canceledException = new IOException("canceled due to " + t);
	    	canceledException.addSuppressed(t);
	    	responseCallback.onFailure(RealCall.this, canceledException);
	  	}
	  	throw t;
	} finally {
		// 执行完后,需要将当前执行的任务从runningAsyncCalls队列移除。
	  	client.dispatcher().finished(this);
	}
}

void finished(AsyncCall call) {
  call.callsPerHost().decrementAndGet(); //将perHost关联的数量减1,一个perHost最多只能关联5个call
  finished(runningAsyncCalls, call);
}


private <T> void finished(Deque<T> calls, T call) {
  	Runnable idleCallback;
  	synchronized (this) {
  		// 将指定的call从指定的队列中移除。
   		if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
    	idleCallback = this.idleCallback;
  	}
  	//在释放上个请求的同时,重新去readyAsyncCalls队列获取请求,直到队列中没有请求为止。
  	boolean isRunning = promoteAndExecute(); 
  	if (!isRunning && idleCallback != null) {
    	idleCallback.run();
  	}
}

4. getResponseWithInterceptorChain()

getResponseWithInterceptorChain() 方法中添加了一系列的拦截器,这些拦截器实现了网络数据的获取。

  1. 这一系列拦截器怎么执行呢?
    这里通过 RealInterceptorChain 类将他们串行执行 (责任链模式)。
// RealCall.class
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
	//这里添加了一系列的拦截器
    List<Interceptor> interceptors = new ArrayList<>();
    //添加用户应用层自定义的Interceptor
    interceptors.addAll(client.interceptors());
    //这个Interceptor是处理请求失败的重试,重定向  
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    //这个Interceptor工作是添加一些请求的头部或其他信息
    //并对返回的Response做一些友好的处理(有一些信息你可能并不需要)
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //这个Interceptor的职责是判断缓存是否存在,读取缓存,更新缓存等等
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //这个Interceptor的职责是建立客户端和服务器的连接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
    	//添加用户自定义的网络层拦截器
      	interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));
	//一个包裹这request的chain
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        	originalRequest, this, client.connectTimeoutMillis(),
        	client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
    	// 请求发出后获取到的响应数据
      	Response response = chain.proceed(originalRequest);
      	if (transmitter.isCanceled()) {
        	closeQuietly(response);
        	throw new IOException("Canceled");
      	}
      	return response;
    } catch (IOException e) {
      	calledNoMoreExchanges = true;
      	throw transmitter.noMoreExchanges(e);
    } finally {
      	if (!calledNoMoreExchanges) {
        	transmitter.noMoreExchanges(null);
      	}
    }
}

// RealInterceptorChain.class
public Response proceed(Request request) throws IOException {
    return proceed(request, transmitter, exchange);
}
// RealInterceptorChain.class
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
    // ...省略代码...
    
    // interceptors表示之前的一串拦截器,index从0开始,proceed()方法每被执行一次,index都会增加1,所以相当于会获取到下一个拦截器(实现了拦截器的串行)。
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // ...省略代码...
    return response;
}

// RetryAndFollowUpInterceptor.class
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    // ...省略代码...
    // 这里会执行下一个
	Response response = realChain.proceed(request, transmitter, null);
    // ...省略代码...
}

四、小结

1. OkHttp 请求数据流程

  1. 通过 Builder 模式 构建一个 OkHttpClient 对象,用于请求数据。
  2. 通过 newRealCall() 方法构建一个 RealCall 对象,用于任务的调度(任务调度可以分为:同步、异步)。
  3. 使用 责任链模式,实现了任务的分步处理。并对外提供了 interceptors 拦截和 networkInterceptors 拦截器功能,实现了对请求数据和响应数据的操作。

在这里插入图片描述

2. OkHttp 涉及的设计模式

序号模式应用场景
1Builder 模式构建 OkHttpClient
2工厂模式OkHttpClient 中构建 Call 对象
3责任链模式RetryAndFollowUpInterceptor 拦截器
4策略模式缓存
5生产者消费者模式添加异步请求任务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值