前言:现在的Android项目基本上都是以OkHttp来进行网络通信的(retrofit也是基于okhttp的)
下面记录下okhttp3中的一些实现方式和原理。
工作流程
以这张图出发,说一下几个注意的点:
(1) 建议让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,因此为每一个请求都创建一个client是不必要的且影响软件效率。
下面我们来看看 new OkHttpClient() 的时候干了什么。从使用方面入手,下面就是创建一个client的常用手法:
#创建一个client主要有以下两种方式
OkHttpClient client = new OkHttpClient();
OkHttpClient client1 = new OkHttpClient.Builder().build();
这两种方式都可以实例化一个client对象,下面看看他们分别怎么做的:
直接调用 new OkHttpClient() 后,也会隐式的创建一个builder对象,然后在OkHttpClient构造方法中初始化一堆数据。如果是new OkHttpClient.Builder().build() 这种方式创建client的话,相当于显式的创建了builder,当然最后build的时候也是返回了一个client对象,如下所示。
他们的区别是 如果使用builder来构建的话,可以在后面追加一些拦截器进去。
(2)上诉步骤中创建了client了,接下来就是创建Request对象,Request这个类可以看做一个请求的封装,里面有url,body,headers等字段。Request对象的创建也使用到了建造者模式。按照get请求和post请求的区别,我们有如下两种创建Request的写法:
//get请求
Request request = new Request.Builder().url("http://www.baidu.com").build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//...
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//...
}
});
//post请求
MediaType mMediaType = MediaType.parse("text/x-markdown; charset=utf-8");
RequestBody body = RequestBody.create(mMediaType, "test");
Request request1 = new Request.Builder().url("http://www.baidu.com").post(body).build();
GET请求是默认使用的请求方式,因此不需要requestbody,但POST请求需要通过post(RequestBody)方法传入对应的请求体。请看他们所属的builder:
下面我们继续追一下post方法中干了什么:
可以看到,这里将原本默认为GET的method修改为了POST,然后还把body给赋值了。
(3)至此,我们有了发送请求的 OkHttpClient ,也有了 Requset,那么就可以执行发送请求了。
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//...
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//...
}
});
Call call = client1.newCall(request1);
call.enqueue();
我们使用newCall就可以构造一次发送请求,然后接收其回调结果即可。
首先我们先来看看newCall方法干了什么:
他调用了realcall类的方法,创建了一个新的realcall对象,这个对象持有我们当前所使用的httpclient和request。然后通过realcall对象,我们创建出Transmitter类。
此时我们有了realcall对象,我们可以选择调用execute()方法或者enqueue()方法。如果选择execute的话,则会马上发送这条请求,并返回response给我们。若是选择enqueue方法则会要求你传入一个接口,因为使用enqueue()方法的话,我们的请求具体执行交给了Dispatcher类来执行。
dispatcher任务任务调度器会将其存入待执行的请求队列中,然后条件允许的话再加入到运行中的请求队列(runningAsyncCalls)中,然后将这个请求放到任务调度器中的线程池中进行消费,具体过程如下:01.加入准备任务队列中
02.判断其是否具有执行条件
03.确认执行的话,会进入RealCall的executeOn方法中
04. 假如03步正确执行了,那么会在此确认执行结果
至此,若是成功返回了response的话,就会触发接口onresponse,我们上层就能收到结果了。 但上图中的getResponseWithInterceptorChain()方法还值得细说。(4)进入拦截链流程getResponseWithInterceptorChain方法
这个方法返回的是response对象,我们看看它都做了些什么:
可见在 getResponseWithInterceptorChain 方法中,创建了一系列拦截器,然后我们的request请求通过拦截器调用链的拦截后,最后才会返回Response对象 就是上面chain.proceed(originalRequest)方法执行完成之后返回。
在看chain.proceed方法前我们先简单介绍下各个拦截器有什么用:(按代码顺序)