OkHttp之RequestBody请求体

QuibblerQuibbler 2021-03-13 约 10 分钟 3339 次阅读

OkHttp之RequestBody请求体

继OkHttp之Request请求一文,看完Request类,内部存储了和请求相关的数据,并没有什么复杂的逻辑。接着看看RequestBody类内部实现是怎样的。

1、RequestBody

RequestBody是抽象类。


1.1、两个抽象方法

有两个抽象方法contentType()和writeTo(sink: BufferedSink)。需要具体的RequestBody实例提供实现:表明数据的媒体类型;提供写入数据内容。

/** Returns the Content-Type header for this body. */
    abstract fun contentType(): MediaType?
    
    /** Writes the content of this request to [sink]. */
    abstract fun writeTo(sink: BufferedSink)

1.2、基本方法

RequestBody内还定义了三个基本方法,描述网络请求的一些属性信息。方法是open的,实现类可重写这些方法。

/**
     * 返回将写入对[writeTo]的调用中的接收器的字节数;如果该计数未知,则返回-1。
     */
    open fun contentLength(): Long = -1L
    
    /**
     * 是否双工:指二台通讯设备之间,允许有双向的资料传输。
     * [grpc]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
     */
    open fun isDuplex(): Boolean = false
    
    /**
     * 如果此主体希望最多调用一次[writeTo],并且最多可以传输一次,则返回true。
     */
    open fun isOneShot(): Boolean = false

1.3、扩展函数

在RequestBody类的伴生对象中定义了四个扩展函数:分别从String、ByteString、ByteArray、File扩展了构造RequestBody实例的方法。

fun String.toRequestBody(contentType: MediaType? = null): RequestBody
    fun ByteString.toRequestBody(contentType: MediaType? = null): RequestBody 
    fun ByteArray.toRequestBody(contentType: MediaType?,offset,byteCount): RequestBody
    fun File.asRequestBody(contentType: MediaType? = null): RequestBody

RequestBody类源码并不多,继续看OkHttp提供的两个实现:FormBody和MultipartBody。

2、FormBody

FormBody实现了RequestBody类的两个抽象方法,携带表单请求数据。


2.1、成员及方法

内部使用两个一样大小的数组保存name及对应的value。 

private val encodedNames: List<String> = encodedNames.toImmutableList()
    private val encodedValues: List<String> = encodedValues.toImmutableList()
    ....
    //提供方法获取表单数据K-V
    fun encodedName(index: Int) = encodedNames[index]
    fun name(index: Int) = encodedName(index).percentDecode(plusIsSpace = true)
    fun encodedValue(index: Int) = encodedValues[index]
    fun value(index: Int) = encodedValue(index).percentDecode(plusIsSpace = true)

2.2、内部类Builder

无法直接使用FormBody的构造方法创建表单请求体实例,需要借助内部的Builder类构造,调用add()方法添加表单数据

fun add(name: String, value: String)
    fun addEncoded(name: String, value: String)

最后调用Builder的build()方法直接创建FormBody表单实例。

fun build(): FormBody = FormBody(names, values)

3、MultipartBody

MultipartBody继承自RequestBody抽象类,用来携带分块请求数据。MultipartBody的实现稍微复杂一些。


3.1、内部类:Part分块数据

通过它的内部构造方法不难看出,是通过Part类型集合存储分块的数据。

class MultipartBody internal constructor(
        val type: MediaType,
        val parts: List<Part>
    )

Part是MultipartBody中的内部类,携带每个分块的数据:Headers和RequestBody。

class Part private constructor(
        val headers: Headers?,
        val body: RequestBody
    )

3.2、内部类Builder

同样的,我们无法直接通过MultipartBody构造函数创建实例,而应该使用MultipartBody类内部的Builder,Builder内部三个成员和MultipartBody一一对应:

class Builder  {
        private val boundary: ByteString = boundary.encodeUtf8()
        private var type = MIXED
        private val parts = mutableListOf<Part>()
        ...
    }

可以看到MultipartBody默认MIME类型是"multipart/mixed"。当然可以调用setType(type: MediaType)方法设置当前构造的分块请求体数据类型。

fun setType(type: MediaType) = apply {
      require(type.type == "multipart") { "multipart != $type" }
      this.type = type
    }

通过Builder的addPart(...)方法添加各种分块RequestBody,以及对应的Headers(如果有的话):

/** Add a part to the body. */
    fun addPart(body: RequestBody)
    
    /** Add a part to the body. */
    fun addPart(headers: Headers?, body: RequestBody)
    
    /** Add a form data part to the body. */
    fun addFormDataPart(name: String, value: String)
    
    /** Add a form data part to the body. */
    fun addFormDataPart(name: String, filename: String?, body: RequestBody)
    
    /** Add a part to the body. */
    fun addPart(part: Part)

最后,调用build()创建并返回MultipartBody实例。

fun build(): MultipartBody {
      return MultipartBody(boundary, type, parts.toImmutableList())
    }

相关推荐

Airbnb开源项目
精选开源

Airbnb开源项目

Airbnb Engineering & Data ScienceAirflow Use Apache Airflow (incubating) to author workflows as directed acyclic graphs (DAGs) of tasks12,263Airpal Web UI for PrestoDB2,502Aerosolve A machine learning

2.3k
WilliamChart:轻量美观的 Android 图表库实战
精选开源

WilliamChart:轻量美观的 Android 图表库实战

WilliamChart:轻量美观的 Android 图表库实战数据可视化是运动、健康、金融、统计类 App 的刚需。MPAndroidChart 功能最全但偏重复杂,WilliamChart 走的则是"轻量 + 美观 + Kotlin 原生"路线——v3 用 Kotlin 完全重写,API 极简,一行 chart.animate(dataSet) 就能把数据画成带动画的折线/柱状/环形图,开箱即

175
sherpa解析:开源语音技术全家桶
精选开源

sherpa解析:开源语音技术全家桶

想在手机、树莓派甚至 RISC-V 开发板上离线跑语音识别,五年前还是个需要啃 Kaldi 源码的硬核课题。k2-fsa 组织的 sherpa 把这件事变成了"下载预训练模型、装个包、跑示例"的三步操作。这个以 Apache-2.0 协议开源的项目自我定位是"新一代 Kaldi 的语音转文本框架",它专注端到端模型的推理部署:transducer 与 CTC 两类架构,C++ 与 Python 双…

44