android okhttp 源码解析,OkHttp 知识梳理(1) - OkHttp 源码解析之入门

OkHttp 知识梳理系列文章

一、简介

OkHttp无疑是目前使用的最多的网络框架,现在最火的Retrofit也就是基于OkHttp来实现的,和以前一样,我们将从最简单的例子开始,一步步剖析OkHttp的使用及内部实现原理。

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中声明网络权限:

最后,我们用一个简单的例子演示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);

}

}

}

运行结果为:

22889e2103ee

运行结果

三、简要流程分析

是不是很简单,仅仅几句话就完成了网络请求,核心的四个步骤为:

构建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 interceptors = new ArrayList();

final List 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 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 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 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之前:

22889e2103ee

List

接着创建了一个RealInterceptorChain对象,它的构造函数的第1参数就是上面List列表,除此之外还需要注意第5个参数为0,这个对于下面的分析很重要,最后就是调用了RealInterceptorChain的proceed方法,其实参就是前面创建的Request对象,为了便于理解,我们再看一下RealInterceptorChain:

22889e2103ee

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

}

整个递归调用的过程为:

22889e2103ee

递归调用结果

在整个递归调用过程中,如果有任意一个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 void finished(Deque 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 知识梳理系列:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值