返回顶部
首页 > 资讯 > 前端开发 > VUE >如何从设计模式看OkHttp源码
  • 244
分享到

如何从设计模式看OkHttp源码

2024-04-02 19:04:59 244人浏览 独家记忆
摘要

今天就跟大家聊聊有关如何从设计模式看OkHttp源码,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。使用读源码,首先就要从它的使用方法开始:val&n

今天就跟大家聊聊有关如何从设计模式看OkHttp源码,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

使用

读源码,首先就要从它的使用方法开始:

val okHttpClient = OkHttpClient()    val request: Request = Request.Builder()        .url(url)        .build()    okHttpClient.newCall(request).enqueue(object : Callback {        override fun onFailure(call: Call, e: ioException) {            Log.d(TAG, "onFailure: ")        }         override fun onResponse(call: Call, response: Response) {            Log.d(TAG, "onResponse: " + response.body?.string())        }    })

从这个使用方法来看,我抽出了四个重要信息:

  • okHttpClient

  • Request

  • newCall(request)

  • enqueue(Callback)

大体意思我们可以先猜猜看:

配置一个客户端实例okHttpClient和一个Request请求,然后这个请求通过okHttpClient的newCall方法封装,最后用enqueue方法发送出去,并收到Callback响应。

接下来就一个个去认证,并找找其中的设计模式。

okHttpClient

首先看看这个okhttp的客户端对象,也就是okHttpClient。

OkHttpClient client = new OkHttpClient.Builder()         .addInterceptor(new HttpLoggingInterceptor())          .readTimeout(500, TimeUnit.MILLISECONDS)         .build();

在这里,我们实例化了一个HTTP的客户端client,然后配置了它的一些参数,比如拦截器、超时时间。

这种我们通过一个统一的对象,调用一个接口或方法,就能完成我们的需求,而起内部的各种复杂对象的调用和跳转都不需要我们关心的设计模式就是外观模式(门面模式)。

外观模式(Facade  Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。

其重点就在于系统内部和各个子系统之间的复杂关系我们不需要了解,只需要去差遣这个门面 就可以了,在这里也就是OkHttpClient。

它的存在就像一个接待员,我们告诉它我们的需求,要做的事情。然后接待员去内部处理,各种调度,最终完成。

外观模式主要解决的就是降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。

这个模式也是三方库很常用的设计模式,给你一个对象,你只需要对这个对象使唤,就可以完成需求。

当然,这里还有一个比较明显的设计模式是建造者模式,下面会说到。

Request

val request: Request = Request.Builder()     .url(url)     .build()  //Request.kt open class Builder {     internal var url: HttpUrl? = null     internal var method: String     internal var headers: Headers.Builder     internal var body: RequestBody? = null      constructor() {       this.method = "GET"       this.headers = Headers.Builder()     }      open fun build(): Request {       return Request(           checkNotNull(url) { "url == null" },           method,           headers.build(),           body,           tags.toImmutableMap()       )     } }

从Request的生成代码中可以看到,用到了其内部类Builder,然后通过Builder类组装出了一个完整的有着各种参数的Request类。

这也就是典型的 建造者(Builder)模式 。

建造者(Builder)模式,将一个复杂的对象的构建与它的表示分离,是的同样的构建过程可以创建不同的表示。

我们可以通过Builder,构建了不同的Request请求,只需要传入不同的请求地址url,请求方法method,头部信息headers,请求体body即可。(这也就是网络请求中的请求报文的格式)

这种可以通过构建形成不同的表示的 设计模式 就是 建造者模式,也是用的很多,主要为了方便我们传入不同的参数进行构建对象。

又比如上面okHttpClient的构建。

newCall(request)

接下来是调用OkHttpClient类的newCall方法获取一个可以去调用enqueue方法的接口。

//使用 val okHttpClient = OkHttpClient() okHttpClient.newCall(request)  //OkHttpClient.kt open class OkHttpClient internal constructor(builder: Builder) : Cloneable, Call.Factory, websocket.Factory {   override fun newCall(request: Request): Call = RealCall(this, request, forWEBSocket = false) }  //Call接口 interface Call : Cloneable {   fun execute(): Response    fun enqueue(responseCallback: Callback)    fun interface Factory {     fun newCall(request: Request): Call   } }

newCall方法,其实是Call.Factory接口里面的方法。

也就是创建Call的过程,是通过Call.Factory接口的newCall方法创建的,而真正实现这个方法交给了这个接口的子类OkHttpClient。

那这种定义了统一创建对象的接口,然后由子类来决定实例化这个对象的设计模式就是 工厂模式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

当然,okhttp这里的工厂有点小,只有一条生产线,就是Call接口,而且只有一个产品,RealCall。

enqueue(Callback)

接下来这个方法enqueue,肯定就是okhttp源码的重中之重了,刚才说到newCall方法其实是获取了RealCall对象,所以就走到了RealCall的enqueue方法:

override fun enqueue(responseCallback: Callback) {   client.dispatcher.enqueue(AsyncCall(responseCallback)) }

再转向dispatcher。

//Dispatcher.kt    val executorService: ExecutorService     get() {       if (executorServiceOrNull == null) {         executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,             SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))       }       return executorServiceOrNull!!     }     internal fun enqueue(call: AsyncCall) {     promoteAndExecute()   }     private fun promoteAndExecute(): Boolean {     //通过线程池切换线程     for (i in 0 until executableCalls.size) {       val asyncCall = executableCalls[i]       asyncCall.executeOn(executorService)     }      return isRunning   }   //RealCall.kt   fun executeOn(executorService: ExecutorService) {        try {         executorService.execute(this)         success = true       }      }

这里用到了一个新的类Dispatcher,调用到的方法是asyncCall.executeOn(executorService)

这个executorService参数大家应该都熟悉吧,线程池。最后是调用executorService.execute方法执行线程池任务。

而线程池的概念其实也是用到了一种设计模式,叫做享元模式。

享元模式(Flyweight  Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

其核心就在于共享对象,所有很多的池类对象,比如线程池、连接池等都是采用了享元模式  这一设计模式。当然,okhttp中不止是有线程池,还有连接池提供连接复用,管理所有的socket连接。

再回到Dispatcher,所以这个类是干嘛的呢?就是切换线程用的,因为我们调用的enqueue是异步方法,所以最后会用到线程池切换线程,执行任务。

继续看看execute(this)中的this任务。

execute(this)

override fun run() {       threadName("OkHttp ${redactedUrl()}") {         try {           //获取响应报文,并回调给Callback           val response = getResponseWithInterceptorChain()           responseCallback.onResponse(this@RealCall, response)         } catch (e: IOException) {           if (!signalledCallback) {             responseCallback.onFailure(this@RealCall, e)           }          } catch (t: Throwable) {           cancel()           if (!signalledCallback) {                          responseCallback.onFailure(this@RealCall, canceledException)           }         }        }

没错,这里就是请求接口的地方了,通过getResponseWithInterceptorChain方法获取响应报文response,然后通过Callback的onResponse方法回调,或者是有异常就通过onFailure方法回调。

那同步方法是不是就没用到线程池呢?去找找execute方法:

override fun execute(): Response {   //...   return getResponseWithInterceptorChain() }

果然,通过execute方法就直接返回了getResponseWithInterceptorChain,也就是响应报文。

到这里,okhttp的大体流程就结束了,这部分的流程大概就是:

设置请求报文 -> 配置客户端参数 -> 根据同步或异步判断是否用子线程 -> 发起请求并获取响应报文 ->  通过Callback接口回调结果

剩下的内容就全部在getResponseWithInterceptorChain方法中,这也就是okhttp的核心。

getResponseWithInterceptorChain

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)     interceptors += ConnectInterceptor     if (!forWebSocket) {       interceptors += client.networkInterceptors     }     interceptors += CallServerInterceptor(forWebSocket)      val chain = RealInterceptorChain(         interceptors = interceptors         //...     )      val response = chain.proceed(originalRequest)   }

代码不是很复杂,就是 加加加 拦截器,然后组装成一个chain类,调用proceed方法,得到响应报文response。

override fun proceed(request: Request): Response {    //找到下一个拦截器   val next = copy(index = index + 1, request = request)   val interceptor = interceptors[index]      val response = interceptor.intercept(next)   return response }

简化了下代码,主要逻辑就是获取下一个拦截器(index+1),然后调用拦截器的intercept方法。

然后在拦截器里面的代码统一都是这种格式:

override fun intercept(chain: Interceptor.Chain): Response {    //做事情A     response = realChain.proceed(request)     //做事情B  }

结合两段代码,会形成一条链,这条链组织了所有连接器的工作。类似这样:

拦截器1做事情A -> 拦截器2做事情A -> 拦截器3做事情A -> 拦截器3做事情B -> 拦截器2做事情B ->  拦截器1做事情B

应该是好理解的吧,通过proceed方法把每个拦截器连接起来了。

而最后一个拦截器ConnectInterceptor就是分割事情A和事情B,其作用就是进行真正的与服务器的通信,向服务器发送数据,解析读取的响应数据。

所以事情A和事情B是什么意思呢?其实就代表了通信之前的事情和通信之后的事情。

再来个动画:

如何从设计模式看OkHttp源码

这种思想是不是有点像..递归?没错,就是递归,先递进执行事情A,再回归做事情B。

而这种递归循环,其实也就是用到了设计模式中的 责任链模式。

责任链模式(Chain of Responsibility  Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。

简单的说,就是让每个对象都能有机会处理这个请求,然后各自完成自己的事情,一直到事件被处理。Android中的事件分发机制也是用到了这种设计模式。

接下来就是了解每个拦截器到底做了什么事,就可以了解到okhttp的整个流程了,这就是下期的内容了。

先预告一波:

  • addInterceptor(Interceptor),这是由开发者设置的,会按照开发者的要求,在所有的拦截器处理之前进行最早的拦截处理,比如一些公共参数,Header都可以在这里添加。

  • RetryAndFollowUpInterceptor,这里会对连接做一些初始化工作,以及请求失败的重试工作,重定向的后续请求工作。

  • BridgeInterceptor,这里会为用户构建一个能够进行网络访问的请求,同时后续工作将网络请求回来的响应Response转化为用户可用的Response,比如添加文件类型,content-length计算添加,gzip解包。

  • CacheInterceptor,这里主要是处理cache相关处理,会根据OkHttpClient对象的配置以及缓存策略对请求值进行缓存,而且如果本地有了可⽤的Cache,就可以在没有网络交互的情况下就返回缓存结果。

  • ConnectInterceptor,这里主要就是负责建立连接了,会建立tcp连接或者TLS连接,以及负责编码解码的HttpCodec。

  • networkInterceptors,这里也是开发者自己设置的,所以本质上和第一个拦截器差不多,但是由于位置不同,用处也不同。这个位置添加的拦截器可以看到请求和响应的数据了,所以可以做一些网络调试。

  • CallServerInterceptor,这里就是进行网络数据的请求和响应了,也就是实际的网络I/O操作,通过socket读写数据。

看完上述内容,你们对如何从设计模式看OkHttp源码有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程网VUE频道,感谢大家的支持。

--结束END--

本文标题: 如何从设计模式看OkHttp源码

本文链接: https://lsjlt.com/news/83425.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何从设计模式看OkHttp源码
    今天就跟大家聊聊有关如何从设计模式看OkHttp源码,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。使用读源码,首先就要从它的使用方法开始:val&n...
    99+
    2024-04-02
  • JDK 源码 阅读 - 2 - 设计模式 - 创建型模式
    A.创建型模式抽象工厂(Abstract Factory)javax.xml.parsers.DocumentBuilderFactory  DocumentBuilderFactory...
    99+
    2024-04-02
  • JDK 源码 阅读 - 3 - 设计模式 - 结构型模式
    1.适配器(Adapter)java.util.Arrays$ArrayList(java.util.Arrays#asList()); 这里要特别注意,这里的ArrayList是Arrays的内...
    99+
    2024-04-02
  • JDK 源码 阅读 - 4 - 设计模式 - 行为型模式
    1.职责链模式(Chain of responisibility)java.util.logging.Logger#log()javax.servlet.Filter#doFilter()2.命令模式(Co...
    99+
    2024-04-02
  • 从MySQL源码看其网络IO模型
    从MySQL源码看其网络IO模型 前言 MySQL是当今最流行的开源数据库,阅读其源码是一件大有裨益的事情(虽然其代码感觉比较凌乱)。而笔者阅读一个Server源码的习惯就是先从其网络IO模型看起。于是,便有了本篇博客。 MySQL启动So...
    99+
    2017-06-01
    从MySQL源码看其网络IO模型 数据库入门 数据库基础教程 数据库 mysql
  • Java从源码看异步任务计算FutureTask
    目录了解一下什么是FutureTask?FutureTask 是如何实现的呢?FutureTask 运行流程FutureTask 的使用前言: 大家是否熟悉FutureTask呢?或...
    99+
    2024-04-02
  • 基于Android设计模式之--SDK源码之策略模式的详解
    策略模式其实特别简单(听到这句话,大家是不是心里一下子放松了?)。比如排序,官方告诉大家我这里有一个排序的接口ISort的sort()方法,然后民间各尽其能,实现这个排序的方法...
    99+
    2022-06-06
    策略模式 sdk Android
  • 如何理解设计模式之桥模式
    本篇内容介绍了“如何理解设计模式之桥模式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!举个例子桥模式的主要...
    99+
    2024-04-02
  • Python代码如何实现大话设计模式
    这篇文章将为大家详细讲解有关Python代码如何实现大话设计模式,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。本人接触到的面向对象语言中,只对C++和Python还算了解,为了加深对各个模式...
    99+
    2023-06-17
  • C++设计模式中的观察者模式一起来看看
    设计模式:观察者模式 观察者模式也称发布订阅模式,发布者发布消息,订阅者接收消息。 发布者接口 #ifndef __observerPatterns_publish_hpp__ #d...
    99+
    2024-04-02
  • 如何使用设计模式的外观模式
    这篇文章主要介绍“如何使用设计模式的外观模式”,在日常操作中,相信很多人在如何使用设计模式的外观模式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何使用设计模式的外观模式”...
    99+
    2024-04-02
  • PHP 设计模式从入门到精通
    设计模式是 php 中用于创建可维护、可扩展且可重用的代码的经过验证的解决方案。基本设计模式可分为创建型、结构型和行为型。实战案例展示了设计模式在购物车系统中的应用,包括使用工厂模式创建...
    99+
    2024-05-07
    php 设计模式
  • 如何理解command设计模式
    本篇内容介绍了“如何理解command设计模式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!command...
    99+
    2024-04-02
  • C#设计模式如何实现
    这篇文章主要介绍“C#设计模式如何实现”,在日常操作中,相信很多人在C#设计模式如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#设计模式如何实现”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!游戏...
    99+
    2023-06-30
  • 设计模式如何应对代码维护难题
    设计模式通过提供可重用和可扩展的解决方案来解决代码维护难题:观察者模式:允许对象订阅事件,并在事件发生时收到通知。工厂模式:提供了一种创建对象的集中式方式,而无需依赖具体类。单例模式:确...
    99+
    2024-05-09
    设计模式 代码维护 数据访问
  • 如何理解从观察者模式到响应式的设计原理
    这篇文章主要介绍“如何理解从观察者模式到响应式的设计原理”,在日常操作中,相信很多人在如何理解从观察者模式到响应式的设计原理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解从观察者模式到响应式的设计原理...
    99+
    2023-06-16
  • 从零开始单排学设计模式「简单工厂设计模式」黑铁 III
    阅读本文大概需要 2 分钟。...
    99+
    2023-06-05
  • Python的23种设计模式(完整版带源码实例)
    作者:虚坏叔叔 博客:https://xuhss.com 早餐店不会开到晚上,想吃的人早就来了!😄 Python的23种设计模式 一 什么是设计模式 设计模式是面对各种问题进行...
    99+
    2023-09-02
    python 设计模式 开发语言
  • Java设计模式之如何实现桥接模式
    这篇文章主要为大家展示了“Java设计模式之如何实现桥接模式”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java设计模式之如何实现桥接模式”这篇文章吧。桥接模式桥接模式是将抽象部分与它的实现部...
    99+
    2023-06-15
  • Java设计模式的单例模式如何实现
    这篇文章主要介绍了Java设计模式的单例模式如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java设计模式的单例模式如何实现文章都会有所收获,下面我们一起来看看吧。单例模式单例模式顾名思义就是单一的实例...
    99+
    2023-06-29
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作