一. 基本使用
https://github.com/square/okhttp
https://square.github.io/okhttp/
implementation 'com.squareup.okhttp3:okhttp:3.14.7'
1. 三个重要对象
OkHttpClient
Call的工厂,创建一个单例,用于发送HTTP请求并读取其响应。每个clien都拥有自己的连接池和线程池,重用连接池和线程池可以减少延迟并节省内存。
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(new Cache(cacheDir, cacheSize))
.build();
Request
HTTP请求,如果此实例的body为null或者本身是不可变的,则它们是不可变的。
Request request = new Request.Builder().url("http://www.baidu.com").build();
Call
表示已准备好执行的请求,在未完成之前可以取消请求。由于此对象表示单个请求/响应对(流),因此无法执行两次。
Call call = client.newCall(request);
//同步方法
Response response = call.execute();
//异步方法
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
总结起来共三步便可发送一个请求:
- 创建OkHttpClient和Request对象;
- 将Request对象封装成Call对象;
- 调用Call的execute方法发送同步请求或enqueue方法发送异步请求。
2.请求步骤分析
同步请求
-
将请求加入到同步队列后直接执行
异步请求
-
将异步请求交给Dispatcher类来管理
-
将请求加入到准备队列,最后调用执行方法
-
在满足条件之后,将准备执行队列中的线程移入到执行队列中并开始执行任务。
-
利用线程池开始执行任务
-
执行线程的run方法,可以看到,在run中执行了抽象方法execute
-
最终执行了请求并将结果交给了回调方法。
finished方法分析
从前面的代码中可以看到,在同步请求和异步请求执行结束之后都会调用finished方法。
从代码可以很清晰的看到,finished方法中执行了两个步骤
- 从正在执行的任务队列中删除此任务;
- 在没有请求任务执行时可以执行空闲的回调。可以利用这一点,在后台执行一些耗时、优先级较低的任务。
请求流程图
注意
- 同步请求发送后,就会进入阻塞状态,直到收到响应。
- 异步请求发送后,onFailure和onResponse都是在子线程中执行的,要在这两个回调方法中执行UI操作,需要先转换到主线程。
二. Dispatcher源码分析
维护请求的状态,并维护一个线程池,用于执行请求。
Dispatcher内部维护着3个队列:
- readyAsyncCalls ---- 就绪等待的异步队列
- runningAsyncCalls --- 正在执行的异步队列
-
runningSyncCalls --- 正在执行的同步队列
异步调用是通过内部的线程池来消费的,最大请求个数默认是64,每个host的最大请求数是5,线程最大空闲时间60s。
三. OkHttp拦截器
拦截器是OkHttp中提供的一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能,分为应用拦截器和网络拦截器两种。
拦截器总结:
- 创建一系列拦截器,并将其放入一个拦截器list中;
- 创建一个拦截器链RealInterceptorChain,并执行拦截器链的proceed方法,应用了责任链模式;
- 在发起请求前对request进行处理;
- 调用下一个拦截器,获取response;
- 对response进行处理,返回给上一个拦截器。
利用拦截器我们可以自由的来处理request和response。比如,在实际开发上,一般有多个环境用来测试App,dev、test、release等,可以在debug环境添加一个拦截器用来替换想要请求环境的域名。
缓存
- 只缓存Get请求
- 使用DiskLruCache磁盘缓存算法
连接池
连接池最大缓存空闲连接5个。内部有个线程池在每次添加连接的时候会清除空闲连接。