当前位置: 首页 > news >正文

我司如何自己建设动态网站/关键词搜索引擎又称为

我司如何自己建设动态网站,关键词搜索引擎又称为,设计类专业需要美术功底吗,临沂高端网站建设前言 框架和流程——OkHttp 源码详解(一) ConnectInterceptor 解析——OkHttp 源码详解(二) OkHttp应该是目前Android平台上使用最为广泛的开源网络库了,Android 在6.0之后也将内部的HttpUrlConnection的默认实现替换…

前言

框架和流程——OkHttp 源码详解(一)
ConnectInterceptor 解析——OkHttp 源码详解(二)

OkHttp应该是目前Android平台上使用最为广泛的开源网络库了,Android 在6.0之后也将内部的HttpUrlConnection的默认实现替换成了OkHttp。

网上很多分析OkHttp的,都是在总体流程上,没有那么的细致,甚至有的同学看完了文章,认为OkHttp没有DNS解析。所以本系列会深入源码,既掌握结构,也了解细节

一、鸟瞰OkHttp

下面的代码是一个很简单的例子,一步一步分析,起内部的工作原理

 OkHttpClient client = new OkHttpClient();// Create request for remote resource.Request request = new Request.Builder().url(ENDPOINT).build();// Execute the request and retrieve the response.Response response = client.newCall(request).execute()

OkHttp 的整体架构是很简单的,Request 作为请求, Response作为响应,在RealCall 中处理同步异步请求,处理过程就是一系列的拦截器。
在这里插入图片描述
图片来自

open class OkHttpClient internal constructor(builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {// 使用默认的Builder 来创建OkHttpClientconstructor() : this(Builder())...省略代码...init {...省略代码...//获取证书信任管理器	this.x509TrustManager = Platform.get().platformTrustManager()//用指定的证书信任管理器,来创建一个sslSocket工厂this.sslSocketFactoryOrNull =Platform.get().newSslSocketFactory(x509TrustManager!!)//省略与TLS握手无关的意外证书,并提取受信任的CA证书以使证书固定。this.certificateChainCleaner = CertificateChainCleaner.get(x509TrustManager!!)//用来限制哪些证书可以被信任this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(certificateChainCleaner!!)...省略代码...verifyClientState()}/** Prepares the [request] to be executed at some point in the future. */override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)...省略代码...//Builder 模式,可以定制OkHttp
class Builder constructor() { //异步调用分配者,通过线程池,来分别调用internal var dispatcher: Dispatcher = Dispatcher()//线程池,所有的有效链接都会保存在这里,也优先在这里查找是否有可用的链接internal var connectionPool: ConnectionPool = ConnectionPool()//自定义拦截器 集合internal val interceptors: MutableList<Interceptor> = mutableListOf()//网络拦截器 集合internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()//事件监听,一些事件发生后,回调该接口,比如网络链接成功,tls握手成功,dns解析开始结束等,internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()//cookieinternal var cookieJar: CookieJar = CookieJar.NO_COOKIES//缓存internal var cache: Cache? = null//解析dns的类internal var dns: Dns = Dns.SYSTEM//通过代理来访问网络,例如,socket代理,http代理等 ,通过http代理 访问https,需要建立通道 Tunnelinternal var proxy: Proxy? = null//代理选择器internal var proxySelector: ProxySelector? = nullinternal var proxyAuthenticator: Authenticator = Authenticator.NONE//负责创建socket连接internal var socketFactory: SocketFactory = SocketFactory.getDefault()//负责创建SSLSocket连接 ,若无指定,在初始化OkHttp的时候 被赋值internal var sslSocketFactoryOrNull: SSLSocketFactory? = null//X509证书信任管理器,若无指定,在初始化OkHttp的时候 被赋值internal var x509TrustManagerOrNull: X509TrustManager? = nullinternal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECSinternal var protocols: List<Protocol> = DEFAULT_PROTOCOLSinternal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier//用来限制哪些证书可以被信任internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT//省略与TLS握手无关的意外证书,并提取受信任的CA证书以使证书固定。internal var certificateChainCleaner: CertificateChainCleaner? = nullinternal var callTimeout = 0internal var connectTimeout = 10_000internal var readTimeout = 10_000internal var writeTimeout = 10_000internal var pingInterval = 0internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE//每一个Route 对应一个Url 链接internal var routeDatabase: RouteDatabase? = null...省略代码...}}

示例中,调用newCall后,创建了RealCall 对象,调用execute() 执行同步请求,调用enqueue 执行异步请求

同步操作

//同步操作,override fun execute(): Response {check(executed.compareAndSet(false, true)) { "Already Executed" }timeout.enter()callStart()try {//这里的dispatcher 是在创建OkHttp 的时候创建的//因为这个是同步请求操作,所以executed 只是把RealCall对象放入到runningSyncCalls 堆中,表示正在进行client.dispatcher.executed(this)//所有的拦截器都在这里执行,OkHttp 的核心就是拦截器,所有的操作都是以拦截器的形式(责任链模式),例如Dns解析,socket连接,tls连接,缓存,网络请求,自定义拦截器等等return getResponseWithInterceptorChain()} finally {//把RealCall对象从runningSyncCalls 堆中删除,并调用promoteAndExecute() ,来执行已经准备好的异步操作client.dispatcher.finished(this)}}

异步操作

// 异步操作,因为是异步的,所以需要设置回调接口override fun enqueue(responseCallback: Callback) {check(executed.compareAndSet(false, true)) { "Already Executed" }callStart()//用回调接口创建一个AsyncCall对象,下面介绍AsyncCall//enqueue 只是把该请求对象,放入readyAsyncCalls堆中,然后调用promoteAndExecute()client.dispatcher.enqueue(AsyncCall(responseCallback))}

下面到Dispatcher 中 去看看

internal fun enqueue(call: AsyncCall) {synchronized(this) {// 把异步请求,加入到堆中readyAsyncCalls.add(call)...省略代码...}//处理异步请求promoteAndExecute()}private fun promoteAndExecute(): Boolean {this.assertThreadDoesntHoldLock()val executableCalls = mutableListOf<AsyncCall>()val isRunning: Booleansynchronized(this) {//从堆中拿出异步请求对象val i = readyAsyncCalls.iterator()while (i.hasNext()) {val asyncCall = i.next()if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.i.remove()asyncCall.callsPerHost.incrementAndGet()//如果符合上面的两个条件,就把异步请求加入到executableCallsexecutableCalls.add(asyncCall)runningAsyncCalls.add(asyncCall)}isRunning = runningCallsCount() > 0}for (i in 0 until executableCalls.size) {val asyncCall = executableCalls[i]//在线程池中依次执行 asyncCallasyncCall.executeOn(executorService)}return isRunning}

异步请求对象AsyncCall

internal inner class AsyncCall(private val responseCallback: Callback) : Runnable {...省略代码.../*** Attempt to enqueue this async call on [executorService]. This will attempt to clean up* if the executor has been shut down by reporting the call as failed.*/fun executeOn(executorService: ExecutorService) {client.dispatcher.assertThreadDoesntHoldLock()var success = falsetry {// 通过线程池,来调用AsyncCall,它是继承自Runnable,真正的实现在run函数中executorService.execute(this)success = true} catch (e: RejectedExecutionException) {...省略代码...} finally {if (!success) {client.dispatcher.finished(this) // This call is no longer running!}}}override fun run() {threadName("OkHttp ${redactedUrl()}") {var signalledCallback = falsetimeout.enter()try {//和同步请求一样的处理方式,返回处理结果val response = getResponseWithInterceptorChain()signalledCallback = true//通过回调接口来返回请求结果,这里实在线程中返回的,所以不能直接在回调接口中进行UI操作responseCallback.onResponse(this@RealCall, response)} catch (e: IOException) {...省略代码...} finally {client.dispatcher.finished(this)}}}}

网络请求的大概流程,算是搞清楚了,接下来就来看看OkHttp核心——拦截器

二、OkHttp的核心:拦截器

这里主要分析,所有的拦截器是如何进行组织,链式调用的 ,具体每个拦截器的处理方式先不展开

一图胜千言,拦截器的调用,在拦截器A的任意位置,对用拦截器B,在B返回后,可继续执行A
在这里插入图片描述
图片来源

从前面的同步请求和异步请求可以看出,最终都是要调用getResponseWithInterceptorChain()来处理,下面就来看看这个函数到底都做了些什么

  @Throws(IOException::class)internal fun getResponseWithInterceptorChain(): Response {// Build a full stack of interceptors.val interceptors = mutableListOf<Interceptor>()//自定义应用拦截器interceptors += client.interceptors//interceptors += RetryAndFollowUpInterceptor(client)//interceptors += BridgeInterceptor(client.cookieJar)//缓存拦截器interceptors += CacheInterceptor(client.cache)//链接拦截器,dns解析,socket链接,tls链接,代理处理等interceptors += ConnectInterceptorif (!forWebSocket) {//自定义网络拦截器interceptors += client.networkInterceptors}interceptors += CallServerInterceptor(forWebSocket)//创建一个RealInterceptorChain,把所有拦截器传入,算是拦截器处理的入口val chain = RealInterceptorChain(call = this, // 方便在拦截器中,获取当前的RealCall对象,因为有些操作是在RealCall对象里面的interceptors = interceptors, //传入拦截器集合index = 0, //默认从第一个拦截器开始,随着程序的运行,RealInterceptorChain中的这个变量会增加exchange = null,request = originalRequest,//网络请求,//以下是超时时间设置connectTimeoutMillis = client.connectTimeoutMillis,readTimeoutMillis = client.readTimeoutMillis,writeTimeoutMillis = client.writeTimeoutMillis)var calledNoMoreExchanges = falsetry {//开始执行 拦截器val response = chain.proceed(originalRequest)if (isCanceled()) {response.closeQuietly()throw IOException("Canceled")}return response} catch (e: IOException) {calledNoMoreExchanges = truethrow noMoreExchanges(e) as Throwable} finally {if (!calledNoMoreExchanges) {noMoreExchanges(null)}}}

拦截器处理的核心,
1、这些拦截器需要按照顺序来依次执行,所以所有的拦截器必须有统一的接口,这样不管是什么拦截器,调用的方式都是一致的。(依赖倒置原则) 所有的拦截器都实现了Interceptor接口

2、 如何依次调用每个拦截器,其实 每个拦截器对应一个 RealInterceptorChain,拦截器在集合中的索引值 = RealInterceptorChain中的index,在调用拦截器 时,把 next(RealInterceptorChain对象)传入interceptor.intercept(next) 这样就可以在当前拦截器的任意位置,调用下一个拦截器了。

有同学可能有疑问,直接一个循环,遍历每个拦截器,调用每个Interceptor接口,这样不行吗?

这样没办法实现 在当前拦截器的任意位置调用下一个拦截器(每个拦截器的处理逻辑不一样,调用下一个拦截器的时机也不同)

class RealInterceptorChain(internal val call: RealCall,private val interceptors: List<Interceptor>,private val index: Int,internal val exchange: Exchange?,internal val request: Request,internal val connectTimeoutMillis: Int,internal val readTimeoutMillis: Int,internal val writeTimeoutMillis: Int
) : Interceptor.Chain {private var calls: Int = 0// 创建一个RealInterceptorChain 对象,为了调用下一个拦截器 (集合中index对应的拦截器)internal fun copy(index: Int = this.index,exchange: Exchange? = this.exchange,request: Request = this.request,connectTimeoutMillis: Int = this.connectTimeoutMillis,readTimeoutMillis: Int = this.readTimeoutMillis,writeTimeoutMillis: Int = this.writeTimeoutMillis) = RealInterceptorChain(call, interceptors, index, exchange, request, connectTimeoutMillis,readTimeoutMillis, writeTimeoutMillis)...省略代码...override fun call(): Call = calloverride fun request(): Request = request// 上面调用了这个函数,从这里开始处理拦截器@Throws(IOException::class)override fun proceed(request: Request): Response {check(index < interceptors.size)calls++if (exchange != null) {check(exchange.finder.sameHostAndPort(request.url)) {"network interceptor ${interceptors[index - 1]} must retain the same host and port"}check(calls == 1) {"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"}}// Call the next interceptor in the chain.//指定下一个拦截器的索引值,也就是当前拦截器索引值+1val next = copy(index = index + 1, request = request)//获取当前的拦截器val interceptor = interceptors[index]@Suppress("USELESS_ELVIS")//调用拦截器val response = interceptor.intercept(next) ?: throw NullPointerException("interceptor $interceptor returned null")if (exchange != null) {check(index + 1 >= interceptors.size || next.calls == 1) {"network interceptor $interceptor must call proceed() exactly once"}}check(response.body != null) { "interceptor $interceptor returned a response with no body" }return response}
}

看到这里你可能还不清楚,这些拦截器到底是如何依次被触发的,在分析 ConnectInterceptor拦截器的文章 中,你可以更清楚的看到,下一个拦截器的触发

三、 关于自定义拦截器

在代码中,可以通过 addInterceptor() 和 addNetworkdInterceptor() 来添加自己的拦截器,分别叫做应用拦截器和网络拦截器,他们的调用时机如下图:

在这里插入图片描述

getResponseWithInterceptorChain源码中可以看到,

  • 应用拦截器 interceptors 是最先被加入集合的,
  • 网络拦截器networkInterceptors 是在ConnectInterceptor 之后被添加的,

也就是说应用拦截器是最先被调用的,网络拦截器是在网络链接后才被调用,如果发生地址重定向,网络连接器会被多次调用

自定义拦截器的使用、区别、示例代码,可以查看这篇文章Interceptors拦截器——OkHttp3详细使用教程

结语

OkHttp的大概流程,就算是清楚了,接下来,就是针对每个拦截器的分析了。掌握了架构,再去补充细节,就容易掌握一些

请点赞、收藏,感谢大家的支持,欢迎评论区提问、吐槽

http://www.lbrq.cn/news/1293859.html

相关文章:

  • 信息课做网站的软件/seo咨询师
  • 做学校网站的目的是什么/恶意点击软件哪个好
  • 上海建筑工程股份有限公司/江西短视频seo搜索报价
  • 做的美食视频网站/百度推广可以自己开户吗
  • 澳门网站建设/免费学生网页制作成品代码
  • 酒店电子商务网站策划书/制作一个网站大概需要多少钱
  • 网站建设百度搜索到左边的图/网络营销管理
  • js网站建设/b站2020推广网站
  • 常德公司做网站/百度广告联盟平台的使用知识
  • 实验一html静态网站开发/百度上海总部
  • 直播视频网站如何做/营销管理制度范本
  • 如何建立免费网站的步骤/内江seo
  • 聊城哪里做优化网站/点击器
  • 网站 空间 备案/百度手机助手app官方下载
  • 做影视后期有哪些资源网站/网络营销的分类
  • 沈阳市工伤网站做实/如何做好seo基础优化
  • tp框架做的网站/青岛网站seo服务
  • 网站建设有哪些软件有哪些/挖掘关键词的工具
  • wordpress注册开启邮件验证/广州软件系统开发seo推广
  • 好听大气的公司名称/杭州seo网络推广
  • 大连网站优化/幽默广告软文案例
  • 建站技术分享/seo营销推广
  • 旅游网站名字/谷歌广告联盟怎么做
  • 门户网站管理建设/网络口碑推广公司
  • 专门做进口零食的网站/想要网站导航推广
  • 网站流量平台/vivo应用商店
  • 中国企业信息网/优化服务内容
  • 建设职业技术学院网站/百度推广官网电话
  • 网站推广策划的思路包括哪些内容/个人网站推广怎么做
  • 广州黄埔做网站/seo 首页
  • boost::asio 中 io_service与线程的关系
  • 树链剖分-苹果树
  • Unity 插件Resize Pro 最快的 Texture2D 调整大小工具
  • Linux场景常见的几种安装方式
  • Kubernetes中为ELK组件配置持久化存储
  • FFmpeg 图片处理