OkHttp 知名第三方网络框架SDK,使用简单,性能优秀,可是内核并不简单,此系列文章,专挑硬核知识点详细讲解. 何为硬核,就是要想深刻研究,你绝对绕不过去的知识点
OkHttp是时下很是流行的网络编程框架,由行业巨佬 Square公司开源,不少其余的流行框架好比 retrofit的底层也是okhttp,只不过使用了注解反射动态代理将其进行了封装。流行版本为:3.10.0,最新版本为:4.0.1,只不过将实现语言从java改为了kotlin。
相对于其余网络框架,有以下优势:java
添加gradle依赖编程
dependencies { .... implementation ("com.squareup.okhttp3:okhttp:4.0.1") }
Java调用(同步请求,异步请求)设计模式
public class MyRequest { /** * 异步请求 */ public void sendReqAsync() { OkHttpClient client = new OkHttpClient.Builder().build(); Request request=newRequest.Builder().url("http://www.baidu.com").build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NotNull Call call,@NotNull IOException e) { Log.d("sendReqTag", "onFailure:"+e.getLocalizedMessage()); } @Override public void onResponse(@NotNull Call call,@NotNull Response response) throws IOException { String s = new String().concat(response.code() +"\n") .concat(response.message()+"\n") .concat(response.body().string()); Log.d("sendReqTag","onSuccess\n "+ s); } }); } /** * 同步请求 */ public void sendReqSync() { OkHttpClient client = new OkHttpClient.Builder().build(); Request request = new Request.Builder().url("http://www.baidu.com").build(); Call call = client.newCall(request); try { Response response = call.execute(); String s = new String().concat(response.code() + "\n") .concat(response.message() + "\n") .concat(response.body().string()); Log.d("sendReqTag","onSuccess\n "+s); } catch (IOException e) { e.printStackTrace(); } } }
OkHttp的简单使用方法大体使用如上,其中也有一些细节须要注意:缓存
一、在应用层使用OkHttp,必然会涉及到4个重要元素:性能优化
2.执行网络请求必须在manifest中申请 INTERNET权限,否则会抛异常.网络
三、完整的一个请求执行出去,流程以下图:架构
OkHttp源码核心类之一:分发器详解并发
上述,提到Call类,能够选择性执行 同步或者异步请求,可是不管同步异步,都必定会通过一个门户:"分发器" : 索引进源码(okhttp v3.10.0):框架
虽然用户不须要直接操做分发器,可是 分发器,做为 OkHttp架构的一个门户层,是全部请求的必经之路,其中的代码仍是有必要了解细节的。异步
进入 分发器Dispatcher
以后, 会执行 getResponseWithInterceptorChain()
来执行这个 Call任务,获得一个 Response
,其中的细节分为两步:
一、client.dispatcher().executed(this);
,进入源码能够看到 仅仅是执行了runningSyncCalls.add(call);
,将call对象加入到了一个双端队列Deque<RealCall>runningSyncCalls
中。
二、getResponseWithInterceptorChain()
是执行网络请求的核心内容,涉及到拦截器,在这一节上暂时不详述。
同步请求的执行步骤十分简单,将任务加入到 runningSyncCalls列表,而且直接执行核心方法,同步阻塞拿到response。
异步请求进入分发器以后,
可能会被加入到 Deque<AsyncCall>runningAsyncCalls
这么一个双端队列中,而后 executorService().execute(call);
其实是用了线程池来执行了这个异步任务。可是,请注意(仍是刚才的enqueue方法代码)这里有一个判断条件 if分支 :
这个条件是否知足,将会直接决定是直接执行这个任务,仍是将任务加入到 readyAsyncCalls 双端队列.
那么设置这个条件的目的是什么呢?从变量命名来看: runningAsyncCalls 执行中的异步任务 runningCallsForHost 同一个域名正在执行的任务数 readyAsyncCalls 预备执行的任务队列(还没有执行)
当正在执行的任务数小于最大值(默认为64)而且,同一个域名正在请求的任务数小于最大值(默认5)时,才会当即执行,不然,这个任务会被加入到 readyAsyncCalls中等待安排。
那么问题来了,readyAsyncCalls中的任务何时会被执行?追踪代码:追踪 readyAsyncCalls 的使用代码,找到 遍历这个队列的地方:
继续追踪,找到了这个 finish方法:
继续追踪finish在哪里调用的,找到两处:
因此,得出结论:在一个任务(不管同步仍是异步)结束以后,分发器中的异步任务,存在两个队列,一个 running队列,一个 ready队列,当 running队列的size小于最大值,而且同一个域名正在执行的任务数小于最大值时,能够直接加入到running队列,当即执行。若是不知足这条件,这个异步任务就会被加入到 ready队列.
在任意一个任务( 不管同步或是异步任务)执行完毕( 不管成败)以后,就会遍历 ready队列,每次从 ready队列中取出一个任务,判断同时执行的异步任务数是否达到上限,而且同一主机的访问数是否达到上限,若是都知足,就加入到running队列,而且当即执行,不知足,就中止遍历。周而复始,直到全部的异步任务都执行完。
文字不够形象,画个图表示。
关于okhttp的分发器Dispatcher用到的线程池
同步请求,没有用到线程池。
可是异步请求的代码中,有这么一句。
咱们知道,为何这里会用到线程池呢?
1.观察 同步或者异步的call的实例
那么这个 Call是什么?它是一个接口,它的惟一实现类是 RealCall,
在 RealCall
中,异步请求的执行方法, enqueue()
实际上是交给了 分发器一个 AsyncCall
对象,它继承自 NamedRunnable
可命名的 Runnable
任务。因此,这里能够用 线程池 ExecutorService
来执行这个 Runnable
.
进一步观察这个线程池的细节:
它是一个核心线程数为0的线程池,而且使用了一个无容量的阻塞队列做为参数。其实也不没必要本身去建立线程池,而能够直接使用 Executors.newCachedThreadPool();
来建立,效果同样。线程池,系统提供了有多种默认实现
为何okhttp恰恰选择了这一种?
为了实现最大并发量。
详解以下:既然这里提到了线程池,那么就把线程池的基本机制整理一下:
线程池的构造函数中,有一个阻塞队列参数。
它有3个实现类:ArrayBlockingDeque/ LinkedBlockingDeque / SynchronousQueue
是咱们线程池常常用的。前面2个都是有容量的,而第三个是无容量的,加入进去,必定会失败。而参照上面线程池的工做流程图,若是加入失败,就会尝试去非核心线程执行任务。这样,便保证了每个提交进来的异步任务,都会当即尝试去执行,而不是塞入等待队列中等待空闲线程,从而确保了 异步任务的并发。
OkHttp源码核心类之一:拦截器简述
上面讲解分发器的时候,提到了 RealCall类的 getResponseWithInterceptorChain()
方法。它是一个网络请求执行的真正核心方法。进入方法:
首先解释一下 责任链模式,它是21种基本设计模式中,行为模式中一种。下面的案例能够很好地解释它:
当一个国企要采购一批设备的时候,按照上图整个任务流程中,存在5个对象,都能对采购流程形成影响,采购任务开始的时候,是从上到下依次对采购流程负责。而总经理,他才不关心下面的人怎么操做,他只关心最后的结果。正如此案例中所述,okhttp的责任链模式,使用者也不须要关心这个请求到底经历了哪些过程,他只知道,我给了request,你就要给我response,而过程当中,发生做用的各种拦截器,无需使用者知道,这样就达成了 面向对象程序开发中的 最少知道原则。
而,这些拦截器,偏偏是okhttp的核心内容,下篇文章将会详细讲解。
本文是 okhttp的开篇,若是要详细解读 okhttp的每一个细节,每一篇文章将会显得 很是冗长并且乏味,因此我选了重要节点着重分析。就像攻城略地打天下,先 占领据点,再 企图扩张,一步一个脚印,稳扎稳打,才能长远发展
问题加群讨论哦 本专栏为那些想要进阶成为高级Android工程师所准备。 从初中级转向高级工程师须要从技术的广度,深度上都有必定的造诣。因此本专栏就主要为你们分享一些技术,技术原理等。 包含源码解析,自定义View,动画实现,架构分享等。 内容难度适中,篇幅精炼,天天只需花上十几分钟阅读便可。 你们能够跟我一块儿探讨,欢迎加群探讨,有flutter—底层开发-性能优化—移动架构—资深UI工程师 —NDK-人工智能相关专业人员和视频教学资料 。后续还有最新鸿蒙系统相关内容分享。 QQ群号:765080964
![]()