前言:
1.1为什么要写这篇文章? OKHttp是square出品的开源通信框架,目前市面上大多数的APP都使用了该框架,那为什么这么多的APP都选择使用该框架呢?有的人说别人都用我也就用呗,那肯定不是的。本文就带你了解为什么我们要使用OKHttp,以及OKHttp的原理。
1.2 OKHttp项目地址: https://github.com/square/okhttp
1.3 系列简介: OKHttp项目本身还是很大的,而且涉及到的知识点很多,所以一篇文章是很难概括全的,所以准备写一个系列的文章来详细讲解OKHttp。
准备写以下几篇。
OKHttp原理讲解之基本概念(预计03.15发布)
OKHttp原理讲解之责任链模式及扩展(预计03.18发布)
OKHttp原理讲解之重试拦截器(预计03.22发布)
OKHttp原理讲解之路由拦截器
OKHttp原理讲解之缓存拦截器
OKHttp原理讲解之连接池拦截器
OKHttp原理讲解之请求拦截器
PS:预计每周更新1-2篇的进度进行
1.4 OKHttp系列第一篇 本篇主要讲解以下部分:
1.OKHttp的简单使用
2.OKHttp的几个主要概念
3.OKHttp发送请求的基本流程
4.调度器中线程的管理
5.为什么选择使用OKHttp
第一章:OKHttp的几个主要概念 PS:以下几个中文名字都是按照我自己的理解取的,不是官方起名。
1.配置中心,OKHttpClient
OKHttp是一个可以高度定制化的框架,它提供了很多可配置的功能项,可以由开发者根据自主选择去配置。比如Dns,Proxy,connectTimeout等等。
而OKHttpClient就是这些配置的一个容器,它使用构造者模式去创建,开发者可以根据自己的需求自主选择。
2.请求体,RealCall
对于每一个请求,OKHttp都封装了了一个RealCall来执行具体请求的流程。所以自然的,五层责任链也是由它来创建和调用的。
3.调度器,Dispatcher
调度器负责每个请求体的具体执行。所以自然的,调度器中包含了一个请求线程池,以及请求队列,执行队列,等待队列等等。
4.责任链,List
OKHttp中默认包含五层责任链,分别为
RetryAndFollowUpInterceptor:重试跟进拦截器
BridgeInterceptor:桥接拦截器
CacheInterceptor:缓存拦截器
ConnectInterceptor:连接池拦截器
CallServerInterceptor:请求拦截器
一个请求的完整发送流程,会从上向下依次执行,如果某一个拦截器判断执行完成,则一层一层向上返回最终结果。
所以五层拦截器也是OKHttp的核心,后面会有专门的篇章来一一进行讲解。
5.请求,Request
顾名思义,包含请求所需要的所有信息。
6.响应,Response
顾名思义,包含响应所需要的所有信息。
第二章:OKHttp基本使用 这不是本文要讲解的重点,所以只是简单的介绍下使用方式。
2.1配置OKHttpClient 这里我只进行了一个配置,配置了一个缓存目录。
val builder = OkHttpClient.Builder()
builder.cache(Cache(File(context?.filesDir?.absolutePath + File.separator + "ok"), 100))
val client = builder.build()
2.2发送请求, 请求分两种,同步请求和异步请求,同步请求需要在子线程执行。
第一种同步的方式:
val builder = Request.Builder()
val request = builder.url("https://www.baidu.com").build()
val newCall = client.newCall(request)
service.execute {
val response = newCall.execute()
}
第二种异步的方式:
val builder = Request.Builder()
val request = builder.url("https://www.baidu.com").build()
val newCall = client.newCall(request)
newCall.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
logI(call.toString());
}override fun onResponse(call: Call, response: Response) {}})
2.3 处理返回值 reponse中包含返回的数据流,我们只要序列化返回的data数组即可。
val content = IOHelper.readStrByCode(response.body()?.byteStream(), "utf-8")
logI(content)
readStrByCode方法链接:
//todo
第三章:OKHttp发送主体流程 异步和同步请求其实流程上差不多,只不过同步请求是直接返回reponse,而异步则是通过回调的方式通知。这里就以异步请求为例,讲解整个请求的发送流程。
3.1 构建请求体RealCall 这里构建的RealCall中,主要包含原始的request,以及事件监听。
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
3.2 开始请求流程RealCall.enqueue() 这里主要做两件事,
第一件事,对于观察者进行回调
第二件事,交由调度器,在线程中执行AsyncCall的execute方法。
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));
}
3.3 执行请求流程execute() 首先调用责任链,去获取最终的响应。
然后进行通知callBack进行对应的回调。
最终通过调度器完整的结束该请求
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
第四章:调度器中线程的管理 上面讲了由调度器负责把一个一个的请求任务,分发给线程去管理。那么线程是如何管理的呢?
4.1 Dispatcher中的enqueue()方法
4.1.1 方法加锁:
这个方法是加锁的,避免多线程添加任务时,出现冲突。
4.1.2 判断数量是否超标
这里判断了正在执行的请求数量是否大于64,以及单个链接的请求数是否大于5(这里的64和5是可以配置的,单个链接指的是请求接口地址是一样的),如果超过,则排队;否则则可以立即执行。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
4.1.3 获取并创建线程池
1.判断是否存在线程池
如果不存在,则创建线程池。否则直接返回
这里扩展一个小问题,为什么使用的时候才创建线程池呢?我猜测是为了性能,提早创建了线程池不用浪费性能。
2.创建线程池
这里使用的线程池配置参数,核心线程数为0,最大线程数为MAX_VALUE,非核心线程存活时间为60秒。
队列使用的是SynchronousQueue,适合生产者消费者模型。具体SynchronousQueue可以自行百度,这里就不扩展了。
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue
3.为什么不会出现性能问题
我们这里看到,OKHttp请求线程池数量并没有设置上线,那么不会存在性能问题吗?实际上由于之前已经有了maxRequest的限制,所以线程数量并不会真的达到MAX_VALUE的级别。
所以可以这么理解OK的线程池策略,同时并发每一个请求,那么每一个请求都是一个单独线程去处理。如果一个请求完成后,那么其所占用的线程就可以被释放供后续请求使用。
4.自定义线程池策略
即使已经限制了最大请求数64,对于某些性能较差的手机,有可能真的大并发请求时还是吃力的。所以没关系,OK的一大特色就是可配置性强。没关系,线程池我们可以自定义传入,让OK使用我们自定义的线程池。
我们可以通过Dispatcher的有参构造函数传入自定义线程池。
val newSingleThreadExecutor = Executors.newSingleThreadExecutor()
val dispatcher = Dispatcher(newSingleThreadExecutor)
val builder1 = OkHttpClient.Builder()
builder1.dispatcher(dispatcher);
val build = builder.build()
4.1.4 线程池调度调用Call
1.执行AsyncCall
获取到线程池后,就可以交给其最终的call任务,让其去执行。最终会调用到AsyncCall
的execute方法。
executorService().execute(call);
2.为什么会执行AsyncCall的execute方法
AsyncCall继承自NameRunnable,NameRunnable继承自Runnable。NameRunnable中的实现如下,所以会执行NameRunnable的execute方法。
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}protected abstract void execute();
}
第五章:为什么选择使用OKHttp
常规答案 这里面试经常被问到的一道题,我面试别人的时候也经常问。很常规的答案如下:
1.大家都在用,所以我也就用。
2.大厂出门的,稳定性好,比较放心。
3.使用简单,用起来比较方便。
4.持续维护中,不担心出了问题没人解决
更为合适的答案 但是其实这题面试官更想考验的是对OKHttp的了解程度,所以除了上面那些优势,我们还可以从OKHttp框架的优势的角度去谈一谈:
1.可扩展性高。类似于缓存,Dns,请求/连接/响应超时时间等等都可以通过配置传入,甚至线程池都可以根据自己的需求来配置。
2.OKHttp使用了连接池缓存,提高通信效率。
3.责任链五层拦截器模式,每层功能清晰明了,并且提供了两层可扩展的拦截器方便进行所需要的改造。
4.层次结构清晰,方便进行问题的排查。
5.观察者模式的充分使用,查看请求状态和监控请求状态变得十分简单。
6.使用了OKIO框架进行数据的处理,效率和安全性上更高。
等等,随着文章的不断详细逐渐补充。
【安卓开源框架学习|OKHttp原理讲解之基本概念】
推荐阅读
- 史上最强C语言|二十万字带你入门C语言-史上最强C语言教程(汇总篇)
- Java学习|SpringBoot---杂七杂八---终篇
- 面渣逆袭|Spring Bean生命周期,好像人的一生。。
- 阿里一面(ReadWriteLock 读写之间互斥吗(我竟然答不上来。。))
- python|100天精通Python(基础篇)——第29天(标准库sys)
- Java|让你加班的不是你的老板,而是其他愿意加班的人
- JAVA人生|全球程序员收入报告(字节跳动高级工程师以年薪274万排名第五)
- xiaoxi整理
- Java版堆排序