OkHttp 知识梳理系列文章
OkHttp 知识梳理(1) - OkHttp 源码解析之入门
OkHttp 知识梳理(2) - OkHttp 源码解析之异步请求 & 线程调度
OkHttp 知识梳理(3) - OkHttp 之缓存基础
一、简介
OkHttp
无疑是目前使用的最多的网络框架,现在最火的Retrofit
也就是基于OkHttp
来实现的,和以前一样,我们将从最简单的例子开始,一步步剖析OkHttp
的使用及内部实现原理。
OkHttp
项目相关文档
- 官网地址:http://square.github.io/okhttp/
- 源码地址:https://github.com/square/okhttp
-
API
文档:http://square.github.io/okhttp/3.x/okhttp/
二、OKHttp 的使用
2.1 应用背景
我们首先用一个简单的例子来演示OkHttp
的简单使用 - 通过中国天气网提供的官方API
获取城市的天气,完整的代码可以查看我的Github
仓库 RepoOkHttp 中第一章的例子。
2.2 引入依赖
目前官网的最新版本为3.9.1
,因此我们需要在build.gradle
文件中进行声明:
compile 'com.squareup.okhttp3:okhttp:3.9.1'
不要忘了在AndroidManifest.xml
中声明网络权限:
<uses-permission android:name="android.permission.INTERNET" />
最后,我们用一个简单的例子演示OkHttp
的同步请求,首先,通过HandlerThread
创建一个异步线程,通过发送消息的形式,通知它发起网络请求,请求完毕之后,将结果中的body
部分发送回主线程用TextView
进行展示:
public class SimpleActivity extends AppCompatActivity {
private static final String URL = "http://www.weather.com.cn/adat/sk/101010100.html";
private static final int MSG_REQUEST = 0;
private static final int MSG_UPDATE_UI = 0;
private Button mBtRequest;
private Button mBtRequestAsync;
private TextView mTvResult;
private BackgroundHandler mBackgroundHandler;
private MainHandler mMainHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
mBtRequest = (Button) findViewById(R.id.bt_request_sync);
mBtRequestAsync = (Button) findViewById(R.id.bt_request_async);
mTvResult = (TextView) findViewById(R.id.tv_result);
mBtRequest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startSyncRequest();
}
});
mBtRequestAsync.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAsyncRequest();
}
});
HandlerThread backgroundThread = new HandlerThread("backgroundThread");
backgroundThread.start();
mBackgroundHandler = new BackgroundHandler(backgroundThread.getLooper());
mMainHandler = new MainHandler();
}
/**
* 同步发起请求的例子。
*/
private void startSyncRequest() {
//发送消息到异步线程,发起请求。
mBackgroundHandler.sendEmptyMessage(MSG_REQUEST);
}
/**
* 异步发起请求的例子。
*/
private void startAsyncRequest() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(URL).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
//返回结果给主线程。
Message message = mMainHandler.obtainMessage(MSG_UPDATE_UI, result);
mMainHandler.sendMessage(message);
}
});
}
private class BackgroundHandler extends Handler {
BackgroundHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//在异步线程发起请求。
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(URL).build();
Call call = client.newCall(request);
try {
Response response = call.execute();
String result = response.body().string();
//返回结果给主线程。
Message message = mMainHandler.obtainMessage(MSG_UPDATE_UI, result);
mMainHandler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//主线程获取到结果之后进行更新。
String result = (String) msg.obj;
mTvResult.setText(result);
}
}
}
运行结果为:
三、简要流程分析
是不是很简单,仅仅几句话就完成了网络请求,核心的四个步骤为:
- 构建
OkHttpClient
对象 - 构建
Request
对象 - 由前两步创建的
OkHttpClient
和Request
对象创建Call
对象 - 通过
Call
对象发起请求,并得到一个Response
,它就是最终返回的结果。
//1.构建 OkHttpClient 对象
OkHttpClient client = new OkHttpClient();
//2.构建 Request 对象。
Request request = new Request.Builder().url(URL).build();
//3.由 OkHttpClient 通过 Request 创建 Call 对象
Call call = client.newCall(request);
try {
//4.通过 Call 对象发起请求
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
3.1 构建 OkHttpClient 对象
OkHttpClient
提供了许多配置项供使用者进行设置,例如缓存、Cookie
、超时时间等等,对于其中参数的初始化可以采用建造者模式,其源码地址为 OkHttpClient.java,例如:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(3000, TimeUnit.MILLISECONDS)
.readTimeout(3000, TimeUnit.MILLISECONDS);
OkHttpClient client = builder.build();
如果我们什么也不做,像最开始的那样直接new OkHttpClient()
,那么将会采用默认的配置,部分配置如下,关于各个参数的含义可以参考:OkHttpClient.Builder。
//(1) Sets the dispatcher used to set policy and execute asynchronous requests.
this.dispatcher = new Dispatcher();
//(2) Configure the protocols used by this client to communicate with remote servers
this.protocols = OkHttpClient.DEFAULT_PROTOCOLS;
//(3) Unknow
this.connectionSpecs = OkHttpClient.DEFAULT_CONNECTION_SPECS;
//(4) Configure a factory to provide per-call scoped listeners that will receive analytic events for this client
this.eventListenerFactory = EventListener.factory(EventListener.NONE);
//(5) Sets the proxy selection policy to be used if no is specified explicitly
this.proxySelector = ProxySelector.getDefault();
//(6) Sets the handler that can accept cookies from incoming HTTP responses and provides cookies to outgoing HTTP requests
this.cookieJar = CookieJar.NO_COOKIES;
//(7) Sets the socket factory used to create connections
this.socketFactory = SocketFactory.getDefault();
//(8) Sets the verifier used to confirm that response certificates apply to requested hostnames for HTTPS connections
this.hostnameVerifier = OkHostnameVerifier.INSTANCE;
//(9) Sets the certificate pinner that constrains which certificates are trusted
this.certificatePinner = CertificatePinner.DEFAULT;
//(10) Sets the authenticator used to respond to challenges from proxy servers
this.proxyAuthenticator = Authenticator.NONE;
//(11) Sets the authenticator used to respond to challenges from origin servers
this.authenticator = Authenticator.NONE;
//(12) Sets the connection pool used to recycle HTTP and HTTPS connections
this.connectionPool = new ConnectionPool();
//(13) ets the DNS service used to lookup IP addresses for hostnames
this.dns = Dns.SYSTEM;
//(14) Configure this client to follow redirects from HTTPS to HTTP and from HTTP to HTTPS
this.followSslRedirects = true;
//(15) Configure this client to follow redirects
this.followRedirects = true;
//(16) Configure this client to retry or not when a connectivity problem is encountered
this.retryOnConnectionFailure = true;
//(17) TimeOut
this.connectTimeout = 10000;
this.readTimeout = 10000;
this.writeTimeout = 10000;
//(18) Sets the interval between web socket pings initiated by this client
this.pingInterval = 0;
上面的参数很多,后面用到的时候再去分析,这里我们主要关注两个重要的成员变量,它们是Interceptor
类型元素的列表,在后面我们将会花很大的篇幅来介绍它:
final List<Interceptor> interceptors = new ArrayList();
final List<Interceptor> networkInterceptors = new ArrayList();
3.2 构建 Request
OkHttpClient
用于全局的参数配置,一般来说,一个进程中拥有一个OkHttpClient
对象即可。而Request
则对应于一个请求的具体信息,每发起一次请求,就需要创建一个新的Request
对象,其配置信息包括请求的url
、method
、headers
、body
等等,这些都是HTTP
的基础知识,推荐大家看一下这篇文章 一篇文章带你详解 HTTP 协议(网络协议篇一),总结得很全面。
Request
中的参数也可以通过建造者模式来进行配置,其源码地址为 Request.java。
3.3 构建 Call
经过前面两步我们得到了OkHttpClient
和Request
这两个实例,接下来就需要创建请求的具体执行者:
Call call = client.newCall(request);
newCall
的代码很简单,其实就是通过RealCall
的静态方法返回了一个RealCall
对象,并持有OkHttpClient
和Request
的引用,同时它实现了Call
接口:
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false);
}
Call
接口的定义如下,它定义了请求的接口:
public interface Call extends Cloneable {
//返回原始的请求对象。
Request request();
//同步发起请求。
Response execute() throws IOException;
//异步发起请求。
void enqueue(Callback var1);
//取消请求。
void cancel();
//请求是否已经被执行。
boolean isExecuted();
//请求是否取消。
boolean isCanceled();
//clone 对象。
Call clone();
//工厂类。
public interface Factory {
Call newCall(Request var1);
}
}
当使用RealCall
发起请求时,有同步和异步两种方式,分别对应于.execute
和.enqueue
两个方法,这里我们先将 同步的方法,因为 异步 也是建立在它的基础之上的。
3.4 同步发起请求 - execute()
//[同步请求的函数]
public Response execute() throws IOException {
synchronized(this) {
//如果已经发起过请求,那么直接跑出异常。
if(this.executed) {
throw new IllegalStateException("Already Executed");
}
//标记为已经发起过请求。
this.executed = true;
}
//捕获这个请求的 StackTrace。
this.captureCallStackTrace();
//通知监听者已经开始请求。
this.eventListener.callStart(this);
Response var2;
try {
//通过 OkHttpClient 的调度器执行请求。
this.client.dispatcher().executed(this);
//getResponseWithInterceptorChain() 责任链模式。
Response result = this.getResponseWithInterceptorChain();
if(result == null) {
throw new IOException("Canceled");
}
var2 = result;
} catch (IOException var7) {
//通知监听者发生了异常。
this.eventListener.callFailed(this, var7);
throw var7;
} finally {
//通过调度器结束该任务。
this.client.dispatcher().finished(this);
}
//返回结果。
return var2;
}
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList();
interceptors.addAll(this.client.interceptors());
interceptors.add(this.retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
interceptors.add(new CacheInterceptor(this.client.internalCache()));
interceptors.add(new ConnectInterceptor(this.client));
if(!this.forWebSocket) {
interceptors.addAll(this.client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(this.forWebSocket));
Chain chain = new RealInterceptorChain(interceptors, (StreamAllocation)null, (HttpCodec)null, (RealConnection)null, 0, this.originalRequest, this, this.eventListener, this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis());
return chain.proceed(this.originalRequest);
}
整个通过请求分为以下几步:
3.4.1 将 Call 任务加入到同步队列当中
this.client.dispatcher().executed(this);
这里的执行并不是真正的执行,默认情况下调度器的实现为Dispatcher
,它的executed
方法实现为:
//同步队列。
private final Deque<RealCall> runningSyncCalls = new ArrayDeque();
//Dispatcher.executed 仅仅是将 Call 加入到队列当中,而并没有真正执行。
synchronized void executed(RealCall call) {
this.runningSyncCalls.add(call);
}
3.4.2 执行请求
Response result = this.getResponseWithInterceptorChain()
真正地触发了请求的执行是上面这句,我们来简单看一下getResponseWithInterceptorChain
是怎么触发请求的。
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList();
//先加入使用者自定义的拦截器。
interceptors.addAll(this.client.interceptors());
//加入标准的拦截器。
interceptors.add(this.retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
interceptors.add(new CacheInterceptor(this.client.internalCache()));
interceptors.add(new ConnectInterceptor(this.client));
if(!this.forWebSocket) {
interceptors.addAll(this.client.networkInterceptors());
}
//访问服务器的拦截器。
interceptors.add(new CallServerInterceptor(this.forWebSocket));
//创建调用链,注意第五个参数目前的值为0。
Chain chain = new RealInterceptorChain(interceptors, (StreamAllocation)null, (HttpCodec)null, (RealConnection)null, 0, this.originalRequest, this, this.eventListener, this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis());
//执行调用链的 proceed 方法。
return chain.proceed(this.originalRequest);
}
这里我们将一系列的Interceptor
加入到了List
当中,构建完之后,List
中的内容如下所示,对于使用者自定义的interceptors
将会加在列表的头部,而自定义的networkInterceptors
则会加在CallServerInterceptor
之前:
接着创建了一个
RealInterceptorChain
对象,它的构造函数的第1
参数就是上面List<Interceptor>
列表,除此之外还需要注意第5
个参数为0
,这个对于下面的分析很重要,最后就是调用了RealInterceptorChain
的proceed
方法,其实参就是前面创建的Request
对象,为了便于理解,我们再看一下RealInterceptorChain
:
接下来,看一下 最关键的 RealInterceptorChain
的proceed
中的逻辑:
//RealInterceptorChain 的 proceed 方法。
public Response proceed(Request request) throws IOException {
return this.proceed(request, this.streamAllocation, this.httpCodec, this.connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
if(this.index >= this.interceptors.size()) {
throw new AssertionError();
} else {
++this.calls;
if(this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must retain the same host and port");
} else if(this.httpCodec != null && this.calls > 1) {
throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must call proceed() exactly once");
} else {
//关键部分的代码是这几句。
//(1) 创建一个新的 RealInterceptorChain 对象,这里注意前面说的第5个参数变成了 index+1。
RealInterceptorChain next = new RealInterceptorChain(this.interceptors, streamAllocation, httpCodec, connection, this.index + 1, request, this.call, this.eventListener, this.connectTimeout, this.readTimeout, this.writeTimeout);
//(2) 取列表中位于 index 位置的拦截器。
Interceptor interceptor = (Interceptor)this.interceptors.get(this.index);
//(3) 调用它的 intercept 方法,并传入新创建的 RealInterceptorChain。
Response response = interceptor.intercept(next);
if(httpCodec != null && this.index + 1 < this.interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once");
} else if(response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
} else if(response.body() == null) {
throw new IllegalStateException("interceptor " + interceptor + " returned a response with no body");
} else {
return response;
}
}
}
}
忽略不重要的代码,RealInterceptorChain
关键的代码有三个步骤:
- 创建一个新的
RealInterceptorChain
对象,这里注意前面说的第5
个参数变成了index+1
- 取列表中位于
index
位置的拦截器。 - 调用它的
intercept
方法,并传入新创建的RealInterceptorChain
而每个Interceptor
在执行完它的操作之后,就会调用RealInterceptorChain
的proceed
方法,使得下一个Interceptor
的intercept
方法可以被执行,以第一个拦截器RetryAndFollowUpInterceptor
为例:
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
this.streamAllocation = new StreamAllocation(this.client.connectionPool(), this.createAddress(request.url()), call, eventListener, this.callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
//这是一个 While 循环,知道没有达到终止的条件就一直重试。
while(!this.canceled) {
boolean releaseConnection = true;
Response response;
try {
//调用下一个拦截器。
response = realChain.proceed(request, this.streamAllocation, (HttpCodec)null, (RealConnection)null);
releaseConnection = false;
} catch (RouteException var16) {
if(!this.recover(var16.getLastConnectException(), false, request)) {
throw var16.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException var17) {
boolean requestSendStarted = !(var17 instanceof ConnectionShutdownException);
if(!this.recover(var17, requestSendStarted, request)) {
throw var17;
}
releaseConnection = false;
continue;
} finally {
if(releaseConnection) {
this.streamAllocation.streamFailed((IOException)null);
this.streamAllocation.release();
}
}
if(priorResponse != null) {
response = response.newBuilder().priorResponse(priorResponse.newBuilder().body((ResponseBody)null).build()).build();
}
Request followUp = this.followUpRequest(response);
if(followUp == null) {
if(!this.forWebSocket) {
this.streamAllocation.release();
}
//返回了 response,那么整个调用就结束了。
return response;
}
//....
}
this.streamAllocation.release();
throw new IOException("Canceled");
}
整个递归调用的过程为:
在整个递归调用过程中,如果有任意一个Interceptor
的intercept
方法返回了而没有调用proceed
方法,那么整个调用将会结束,排在它之后的Interceptor
将不会被执行。
CallServerInterceptor
CallServerInterceptor
是最后一个Interceptor
,与之前的拦截器不同,在它的intercept
方法中 不会创建一个新的 RealInterceptorChain ,而是直接返回了Response,使得整个递归调用一步步向上返回。
public Response intercept(Chain chain) throws IOException {
//发起请求..
Response response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
realChain.eventListener().responseHeadersEnd(realChain.call(), response);
int code = response.code();
if(this.forWebSocket && code == 101) {
response = response.newBuilder().body(Util.EMPTY_RESPONSE).build();
} else {
response = response.newBuilder().body(httpCodec.openResponseBody(response)).build();
}
//只有两种选择,抛出异常或者返回结果,不会进行下一步的调用。
if((code == 204 || code == 205) && response.body().contentLength() > 0L) {
throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
} else {
return response;
}
}
3.4.3 结束任务
回到最开始的代码,当getResponseWithInterceptorChain
返回之后,最后通过dispatcher.finish(RealCall call)
方法结束任务:
void finished(RealCall call) {
this.finished(this.runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized(this) {
//从 runningSyncCalls 移除它。
if (!calls.remove(call)) {
throw new AssertionError("Call wasn't in-flight!");
}
//false 不执行。
if (promoteCalls) {
this.promoteCalls();
}
runningCallsCount = this.runningCallsCount();
idleCallback = this.idleCallback;
}
//如果当前已经没有可以执行的任务,那么调用 idleCallback.run() 方法。
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
public synchronized int runningCallsCount() {
return this.runningAsyncCalls.size() + this.runningSyncCalls.size();
}
四、小结
可以看到,对于 同步请求,这个函数的调用都是在.execute()
调用的线程执行的,其实 异步请求 的核心逻辑和同步请求是相同的,只不过加入了线程的管理。不知不觉说得又有点长了,还是把 异步请求 的分析放在下一篇文章里面讲吧,顺便结合OkHttp
中对于线程的管理,这一章只是一个入门,关键是让大家对整个OkHttp
请求的流程有个大概的印象,特别是调用链的模式。
更多文章,欢迎访问我的 Android 知识梳理系列:
- Android 知识梳理目录://www.greatytc.com/p/fd82d18994ce
- Android 面试文档分享://www.greatytc.com/p/8456fe6b27c4