您的位置:首页 > 理论基础 > 计算机网络

Okhttp3使用 + 源码完全解析

2017-09-21 20:09 302 查看
在使用过okhttp3之后,必然的一步当是对源码的研究 这样可以对其优劣和功能封装有一个全面详尽的了解

ok 下面粘贴okhttp3的核心代码(url暂时随意定义)

OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("www.baidu.com")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {

}
});


下面进入正题,来对源码进行分析

1.okhttp实例 和Request实例和配置

相当于初始化实例 比较简单

首先来看okhttp初始化实例

1.1 //OkHttpClient okHttpClient = new OkHttpClient();

public OkHttpClient() {
this(new 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;
}


其实就是初始化对象 和属性的相关配置过程

1.2Requst的初始化

Request request = new Request.Builder()
.url("www.baidu.com")
.build();


下面来看其源码:

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

public Builder url(String url) {
if (url == null) throw new NullPointerException("url
4000
== null");

// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}

HttpUrl parsed = HttpUrl.parse(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
return url(parsed);
}

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


到这里准备工作就完成了

下面来从请求开始详细看源码(以上比较简单 没有什么可以讲的)

2.请求处理

//Call call = okHttpClient.newCall(request);

@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}

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.eventListener = client.eventListenerFactory().create(call);
return call;
}


okhttp3创建请求的主体call对象 其实返回的是一个realCall的实例化对象,并完成一系列的相关初始化配置;

下面来看请求

//call.enqueue

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


由以上代码 可以看出来 okhttp的请求是由dispatcher来完成的;而dispatcher又是什么呢 ?

其实是一个网络的任务调度器;

那么下面来看下调度器的实现.

3.dispatcher任务调度

//最大请求数
private int maxRequests = 64;
//最大主机请求数
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
//消费者线程池
private @Nullable ExecutorService executorService;
//将要运行的异步请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在运行的异步请求队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在运行的同步请求队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}

public Dispatcher() {
}


Dispatcher主要用于控制并发 并且维护了一部分变量

在请求之前dispatcher会自己创建线程池

当然也可以有程序员自己来创建线程池

ok~初步了解调度器之后来看异步请求的源码

synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}


在调度器的enqueue中会先判断当前最大请求数和当前最大主机请求数 如果不超过默认的最大值 则吧请求加入到正在运行的请求队列—->runningAsyncCalls;

否则会加入到将要运行的请求队列中进行等待—->readyAsyncCalls

然后 executorService().execute(call);开始运行传入的线程 即传入的AsyncCall;而AsyncCall作为RealCall的内部实现类 会走execute;

ok下面来看AsyncCall–>execute ,在这里 开始进行网络请求:

@Override protected void execute() {
boolean signalledCallback = false;
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) {
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);
}
}
}


在上面代码中不难看出走了一系列方法 来进行网络请求 为了方便读者理解 先来看finally下的方法 (这个方法必然会走到的):

void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}

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

//####promoteCalls()
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();

if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}

if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}


直接提取关键代码:

finished—->if (promoteCalls) promoteCalls(); 当请求完之后 promoteCalls为true 则会走promoteCalls() ;从上面代码中可图看出来promoteCalls()中用迭代器遍历readyAsyncCalls 然后加入到runningAsyncCalls

其实就是在请求完成后 来请求缓存池中的线程

那么继续回头看AsyncCall–>execute中的逻辑,在这之前先简单介绍一下拦截器 okhttp中请求有用到拦截器

4.拦截器Interceptors

拦截器主要是用来监听网络的请求和响应 拦截器的添加可以添加,移除或者转换请求头;其实简单来说就是对网络请求和响应的一个包装吧,有兴趣可以自己研究一下 这里简单介绍一下方便接下来的讲解;

继续回到代码:AsyncCall–>execute:

@Override protected void execute() {
boolean signalledCallback = false;
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) {
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);
}
}


上面代码中关键的请求代码是:

Response response = getResponseWithInterceptorChain();


下面我们来看见其源码:

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

return chain.proceed(originalRequest);
}


在上面代码中:

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());


其实是初始化了拦截器 传参为读取/请求超时等….

实际请求的代码是:chain.proceed(originalRequest);

@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
//从拦截器列表取出拦截器
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);

if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}

return interceptedResponse;
}

// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}


在拦截器拦截过程中,当存在多个拦截器,需要拦截等待,即第一个请求完下一个请求;其中拦截的代码会在上面代码中:

Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);


而真正请求的代码是//getResponse(request, forWebSocket);

那么接着来看它的源码:

Response getResponse(Request request, boolean forWebSocket) throws IOException {
//请求成功的核心源码

engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

int followUpCount = 0;
while (true) {
if (canceled) {
engine.releaseStreamAllocation();
throw new IOException("Canceled");
}

boolean releaseConnection = true;
try {
engine.sendRequest();
engine.readResponse();
releaseConnection = false;
} catch (RequestException e) {
// The attempt to interpret the request failed. Give up.
throw e.getCause();
} catch (RouteException e) {
...
}

//请求失败的核心源码
boolean releaseConnection = true;
try {
engine.sendRequest();
engine.readResponse();
releaseConnection = false;
} catch (RequestException e) {
// The attempt to interpret the request failed. Give up.
throw e.getCause();
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
if (retryEngine != null) {
releaseConnection = false;
engine = retryEngine;
continue;
}
// Give up; recovery is not possible.
throw e.getLastConnectException();
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
HttpEngine retryEngine = engine.recover(e, null);
if (retryEngine != null) {
releaseConnection = false;
engine = retryEngine;
continue;
}

// Give up; recovery is not possible.
throw e;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
StreamAllocation streamAllocation = engine.close();
streamAllocation.release();
}
}
.....
engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
response);
}

}


到这里okhttp3的源码就解析完毕了 感谢支持 希望可以帮到你们~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  源码