对整个OkHttp框架的介绍,会分为使用篇和源码分析篇两个部分进行介绍:
这里是使用篇的目录:
(一)-基本使用
(二)-常用类介绍
(三)-Interceptor
源码分析篇敬请期待……
在上一篇文章中先整体梳理下OkHttp的用法,没有对于其中涉及到的一些类进行介绍,所以这边文章,我们对其中比较重要的几个类进行一些介绍。但是关于这些类的介绍,还是推荐大家去官网api的文档进行查看是最好的,这个才是真正熟悉类最好的方法。
我们知道,OkHttp发起请求的过程如下:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
我们就根据这个发起请求的过程,来一次介绍其中涉及到的类。
1 OkHttpClient
我们看官方给其的定义就是
Factory for calls, which can be used to send HTTP requests and read their responses.
它的主要目的就是用来创建Call
对象的,而这个Call
对象是用来发起HTTP请求和读取返回结果的。所以,其就是一个工厂类,里面封装了我们请求的一些配置参数而已。而对于配置的分装,通常我们会使用其内部类Builder
进行设置,我们看下Builder
类:
public static final class Builder {
Dispatcher dispatcher;
@Nullable Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable Cache cache;
@Nullable InternalCache internalCache;
SocketFactory socketFactory;
@Nullable SSLSocketFactory sslSocketFactory;
@Nullable CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int callTimeout;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
……
}
这里我只列出了Builder中的属性,而我们配置的过程就是在对这些属性进行赋值操作,比如:
public Builder connectTimeout(long timeout, TimeUnit unit) {
connectTimeout = checkDuration("timeout", timeout, unit);
return this;
}
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
public Builder addNetworkInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
networkInterceptors.add(interceptor);
return this;
}
这里就不全部展开说了,理解了OkHttpClient的创建方式,需要配置什么,找对应的方法就行了。
接着上面的请求流程,我们的OkHttpClient创建好之后,通过newCall(request)
方法去获取一个Call对象来发送请求,该方法需要一个Request
对象。所以我们下面就介绍下Request对象。
2 Request
这里Request泛指和请求相关的类,核心包括Request
、Header
、RequestBody
三个类,下面就针对这三个类进行简单介绍。
通常我们会通过如下方式进行Request构建(以POST为例,因为一般POST才会涉及请求体)
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "Square Logo")
.addFormDataPart("image", "logo-square.png",
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
可以看到一般是先通过构建RequestBody
,然后通过Request.Builder
将RequestBody
和Headers
封装到Request
中。
Headers
相对比较简单,我们放在后面介绍。
2.1 RequestBody
我们看其类定义会发现其是一个抽象类:
public abstract class RequestBody {}
使用过程中真正构建的时候是使用它的实现类,常用的就是FormBody
,MultipartBody
分别对应表单提交和分块提交。
2.1.1 FormBody
对FormBody
的封装,是通过其内部类FormBody.Builder
实现的,主要方法如下:
add(String name, String value)
:添加表单的信息的key(name)-value。这个方法添加的是为经编码的数据。
public Builder addEncoded(String name, String value)
:作用同上,只是添加的是已经编码之后的数据。
FormBody build()
:构建一个FormBody
实例
2.1.2 MultipartBody
同FormBody
一样,也是通过内部类MultipartBody.Builder
进行封装实现,主要方法如下:
setType(MediaType type)
:设置MIME类型
addPart(RequestBody body)
:直接添加一个RequestBody作为一个分块数据
addPart(@Nullable Headers headers, RequestBody body)
:添加Header和RequestBody作为一个分块数据
addFormDataPart(String name, String value)
:添加一个键值对作为表单格式的数据
addFormDataPart(String name, @Nullable String filename, RequestBody body)
:添加一个表单格式的数据块(一般是在上传文件的时候使用较多)
addPart(Part part)
:直接添加一个Part对象作为一个数据块
这里Part类是对分块数据的封装,一个Part对象就代表一个MultiBody中的分块数据
看到这里,你会发现,整个OkHttp框架设计,非常多的地方使用建造者设计模式。
除了这些OkHttp框架已经提供好的RequestBody
的实现,我们平时使用的过程中,如果有特殊需要,我们也可以通过自定义RequestBody来满足要求,具体使用可以参考上一篇中的2.4.3——POST提交流的时候,有过简单实现。
接着我们看上面提到的Request中三大核心类之一的Headers
2.2 Headers
这个比较简单,里面就是通过String
数组存放我们的请求头信息,这里为什么用数组而不用Map
呢,因为HTTP协议中header一个key可以对应多个value,所以这里使用数组,然后每2个步长为一个header的key和value。感兴趣的朋友可以看下它的源码就明白了。
我们实际使用的时候,一般直接通过Request.Builder
的header(String name, String value)
和addHeader(String name, String value)
进行添加。具体区别,我们在第一篇基本使用中已经介绍了,这里就不再赘述。
接着看下真正的信息封装了Request
类吧,这个才是真正承载请求信息的类。
2.3 Request类
我们还是看下官方对该类的定义吧
An HTTP request. Instances of this class are immutable if their {@link #body} is null or itself immutable.
很简单,代表一个HTTP协议的请求信息,而且还说明了该类是不可变的。什么意思呢,就说我们构造好一个Request后,就不能更改其属性,如果需要修改,我们需要通过重新构建一个新的Request来满足要求。
既然该类是请求信息的载体,那里面肯定就有我们的请求url、method,headers等信息的记录了。可以看到该类有如下几个属性:
public final class Request {
final HttpUrl url; // 请求的url地址
final String method; // 请求方法(GET/POST等)
final Headers headers;// 请求头信息封装
final @Nullable RequestBody body; // 请求体封装
……
}
我们可以看到,该类和其属性都是被final
关键字修饰的,所以该类不能被继承,属性一旦初始化就不能修改,这也验证了上面对该类的描述(不可修改的特性)。
那这些属性是怎么封装进来的呢,同OkHttpClient一样,也是通过内部类Builder
进行构建的,所以我们需要怎么配置Request
就直接找Request.Builder
中对应的方法就行了。这里就不详细展开说明了。
上面说到,该类的属性是不能修改的,既然不能修改,那如果我想在已有的请求信息基础上对某些属性进行修改怎么办呢,难道要全部重新构建一遍?当然不用,通过newBuilder()
方法就能得到一个包含之前信息的Builder对象,我们在此基础上,对需要修改的部分重新赋值,再通过Builder.build()
就能拿到新的Request了。
到这里,我们请求信息就算封装好了,然后将其交给newCall(request)
方法就能得到一个Call
对象。由于这里不是分析源码,所以不做特别说明,我们知道通过该方法,就会将Request
中封装的信息,传递给Call,然后用Call发起真正的请求。
2 Call
为了便于说明,我们先来简单看下该类的源码吧。
public interface Call extends Cloneable {
// 返回我们的请求封装信息
Request request();
// 同步执行请求
Response execute() throws IOException;
// 异步执行请求
void enqueue(Callback responseCallback);
// 取消请求,如果该请求已经被执行,是无法取消的。
void cancel();
// 判断当前请求是否已经被执行
boolean isExecuted();
// 判断该请求是否被取消了
boolean isCanceled();
// 请求超时,超时时间通过OkHttpClient.Builder的callTimeout()方法设置
Timeout timeout();
……
}
该类的核心方法,已经在代码里面注释的比较清楚了。我们通过该类真正发起请求,取消请求,并且可以判断当前请求的状态。
可以看到,这是一个接口,它的实现类是RealCall
,还是那句话,这里不是做源码分析,所以如果想了解具体每个方法的实现,就请自己去这个类里面去查看。这里我们只需要知道每个方法的作用即可。
接着最开始那个请求的示例继续,得到Call
对象后,不管是异步(enqueue()
方法)还是同步(excute()
方法)请求,最终我们都会得到Response这个对象。这个里面就是对返回信息的封装。
3 Response
该类和Request构成了HTTP请求中的请求和响应。所以根据前面对Request的介绍,估计诸位已经能猜出该类的设计了。是的,同Request一样,该类也是不可被继承,也不能被修改的。
public final class Response implements Closeable {
final Request request;
final Protocol protocol;
final int code;
final String message;
final @Nullable Handshake handshake;
final Headers headers;
final @Nullable ResponseBody body;
final @Nullable Response networkResponse;
final @Nullable Response cacheResponse;
final @Nullable Response priorResponse;
final long sentRequestAtMillis;
final long receivedResponseAtMillis;
……
}
可以看到,该类和其中的属性都被final
修饰了。而且设计模式也一样,通过对应的Builder进行构建,如果需要在已有Response的基础上重新设置某些属性,通过其newBuilder()
方法。
对HTTP协议稍微熟悉点的人,上面列出的属性应该都不会陌生。至于里面封装的另外三个Response属性,那个涉及到缓存、重定向等问题,这里不做详细讨论。一般我们拿到Response对象后,判断下返回码如果正常,直接拿到body属性,再通过ResponseBody.string()
就能获读取到返回信息了。
不过这里有个注意点,就是这个string()
方法,同一个ReponseBody对象只能调用一次,里面的数据一旦被读走,就没了,再次调用会报错哦,所以如果你需要重复使用,就需要自己读出来后做好备份工作了。
到此,我们对整个请求过程中国涉及到的一些类就做了简单介绍,由于这里并不是做详细的流程分析和源码分析,只是让大家对整个流程有个大概的认识,了解一些常用的API,然后对其中一些使用注意点进行说明,所以介绍的相对比较简单和粗糙,需要深入了解的朋友,可以看后续的源码分析篇。