OkHttp 流程浅析 - NoHarry的博客

Posted wangziqiang123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OkHttp 流程浅析 - NoHarry的博客相关的知识,希望对你有一定的参考价值。

简介

本文通过结合OkHttp源码,分析发送请求的大致流程。

  • 本文源码基于3.12.0版本

示例

首先我们创建一个最简单的请求,以此为例开始进行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
OkHttpClient client=new OkHttpClient.Builder().build();
//创建Request
Request request=new Request
.Builder()
.url(url)
.build();
//发送一个异步请求
client.newCall(request).enqueue(new Callback()
public void (Call call, IOException e)
public void onResponse(Call call, Response response) throws IOException
);
//发送一个同步请求
try
Response response = client.newCall(request).execute();
catch (IOException e)
e.printStackTrace();

流程

1.1 创建请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final class Request
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
private volatile @Nullable CacheControl cacheControl;
Request(Builder builder)
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
...
public static class Builder
@Nullable HttpUrl url;
String method;
Headers.Builder headers;
@Nullable RequestBody body;
Map<Class<?>, Object> tags = Collections.emptyMap();
....

首先使用建造者模式构建一个Requst,来插入请求的数据。

1.2 封装请求

请求封装在了接口Call的实现类RealCall中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
final class RealCall implements Call
...
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket)
//构建的OkHttpClient
this.client = client;
//用户构建的Request
this.originalRequest = originalRequest;
//是不是WebSocket请求
this.forWebSocket = forWebSocket;
//构建RetryAndFollowUpInterceptor拦截器
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
//Okio中提供的用于超时机制的方法
this.timeout = new AsyncTimeout()
protected void timedOut()
cancel();
;
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
...

1.3 执行请求

请求分为同步请求异步请求:

  • 同步请求:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public Response execute() throws IOException
    synchronized (this)
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
    captureCallStackTrace();
    timeout.enter();
    eventListener.callStart(this);
    try
    client.dispatcher().executed(this);
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
    catch (IOException e)
    e = timeoutExit(e);
    eventListener.callFailed(this, e);
    throw e;
    finally
    client.dispatcher().finished(this);
  • 异步请求:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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中进行处理,

区别在于:

  • 同步请求:直接执行executed,并返回结果
  • 异步请求:构造一个AsyncCall,并将其加入到readyAsyncCalls这个准备队列中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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;
/**
* Attempt to enqueue this async call on @code executorService. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService)
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try
executorService.execute(this);
success = true;
catch (RejectedExecutionException e)
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
finally
if (!success)
client.dispatcher().finished(this); // This call is no longer running!
@Override protected void execute()
boolean signalledCallback = false;
timeout.enter();
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)
e = timeoutExit(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);

AsyncCall继承自NamedRunnable,而NamedRunnable可以看成一个会给其所运行的线程设定名字的Runnable,Dispatcher会通过ExecutorService来执行这些Runnable。

1.4 请求的调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public final class Dispatcher
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** 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<>();
void enqueue(AsyncCall call)
synchronized (this)
readyAsyncCalls.add(call);
promoteAndExecute();
synchronized void executed(RealCall call)
runningSyncCalls.add(call);
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();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
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.executeOn(executorService());
return isRunning;

请求的调度主要在Dispatcher类中进行,其中维护了3个双端队列:

  • readyAsyncCalls:准备队列用于添加准备执行的异步请求。
  • runningAsyncCalls:正在执行的异步请求队列。
  • runningSyncCalls:正在执行的同步请求队列。

对于同步请求,Dispatcher会直接将请求加入到同步请求队列执行;对于异步请求首先会将请求加入readyAsyncCalls中,接下来会遍历readyAsyncCalls判断如果当前执行的异步请求数量小于65并且同一host下的异步请求数小于5,则将readyAsyncCalls中的请求加入到runningAsyncCalls开始执行并从readyAsyncCalls中移除。

1.5 请求的执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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最核心的部分就是拦截器的这部分,这里采用责任链的设计模式,使各个功能充分解耦,各司其职,请求从用户自定义的拦截器开始层层传递到CallServerInterceptor,每层做出相应的处理,直到请求发出,与此同时,返回的响应从CallServerInterceptor开始逐层上传直到用户的自定义拦截器,每层都会对返回的响应做出相应处理,最终将处理好的响应结果返回给用户。


原文:大专栏  OkHttp 流程浅析 - NoHarry的博客


以上是关于OkHttp 流程浅析 - NoHarry的博客的主要内容,如果未能解决你的问题,请参考以下文章

okHttp3

okHttp3

深入理解OkHttp源码——提交请求

Retrofit的浅析 —— 针对面试

Java NIO:浅析I/O模型

浅析Scrapy框架运行的基本流程