OkHttp之RequestBody请求体

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

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

1.8k
OkHttp线程池和连接池
开源

OkHttp线程池和连接池

OkHttp线程池和连接池了解了OkHttp的网络请求流程以及拦截器实现原理,再关注OkHttp中两个重要的:OkHttp的线程池和连接池。1、OkHttp线程池在OkHttp网络请求流程一文中,我们分析了OkHttp异步和同步请求流程。请求最后都在Dispatcher中分发调度处理,最后被ExecutorService执行。1.1、DispatcherDispatcher中执行任务的执行器是ex

5.2k
优美的开源动效库:Lottie
开源

优美的开源动效库:Lottie

优美的开源动效库:Lottie1、强大的动效LottieLottie是一个适用于Android,iOS,Web和Windows的库,它可以使用Bodymovin解析以json格式导出的Adobe After Effects动画,并在移动设备和Web上原生渲染它们!GitHub:https://github.com/airbnb/lottie-androidLottie官网:http://airbn

4.3k