okhttp源码解析(一):整体分析
前言现在最流行的网络框架是什么?那肯定是okhttp,它开源已经有很长一段时间了,一直都是开发者们的最爱,例如目前最流行的:Rxjava + Retrofit + okhttp,都是以okhttp作为网络层实现的基础,图片加载框架Fresco,也支持设置okhttp为网络层,为什么大家都为okhttp一路绿灯?不用怀疑,肯定是okhttp过硬的技术实力,现在我们就来了解一下okhttp的源码。正文
前言
现在最流行的网络框架是什么?那肯定是okhttp,它开源已经有很长一段时间了,一直都是开发者们的最爱,例如目前最流行的:Rxjava + Retrofit + okhttp,都是以okhttp作为网络层实现的基础,图片加载框架Fresco,也支持设置okhttp为网络层,为什么大家都为okhttp一路绿灯?不用怀疑,肯定是okhttp过硬的技术实力,现在我们就来了解一下okhttp的源码。
正文
先来回顾一下okhttp的基本用法:
public class MainActivity extends AppCompatActivity {
public static final MediaType JSON = MediaType.parse("application/text; charset=utf-8");
private OkHttpClient okHttpClient;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view);
okHttpClient = new OkHttpClient.Builder().build();
findViewById(R.id.get_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
get();
}
});
findViewById(R.id.post_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
post();
}
});
}
private void get() {
new Thread() {
@Override
public void run() {
super.run();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
try {
// execute为同步方法,不会新起线程,所以这里手动创建了Thread
final Response response = okHttpClient.newCall(request).execute();
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
textView.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
private void post() {
RequestBody body = RequestBody.create(JSON, "test");
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(body)
.build();
okHttpClient.newCall(request)
// enqueue为异步方法,在线程池中运行
.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
@Override
public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {
final String result = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(result);
}
});
}
});
}
}
代码里实现了get请求和post请求,这两种请求没有差别,只是多调用了post方法,明确了请求的方式,然后通过request对象生成Call对象,执行网络请求:excute是同步方法,不会创建新的线程,所以我们需要手动创建线程,enqueue是异步方法,会运行在okhttp内部的线程池中,网络的结果就在response中。
今天的内容就是分析从创建Request到得到Response的过程。
首先我们看看Request这个对象:
public final class Request {
final HttpUrl url; // url地址信息
final String method; // 方法get、post、put等等
final Headers headers; // header信息
final @Nullable RequestBody body; // 请求体,包括参数信息等
final Object tag; // 这个请求的标签,可以根据标签取消网络请求
// 同步
private volatile CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
// setter and getter
public HttpUrl url() {
return url;
}
// 相当于clone方法,复制当前Request的所有配置,返回新的Request
public Builder newBuilder() {
return new Builder(this);
}
/**
* Returns the cache control directives for this response. This is never null, even if this
* response contains no {@code Cache-Control} header.
*/
public CacheControl cacheControl() {
CacheControl result = cacheControl;
return result != null ? result : (cacheControl = CacheControl.parse(headers));
}
public boolean isHttps() {
return url.isHttps();
}
public static class Builder {
...
public Builder() {
// 默认是get方法
this.method = "GET";
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
// setter and getter
}
Request中主要是记录的请求的一些信息,没有什么复杂的逻辑,接下来调用了
final Response response = okHttpClient.newCall(request).execute();
先看看newCall方法:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
内部调用了RealCall.newRealCall,进入到RealCall这个类中:
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // 重试的Interceptor
}
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;
}
看到这里就明白了,okhttpClient.newCall就是创建了一个RealCall对象,里面有OkhttpClient和Request信息,关键还是在excute方法上:
@Override public Response execute() throws IOException {
synchronized (this) { // 如果任务已经开始了,不会执行并抛出异常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this); // okhttpclient的eventlistener,在builde中设置,这里不做介绍
...
client.dispatcher().executed(this); // 把RealCall保存到正在执行的任务列表中
// 这是最关键的代码,通过Interceptors的连锁处理,最终得到Response并返回
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
...
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); // 这个是我们在okhttpclient中自己设置的intceptors,因为优先级最高,所以我们可以完全自定义返回的Response
interceptors.add(retryAndFollowUpInterceptor); // 自动重试的Interceptor,刚才已经看到了创建的代码
interceptors.add(new BridgeInterceptor(client.cookieJar())); // 桥接的Interceptor,主要处理header中的信息
interceptors.add(new CacheInterceptor(client.internalCache())); // 缓存Interceptor,判断是否要使用缓存
interceptors.add(new ConnectInterceptor(client)); // 网络连接Interceptor,读取网络数据
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors()); // 这里是okhttpclient设置的networkInterceptors
}
interceptors.add(new CallServerInterceptor(forWebSocket)); // 读取网络数据Interceptor
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
这里把我们设置的Intercepters和内置的Interceptors组合起来,封装成RealInterceptorChain对象,这里明确了各种Interceptor的优先级,这对于如何设置Interceptor非常重要,我们分析的终点就在proceed方法中了:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError(); // 判断递归是否已经越界
calls++;
// 一些验证,这里省略
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout); // 创建一下Chain,并得到之前的Chain的各种配置信息
Interceptor interceptor = interceptors.get(index); // 得到当前index的interceptor
Response response = interceptor.intercept(next); // 当前Interceptor执行下一个chain
// 一些验证,这里省略
return response;
}
这里应该是最难理解的地方了,到这里我们没有看到for循环,那么Interceptors是怎么按照顺序响应的呢?关键就在与我们如何实现Interceptor接口:
new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return null;
}
}
在当前的Interceptor中,调用执行第一个Chain.proceed()方法;
第一个Chain.proceed()中又执行了第二个Interceptor;
第二个Interceptor执行第二个Chain.proceed();
而在第二个Chain.proceed()中又执行了第三个Interceptor;
....
这样就形成了递归,我们知道okhttpclient中设置的Interceptor优先级最高,如果我们直接返回null会怎么样呢?因为我们打断了递归,所以直接就返回了null,后面的Interceptor都不会再执行了,所以对于定义Interceptor要十分小心。
看完了excute,再看看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)); // 这里是重点,看看dispatcher里做了什么
}
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {// 请求书是否应到最大
runningAsyncCalls.add(call); // 放入到正在运行任务的队列中
executorService().execute(call); // 线程池运行call,那么这个call一定实现了Runnable
} else {
readyAsyncCalls.add(call); // 加载到等待运行的队列中
}
}
看一下AynCall的代码:
final class AsyncCall extends NamedRunnable { // NamedRunnable继承Runnable,为每一个Runnable命名
private final Callback responseCallback; // 这是在enqueue传入的callback参数
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;
}
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(); // 熟悉的代码,跟excute中的一样
if (retryAndFollowUpInterceptor.isCanceled()) { // 任务是否被取消
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled")); // 被取消,回调onFailed
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response); // 返回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); // okhttpclient设置的eventlistener
responseCallback.onFailure(RealCall.this, e); // 回调onFailed
}
} finally {
client.dispatcher().finished(this); // 从队列中结束任务
}
}
}
从服务端读取数据是在CallServerInterceptor中,虽然只是简单的读写操作,但是okhttp封装的却很仔细,所以这里暂时省略,之后我们会单独把Response的读取作为很重要的一部分仔细分析。
总结
是不是觉得很简单?现在我们还只是弄清楚okhttp的主线操作,其他的功能例如缓存,线程池等等还需要研究一下具体的细节。作为研究okhttp源码的开篇,已经为我们之后的研究奠定了非常坚实的基础。
最后祝大家周末愉快!!!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)