开源框架(一): OKHttp源码浅析 请求流程
OKHttp 原理解析
OkHttp是当下Android使用最频繁的网络请求框架,由Square公司开源。Google在Android4.4以后开始将源码中 的HttpURLConnection底层实现替换为OKHttp,同时现在流行的Retrofit框架底层同样是使用OKHttp的。
优点:
1.谷歌官方在6.0以后再android sdk已经移除了httpclient,加入了okhttp.
2.okhttp支持SPDY(是谷歌基于TCP的应用层协议,用于最小化网络延迟,提升网络速度,是对http的一种增强),允许所有的访问统一主机的请求共享一个socket。
3.利用连接池,复用Socket,减少请求延迟
4.支持GZIP压缩, 减少数据流
5.响应缓存减少重复的请求,利用响应缓存来避免重复的网络请求。
即便是网络出现问题时,okhttp依然起作用.它将从常见的链接问题当中恢复.如果你的服务器有多个IP地址,当第一个失败时,okhttp会自动尝试连接其他的地址。
1.0 使用流程
http
请求主要是Get
和Post
。二者都分为的同步和异步请求。
[可以参考这篇文章](OkHttp基本使用流程总结 - 简书 (jianshu.com))
1. OkhttpClient mClient = new OkhttpClient()
2. Call call = mClient.newCall(Request request));
// GET请求
Request request = builder.url(url)//请求地址
.get()//请求方法,不写默认GET方法
.build();
// POST 请求
Request request = builder.url(url)
.Post(RequestBody requestBody)//这个就是请求体
.build();
}
3. call.enqueue(Callback calBack)//异步执行
3. Response res = call.excute();//同步阻塞
// Call 的 execute 代表了同步请求,而 enqueue 则代表异步请求。两者唯一区别在于一个会直接发起网络请求,而另一个使用OkHttp内置的线程池来进行。这就涉及到OkHttp的任务分发器。
另一个使用OkHttp内置的线程池来进行。这就涉及到OkHttp的任务分发器。
4. new Callback() {
@Override public void onFailure(Request request, Throwable throwable) {
throwable.printStackTrace();
}
@Override public void onResponse(Response response) throws IOException {
// response.body()是ResponseBody类代表响应体。
responseBody.string();//获得字符串的表达形式。
responseBody.bytes();//获得字节数组的表达形式, 这两种形式都会把文档加入到内存。
responseBody.charStream();
responseBody.byteStream();//返回流来处理。
}
}
完整的流程图如下:
1.1 浅析设计模式
建造者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
OkHttpClient 和 Request 的创建可以使用它为我们提供的 Builder (建造者模式)。实例化 OKHttpClient和Request的时候,因为有太多的属性需要设置,而且开发者的需求组合千变万化,使用建造 者模式可以让用户不需要关心这个类的内部细节,配置好后,建造者会帮助我们按部就班的初始化表示对象
门面模式:将整个系统的复杂性给隐藏起来,将子系统接口通过一个客户端 OkHttpClient统一暴露出来。
OkHttpClient 中全是一些配置,比如代理的配置、ssl证书的配置等。
责任链设计模式 : 对象行为型模式, 为请求创建了一个接收对象的责任链,在处理请求的时候执行过滤,各司其职
OKHttp 五大拦截器就就是责任链设计模式,各司其职,一层一层向下传递。
Demo
public static void main(String[] args) {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.add(new RetryAndFollowUpInterceptor());
interceptors.add(new BridgeInterceptor());
interceptors.add(new CacheInterceptor());
interceptors.add(new ConnectInterceptor());
interceptors.add(new CallServerInterceptor());
//链条对象 index
Chain chain = new Chain(interceptors, 0);
System.out.println(chain.processd("Http请求"));
}
// 链条的Process方法
public String processd(String request) {
if (index >= interceptors.size()) {
throw new AssertionError();
}
// index +1 过度到下一个拦截器
Chain chain = new Chain(interceptors, index + 1, request);
Interceptor interceptor = interceptors.get(index);// 取出拦截器
return interceptor.intercept(chain);
}
// 拦截器的intercept方法
@Override
public String intercept(Chain chain) {
//可以在执行下一个拦截器之前,做自己的事情
System.out.println("开始执行重试重定向拦截器");
// 执行下一个拦截器
String result = chain.processd(chain.request + "==>经过重试重定向拦截器");
//获得结果后,加一些自己的东西
System.out.println("结束执行重试重定向拦截器");
return result + "==>经过重试重定向拦截器";
}
1.2 分发器Dispatcher
execute 使用OkHttp内置的线程池来进行。这就涉及到OkHttp的任务分发器。
//异步请求同时存在的最大请求
private int maxRequests = 64;
//异步请求同一域名同时存在的最大请求
private int maxRequestsPerHost = 5;
//闲置任务(没有请求时可执行一些任务,由使用者设置)
private @Nullable Runnable idleCallback;
//异步请求使用的线程池
private @Nullable ExecutorService executorService;
//异步请求等待执行队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//异步请求正在执行队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//同步请求正在执行队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
当正在执行的任务未超过最大限制64,同时 runningCallsForHost(call) < maxRequestsPerHost 同一Host的请求 不超过5个,则会添加到正在执行队列,同时提交给线程池。否则先加入等待队列。 加入等待队列后,就需要等待有空闲名额才开始执行。每次执行完 一个请求后,都会调用分发器的 finished 方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
异步请求
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//调用分发器
client.dispatcher().executed(this);
//执行请求
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//请求完成
client.dispatcher().finished(this);
}
}
异步请求的后续同时是调用 getResponseWithInterceptorChain() 来执行请求,异步请求会把一个 AsyncCall 提交给分发器。 AsyncCall 实际上是一个 Runnable 的子类,使用线程启动一个 Runnable 时会执行 run 方法,在 AsyncCall 中被 重定向到 execute 方法:
同步请求
@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));
}
可以看到无论是同步还是异步请求实际上真正执行请求的工作都在 getResponseWithInterceptorChain() 中。这个 方法就是整个OkHttp的核心:拦截器责任链。
请求完成
//异步请求调用
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
//同步请求调用
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
//不管异步还是同步,执行完后都要从队列移除(runningSyncCalls/runningAsyncCalls)
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
//异步任务和同步任务正在执行的和
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
// 有异步任务才会存在限制与等待,所以在执行完了移除正在执行队列中的元素后,异步任务结束会执行 promoteCalls() 。很显然这个方法肯定会重新调配请求。
}
// 没有任务执行执行闲置任务
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
有异步任务才会存在限制与等待,所以在执行完了移除正在执行队列中的元素后,异步任务结束会执行 promoteCalls() 。很显然这个方法肯定会重新调配请求。
private void promoteCalls() {
//如果任务满了直接返回
if (runningAsyncCalls.size() >= maxRequests) return;
//没有等待执行的任务,返回
if (readyAsyncCalls.isEmpty()) return;
//遍历等待执行队列
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
//等待任务想要执行,还需要满足:这个等待任务请求的Host不能已经存在5个了
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
满足条件下,会把等待队列中的任务移动到 runningAsyncCalls 并交给线程池执行。