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

OKHttp原码分析(一)

2017-04-30 12:07 155 查看

一,概述

okhttp并不是对某个网络请求类的封装,它是偏底层的网络请求类库,封装的是传输层的socket,与httpURLconnection是同一级别的。OKHttp比起httpURLconnection做了大量的性能优化和在使用上的优化,因此OKHttp的源码也比较复杂,需要连续多篇blog进行讲解分析。

这篇blog以get请求为例主要分析以下几点:

1,OkHttpClient对象的创建。

2,Request 对象的创建。

3,Call对象的创建

4,Call的execute方法实现同步请求。

5,Call的enqueue方法实现异步请求,异步请求是怎么开启的子线程。

6,同步请求与异步请求是怎么殊途同归到一条路上的。

二,OkHttpClient对象的创建

OKHttpClient对象的创建方式有两种。

第一种是使用构造方法创建对象:

OkHttpClient client = new OkHttpClient();//使用构造方法直接创建OkHttpClient对象。


第二种是使用Builder模式创建对象:

OkHttpClient.Builder builder = new OkHttpClient.Builder();//先创建构建者对象
builder.connectTimeout(3*1000, TimeUnit.MILLISECONDS);//设置超时时间
OkHttpClient client = builder.build();//使用构建者创建OkHttpClient对象。


1,使用构造方法创建OkHttpClient对象

OkHttpClient的构造方法原码是:

public OkHttpClient() {
this(new Builder());
}


本质调用的是OkHttpClient的有参构造,我们先看看这个构造方法的源码,再看Builder类的无参构造方法。

OkHttpClient的有参构造方法的部分原码是

private OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.protocols = builder.protocols;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
。。。略。。。
}


以上是部分代码,从代码中我们可以看出这个构造方法主要是给OkHttpClient的字段进行初始化,OkHttpClient字段的值来自于builder对象。

下面看Builder类的无参构造方法的源码:

public Builder() {
dispatcher = new Dispatcher();
socketFactory = SocketFactory.getDefault();//得到SocketFactory对象,这个工程类可以创建Socket对象。
connectionPool = new ConnectionPool();//链接池
connectTimeout = 10_000;//设置连接超时时间,默认为10秒
readTimeout = 10_000;//设置读取超时时间
writeTimeout = 10_000;//设置写入超时时间
。。。略。。。
}


Builder的无参构造中主要是对Builder的字段进行初始化,这些初始化值也是OkHttpClient的初始化值,也就是对网络请求参数的初始化。

2,使用Builder模式创建OkHttpClient对象

首先看创建Builder对象使用的代码:

OkHttpClient.Builder builder = new OkHttpClient.Builder();


Builder是OkHttpClient的内部类,创建Builder对象使用的是builder的无参构造方法。在上面我们已经看过Builder的无参构造方法,主要功能就是进行一些字段的初始化。

创建OkHttpClient对象使用的代码是:

OkHttpClient client = builder.build();


这是一种build设计模式,下面看Builder类的build方法的源码:

public OkHttpClient build() {
return new OkHttpClient(this);
}


看到这儿会觉得好熟悉啊,build()方法中调用的也是OkHttpClient的有参构造,参数也是Builder对象。通过上面的分析我们知道OkHttpClient的有参构造主要作用就是给OkHttpClient的字段进行初始化。

3,两种创建OkHttpClient对象方式的区别与应用场景

通过上述分析可知,创建OkHttpClient对象最终都是使用的OkHttpClient的有参构造,且都是通过Builder对象给OkHttpClient的字段进行初始化。

区别是:

1,使用构造方法创建OkHttpClient对象:使用builder的无参构造方法创建builder对象后直接给OkHttpClient的字段进行初始化,即这是一步操作,中间不能做任何设置。

2,使用Builder模式创建OkHttpClient对象:先得到Builder对象,然后再通过代码创建OkHttpClient对象对象。这样我们就可以根据需要修改Builder对象的一些字段值。

应用场景:

在Builder的无参构造方法中我们看到系统对连接超时时间,缓存等字段初始化了一些默认值,如果这些值不是我们想要的,我们要修改某些值,此时就需要使用第二种方法创建OkHttpClient对象。如果系统的默认值满足我们的要求,直接使用第一种方法创建OkHttpClient对象即可。

三,Request 对象的创建

get请求时创建Request 对象的代码如下:

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


很明显这是一种Builder设计模式,首先创建Builder对象,然后给builder对象设置属性,然后再通过Builder对象的build方法创建Request 对象,并把builder的值赋给Request 对象。

注意:这个Builder类是Request 的内部类,与OkHttpClient的内部类Builder不是同一个。

下面看Request 的内部类Builder的无参构造原码:

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


此时发现在创建Builder对象时默认把请求方式设置为GET,所以使用get请求不需要再次设置请求方式。

此外还有一行代码是创建headers 。这是headers 的使用细节,这儿不做讲解。

下面看Builder的url方法的原码。

url方法有三个重载方法,最终调用的都是下面这个方法:

public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}


此时注意两点:

1,url方法就是给builder对象的url属性赋值。

2,此时返回this,这是一种典型的链式编程思想。

下面看Builder的build方法的原码:

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


作用就是调用Request的有参构造创建Request对象。

下面看Request有参构造的原码:

private 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;
}


这个方法的作用就是把builder的属性值赋值给Request的属性。

看到这儿我们应该对builder设计模式有个认识了。首先创建builder对象,然后给builder对象的属性赋值,然后将builder对象的属性值赋值给实际要创建的对象。

四,Call对象的创建

创建call对象使用的代码是:

client.newCall(request)


下面看一下OKHttpClient类的newCall方法的原码:

@Override public Call newCall(Request request) {
return new RealCall(this, request);
}


这个方法的作用是创建RealCall对象,并把client对象和request对象都传递过去了。

注意:

1,RealCall是Call接口的实现类,Call调用的方法的实现都在RealCall类中,这个类是关键类。

2,将client对象和request对象传递给了RealCall对象,这样等于RealCall对象拥有了client的属性值和request对象的属性值。即我们在前面对client设置的属性值和对request设置的属性值都传递到了这个对象中。

五,RealCall的execute方法实现同步请求。

通过上面分析我们知道Call是一个接口,他的实现类是RealCall,所以下面看RealCall类的execute方法的源码:

@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}


这个方法中的核心代码是:Response result = getResponseWithInterceptorChain()。这个方法中返回的result 即是我们需要的响应数据。所以getResponseWithInterceptorChain是网络请求的核心方法。现在先记住这个方法,这个方法的源码下篇blog再惊喜讲解。

六,RealCall的enqueue方法实现异步请求

首先上RealCall类的enqueue方法的源码:

@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}


分析:

1,这里的client就是前面创建的OKHttpClient对象。

2,client.dispatcher()方法返回Dispatcher对象,这个类成为调度着,这个对象的创建在OKHttpClient的内部类Builder的构造方法中。

3,重点有两个,一个是Dispatcher的enqueue方法,一个是AsyncCall对象。

下面先看Dispatcher的enqueue方法的源码:

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


这是一个同步方法,核心代码是:executorService().execute(call);

下面首先看executorService()方法的源码:

public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}


这个方法创建了一个线程池对象,这个线程池的特点是:

1,核心线程数为0。

2,最大线程数是Integer的最大值,这里可以理解为无限多。

3,非核心线程存活时间为60秒。

分析:这个线程池没有核心线程,非核心线程的存活时间只有60秒,所以这个线程池的优点不是特别突出。

下面继续看:executorService().execute(call);

executorService()返回一个线程池对象,我们知道execute方法接收Runnable对象,Runnable的run方法执行在子线程。下面开下这个call对象。

这个call对象来源于:client.dispatcher().enqueue(new AsyncCall(responseCallback));

下面看AsyncCall类。

AsyncCall类是RealCall的内部类,AsyncCall继承NamedRunnable类,NamedRunnable又继承Runnable类。在NamedRunnable类中声明了一个抽象方法execute(),且在run方法中调用了。我们由代码executorService().execute(call)知道此时会调用run方法,从而会调用execute方法。

下面看AsyncCall的execute方法的源码(注意:这个方法执行在子线程):

@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
responseCallback.onResponse(RealCall.this, response);
}
}
}


在这个方法中我们看到了熟悉的代码:

Response response = getResponseWithInterceptorChain();

这行代码在同步请求中已经看到过,它是网络请求的核心。到此同步请求和异步请求都殊途同归到了getResponseWithInterceptorChain方法上。不同的是:异步请求是在子线程中调用的getResponseWithInterceptorChain方法。

此外在这儿我们看到了CallBack的onFailure方法和onResponse方法被调用。由于execute方法执行在子线程,所以onFailure方法和onResponse方法都是执行在子线程。

注意事项:

1,在RealCAll的enqueue方法中通过线程池的方式开启了子线程。

2,CallBack的onFailure方法和onResponse方法都执行在子线程中,所以在更新UI时还需要跳转到UI线程。

七,同步请求与异步请求是怎么殊途同归到一条路上的

由上面的分析可知无论是同步请求还是异步请求,最后调用的代码都是:

Response response = getResponseWithInterceptorChain();

这行代码是网络请求的核心,以上的代码都是辅助作用。

这行代码返回了Response 对象,所以表层的原码分析到此也算是一个结点。

getResponseWithInterceptorChain内部到底是怎么执行的,下一篇blog再做分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  OKHttp原码分析