OkHttp之RequestBody请求体

Quibbler 2021-3-13 1660

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类的伴生对象中定义了四个扩展函数:分别从StringByteStringByteArrayFile扩展了构造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提供的两个实现:FormBodyMultipartBody



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())
    }



不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com