不是吧!做了3年Android还没看过OkHttp源码?好吧,带你彻底理解一波!

前言

现在Android界的网络请求已经是OkHttp和Retrofit的天下了,Retrofit本质上也是将请求委托给了OkHttp,所以我们如果想要能够更加全面的使用和掌握OkHttp,了解其源码是必不可少的。 如果对Http还有不了解的小伙伴,可以一下这篇文章,带你全面掌握Http协议 面试官的这份HTTP灵魂追问你Hold住吗?

作者:Mlx
链接:https://juejin.im/post/6881436122950402056

OK,出发~

简单使用

同步方式和异步方式殊途同归,而且异步方式更多了异步和线程的概念,所以本文将直接分析异步,同步的话流程也是一样的。

val client=OkHttpClient().newBuilder().build()
val request=Request.Builder().url("https://www.baidu.com").build()
val call = client.newCall(request)
call.enqueue(object :Callback{
      override fun onFailure(call: Call, e: IOException) {
          println("onFailure:$e")
      }

      override fun onResponse(call: Call, response: Response) {
          println("onResponse:${response.body?.string()}")
      }

})

这可以说是最简单的使用方式了。

使用 OkHttp 基本是以下四步:

  1. 创建 OkHttpClient 对象
  2. 创建Request请求对象
  3. 创建Call对象
  4. 同步请求调用call.execute();异步请求调用call.enqueue(callback)

这都是啥?一个简单的请求为啥需要这么多对象呢?

简单架构

那好,我们来分析一下他们的职责,其实啊一次网络请求和寄快递没啥区别。

首先,你在淘宝买了一个东西,发现不满意,要换货,你得去快递点(OkHttpClient ),然后填写快递单(Request)表明要寄给谁,寄的啥东西,填完以后你交个快递小哥,小哥把你这个快递给你寄出去(Call)。如果是同步请求(call.execute)的话,你就在这等着快递寄出去,然后卖家再发给你换好的货(Response)。显然,这么等的话怕是等个三四天,所以我们把快递给了快递小哥以后,告诉小哥我的电话(CallBack回调),说货到了通知我(触发回调 onResponse/onFailure)。

这样一来是不是很简单了

什么?还不够清晰明了?靠,只能把大宝贝儿掏出来了,看图

好,到此结束,OKHTTP分析完毕!

什么?这就完事了?我不是听说还有啥拦截器,什么责任链,什么缓存balabla的,你这叫我和面试官如何对线?

好吧,那我们接着下面看OkHttp做了什么。

构建OkHttpClient对象

我们一步一步来,先分析我们如何鬼使神差的走进快递点的,啊不,构建OkHttpClient对象的

val client=OkHttpClient().newBuilder().build()

这行代码是构建OkHttpClient对象的,很明显有个Builder对象,这明眼人一看就知道是构建者模式去构建OkHTTPClient对象。

什么?构建者模式是什么

构建者模式一般适用于创建比较复杂的对象使用的,可以屏蔽用户对细节的感知balabala

我最讨厌说定义了,我这么说吧,你如果创建一个对象构造函数中参数特别多的时候,大部分情况下你用构建者就行了。

为什么?就比如说你想整个车,你可以直接说,来奔驰,给我整俩车。你对如何造车完全不知道,但这并不影响你提车泡妹。

可是你不是说参数比较多的时候用构建者么?

没错,参数比较多的时候你是不是得一个个传参,还得小心参数的位置啥的?当然了,在Kotlin中可以指定哪个参数,它本质上和构建者也差不多。你也不想提车的时候还得小心翼翼的和厂家一一说明要求吧,我得有个四个轮的车,白色的,能坐四个人的等等要求。

所以构建者模式就是为了解决此问题,你可以没有要求,我就使用默认值,你可以有很多要求,甚至可以重复,反正我只在你提车的时候最终确认即可给你符合你要求的车。

所以如果使用构建者模式的话,我们不必为参数的位置和忘了传什么参数而烦忧,我们可以写出如下代码:

val client=OkHttpClient()
    .newBuilder()
    .dispatcher()
    .callTimeout()
    .authenticator()
    .addInterceptor()
    .cache()
    .build()

你看你可以使用构建者模式去创建出符合你所有要求的对象,不用担心参数位置,或者忘记传哪个参数。

当然,本篇不是讲设计模式的,随后我将会开辟一个新的系列,专门讲设计模式。这里点到为止。

我们继续回到我们的OkHttp来,使用Builder对象创建了OkHttpclient对象过程中,其实给OkHttpclient创建了一系列的配置,我们这里简要的看一下,在newBuilder方法中创建了一个Builder对象:

class Builder constructor() {
    internal var dispatcher: Dispatcher = Dispatcher()//调度器,一会讲
    internal val interceptors: MutableList<Interceptor> = mutableListOf()//拦截器,一会也会讲
    internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()//网络拦截器,也会讲
    ...//定义很多其他对象
}

在这里省略了很多对象,只列出了三个比较重要的对象,一会都会讲的。

build方法真正构建了OkHttpclient对象:

fun build(): OkHttpClient = OkHttpClient(this)

这个this是什么?

这个build方法是Builder对象的build方法,this当然是Builder对象啦。这里真正创建了一个OkHTTPClient对象,并把Builder对象自身传了过去。在OkHttpclient对象的构造方法中有如下代码:

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
  @get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher //调度器
  @get:JvmName("interceptors") val interceptors: List<Interceptor> =
      builder.interceptors.toImmutableList()//拦截器集合
  @get:JvmName("networkInterceptors") val networkInterceptors: List<Interceptor> =
      builder.networkInterceptors.toImmutableList()//网络拦截器集合
}

可以看到,最终builder对象中的拦截器全部给了OkHttpClient对象,这也正是构建者模式的体现之处。

简而言之,就是OkHttpClient里面定义了很多我们不知道的对象。正如你寄快递的时候,你并不知道快递是如何寄出去的,OkHttp帮你完成了很多的细节工作。

接下来就是构建Request对象了

构建Request对象

你想寄快递,必须填写快递单,写你要寄给谁,寄到哪里,怎么寄平邮还是快件,这一切就是Request对象的职责。它负责记录你的请求信息。

val request=Request.Builder().url("https://www.baidu.com").build()

经过上面的分析,我们已经知道,构建者Builder中所有的参数最终全部会给到Request对象中。所以我们直接看看Request对象的构造方法中有什么东西~

class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,//ur,也就是你要发送的地方
  @get:JvmName("method") val method: String, //请求方法,GET/POST
  @get:JvmName("headers") val headers: Headers,//请求头,一些备注信息或者请求信息
  @get:JvmName("body") val body: RequestBody?,//请求的内容,就是你具体要发送的快递不能
  internal val tags: Map<Class<*>, Any>
) {
    ....
}

注释已经讲的很清楚了,关于HTTP网络这块,大家不太懂的可以看我之前的文章,从HTTP表层到原理全部都有讲解。

接下来也没啥内容了,该轮到Call对象了

创建Call对象

val call = client.newCall(request)

正如你想寄快递,你必须填写好快递单交给快递站的小哥才行。

这里你已经填好了快递单,你需要交给快递站,然后快递站派出快递小哥帮你发快递。

快递小哥就是Call,我们看看是Call是什么东东:

interface Call : Cloneable {

  fun request(): Request//返回请求信息,就是返回你填写的快递单
  @Throws(IOException::class)
  fun execute(): Response//同步请求,就是你在快递站傻等~
  fun enqueue(responseCallback: Callback)//异步请求,接受回调参数。就是你告诉快递小哥你电话,让小哥寄快递
  fun cancel()//取消请求,就是你取消寄快递
  fun isExecuted(): Boolean//是否正在请求,就是你的快递寄出去了没,寄到哪了
  fun isCanceled(): Boolean//是否是取消状态
  fun timeout(): Timeout //超时时间,就是你这个快递寄出去半年了都没动静,你就认为中途丢了
  public override fun clone(): Call
  fun interface Factory {
    fun newCall(request: Request): Call
  }
}

Call本质上是个接口,也符合我们的面向接口编程。它里面定义了很多方法,在上面注释中我也都详细阐述了。可是接口并不能具体帮我们干活啊,就像你知道你要找一个快递小哥,快递小哥是一个抽象的概念并不是一个具体存在的人,你只知道快递小哥能帮你寄快递,这个寄快递就是一个抽象的方法。所以我们需要知道到底是哪个快递小哥帮我们寄了快递。

我们看一下这个newCall方法:

//OkHttpClient.kt
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

啊知道了,RealCall才是真正为我们服务的那个快递小哥。创建RealCall的过程中,我们传进去一共三个参数,第一个this自然就是OkHttpclient对象本身了,第二个参数是请求信息也就是快递单,第三个参数是用于指明不是webSocket请求。暂时不用管第三个参数

好,我们现在万事俱备只欠东风,所有的对象都已经创建完成了,可以寄快递了吧?

发送请求

发送请求就是将你的电话号告诉给快递小哥,快递小哥寄出去以后,收到快递再给你打电话告诉你快递到了或者丢了。对应的就是以下代码。

call.enqueue(object :Callback{
    override fun onFailure(call: Call, e: IOException) {
        println("onFailure:$e")
    }
    override fun onResponse(call: Call, response: Response) {
        println("onResponse:${response.body?.string()}")
    }
})

前面也说了call.enqueue是一个异步方法,我们既然是从源码开始学习的,自然不能只停留在表面,当然要深入源码去探索,看看这个发送的过程到底是怎么样的过程。在上面我们知道,这个快递小哥是一个宽泛的概念(接口),真正执行任务的时候要具体到某个人的(实现类),这个具体的人就是RealCall对象,所以我们看

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值