OkHttp系列三、OkHttp同步请求流程和源码分析

同样的,第一步肯定是创建OkHttpClient客户端:

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

我们先看Builder()构造方法:

public Builder() {
	dispatcher = new Dispatcher();
	protocols = DEFAULT_PROTOCOLS;
	connectionSpecs = DEFAULT_CONNECTION_SPECS;
	eventListenerFactory = EventListener.factory(EventListener.NONE);
	proxySelector = ProxySelector.getDefault();
	cookieJar = CookieJar.NO_COOKIES;
	socketFactory = SocketFactory.getDefault();
	hostnameVerifier = OkHostnameVerifier.INSTANCE;
	certificatePinner = CertificatePinner.DEFAULT;
	proxyAuthenticator = Authenticator.NONE;
	authenticator = Authenticator.NONE;
	connectionPool = new ConnectionPool();
	dns = Dns.SYSTEM;
	followSslRedirects = true;
	followRedirects = true;
	retryOnConnectionFailure = true;
	connectTimeout = 10*000;
	readTimeout = 10*000;
	writeTimeout = 10*000;
	pingInterval = 0;
}

在这个方法中,初始化了许多的参数,其中:

Dispatcher:在异步请求中,它决定这时立即处理还是缓存等待,并在有结果之后进行线程的切换。在同步请求中,他仅仅是把请求加入到队列当中,不做太多的操作。

connectionPool = new ConnectionPool():connectionPool是一个连接池,他统一对我们发起的网络请求进行管理,当我们请求的URL相同的时候,他就会知道这个URL请求是可以复用的,不用再创建。然后还实现了对网络连接状态的处理和管理:那些网络请求可以保持打开状态,那些请求是可以复用的。

第二步,我们再来看看Request报文请求对象的创建:

Request request = new Request.Builder().addHeader(key, value).build();

他也是使用建造者模式创建的,我们先看看他的Builder的构造方法Builder():

public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
}

只有两行有效代码,一是指定请求方式“”GET,二是创建请求头的对象,方便我们添加header头文件。当然他的Request有参构造方法就复杂些了:

Builder(Request request) {
	this.url = request.url;
	this.method = request.method;
	this.body = request.body;
	this.tag = request.tag;
	this.headers = request.headers.newBuilder();
}

其实也比较简单。主要就是设置几个常用的参数:URL请求连接,请求方法,GET还是POST,设置请求标识,设置请求头。
然后他的.Build()方法源码如下:

public Request build() {
	if (url == null) throw new IllegalStateException("url == null");
	return new Request(this);
}

就是创建一个Request对象,并把我们前面给Builder设置的各种参数传进去

第三步,就是创建Call对象:

Call call = client.newCall(mRequest);

newCall()方法如下:

public Call newCall(Request request) {
	return RealCall.newRealCall(this, request, false);
}

在这里,我们要重点关注的是RealCall这个对象,他的构造方法如下:

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
	RealCall call = new RealCall(client, originalRequest, forWebSocket);
	call.eventListener = client.eventListenerFactory().create(call);
	return call;
}

第一个操作是调用RealCall的有参构造,创建RealCall对象:

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
	this.client = client;
	this.originalRequest = originalRequest;
	this.forWebSocket = forWebSocket;
	this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}

这里面,他持有前面两步创建的OkHttpClient客户端对象和Request对象。并创建了一个RetryAndFollowUpInterceptor拦截器对象。

第二个操作是给Call对象设置事件监听。

这里我们需要注意的是,这里的三个步骤,其实无论是同步请求还是异步请求,都是一样的。区别在于第四步:通过Call调用请求方法:execute()/enqueue().

这里我们先看看execute()方法的源码:
Call是一个接口,execute()/enqueue()方法的具体实现由 他的实现类RealCall来实现:

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

这是一个用synchronized加了锁的线程安全的方法,锁里面的if判断的标识executed 意思是同一个请求只会执行一次,反复调用的时候,第一次调用的时候就会执行executed=true,然后后面在调用的时候,就回你抛出new IllegalStateException(“Already Executed”)异常。

然后会调用eventListener.callStart(this)开始一个监听事件,无论调用execute()还是enqueue()都会开启这个监听。

然后就是调用client.dispatcher().executed(this),作用是:

synchronized void executed(RealCall call) {
	runningSyncCalls.add(call);
}

也就是把call请求添加到请求队列中去。runningSyncCalls对象是这样的:

Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

然后就是Response result = getResponseWithInterceptorChain();这个里面就是根据拦截器设定的规则拦截掉不需要的信息,然后返回我们需要的结果。

Response getResponseWithInterceptorChain() throws IOException {
	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);
	}
}

当我们的请求技术后,会执行finally里面的结果:目的是回收我们的请求,以便复用或者清理状态

finally {
	client.dispatcher().finished(this);
}

Finished()方法主要干了这些事情:

void finished(RealCall call) {
	finished(runningSyncCalls, call, false);
}

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

在synchronized同步代码块里面,会首先移除已经执行完毕的call,如果不能移除,就会抛出异常,然后判断promoteCalls参数,由于在同步请求里面promoteCalls=false,所以promoteCalls()不会执行,但是在异步请求里面,promoteCalls=true,会执行promoteCalls()方法。然后再调用runningCallsCount()方法计算当前队列里面正在执行的请求call的数量:

public synchronized int runningCallsCount() {
	return runningAsyncCalls.size() + runningSyncCalls.size();
}

最后再把正在执行的idleCallback线程重新赋值一份。用于后面的判断。最后再执行最后的if判断:如果正在执行的请求书为0,也就是我们的dispatcher分发器里面已经没有可运行的请求了,同时idleCallback不为空的时候,调用run方法,结束请求。

其实dispatcher在同步请求里面作用很简单:把call请求添加到队列,完成后再把call请求移除队列。Dispatcher主要作用在异步请求里面。

原创文章 118 获赞 149 访问量 9万+

猜你喜欢

转载自blog.csdn.net/haoyuegongzi/article/details/103001157