OkHttp之Headers请求头

QuibblerQuibbler 2021-03-14 约 10 分钟 3016 次阅读

OkHttp之Headers请求头

前面看了请求Request和请求体RequestBody类实现源码,最后我们来看看请求头Headers。Headers是HTTP请求和相应的头部所包含的信息:MIME、日期、缓存、Cookie等。

1、Headers

OkHttp设计的Headers类结构如下,让我们仔细看看其中的实现。


1.1、核心成员

Headers类内部并没有用两个数组分别维护请求头中的name和value值。而是用一个集合namesAndValues维护两份一一对应的数据:name和values。这样做适合请求头信息直接发送和解析,省去网络请求的时候再对头部信息进行拼接处理。

private val namesAndValues: Array<String>

在集合中Headers信息的存储是:[name1,value1,name2,value2,name3,value3,... ],所以Headers的大小size是数据集合namesAndValues的一半,

/** Returns the number of field values. */
    val size: Int
        get() = namesAndValues.size / 2

1.2、获取Header信息

获取Headers数据,value紧接着name存储。

/** Returns the field at `position`. */
    fun name(index: Int): String = namesAndValues[index * 2]
  
    /** Returns the value at `index`. */
    fun value(index: Int): String = namesAndValues[index * 2 + 1]

1.3、获取name和values

通过names()方法可以获取Headers请求头中所有不重复的name集合:

fun names(): Set<String> {
        val result = TreeSet(String.CASE_INSENSITIVE_ORDER)
        for (i in 0 until size) {
          result.add(name(i))
        }
        return Collections.unmodifiableSet(result)
    }

借助values(name )可以获取values集合。有点奇怪,为什么获取的values会是集合,一个name不只对应一个value吗?为什么可以用一个name获取values的集合呢?后面看到通过Builder添加name-value的方法就可以发现,name是可以重复添加的,这会导致同一个name可能对应多个values。所以会返回重复name的所有value。

/** Returns an immutable list of the header values for `name`. */
  fun values(name: String): List<String> {
    var result: MutableList<String>? = null
    for (i in 0 until size) {
      if (name.equals(name(i), ignoreCase = true)) {
        if (result == null) result = ArrayList(2)
        result.add(value(i))
      }
    }
    return if (result != null) {
      Collections.unmodifiableList(result)
    } else {
      emptyList()
    }
  }

2、Builder

不能直接通过构造方法创建Headers实例,而应该通过Builder构建Headers实例。

2.1、集合成员

内部也是用一个可修改集合namesAndValues保存,最后将用于构造Headers实例。

internal val namesAndValues: MutableList<String> = ArrayList(20)

2.2、添加Header信息

通过向Headers中添加头部信息,了解Headers是如何用一个集合namesAndValues存储K-V的。以add(name,value)为例:

fun add(name: String, value: String) = apply {
      checkName(name)
      checkValue(value, name)
      addLenient(name, value)
    }

先校验name和value的合法性,再调用内部方法addLenient(name,value)完成请求头信息添加。其它add方法也是如此。

/**
     * Add a field with the specified value without any validation. Only appropriate for headers
     * from the remote peer or cache.
     */
    internal fun addLenient(name: String, value: String) = apply {
      namesAndValues.add(name)
      namesAndValues.add(value.trim())
    }

如果Headers中已有name需要更新,应使用set(name,value)方法,否者会添加重复的name到namesAndValues集合中。

/**
     * Set a field with the specified value. If the field is not found, it is added. If the field is
     * found, the existing values are replaced.
     */
    operator fun set(name: String, value: String) = apply {
      checkName(name)
      checkValue(value, name)
      removeAll(name)
      addLenient(name, value)
    }

还有很多其它添加请求头信息的便捷方法,就不一一列出。开发者在开发中应当以源码为准,实时查看源码的实现。

2.3、校验name和values

Headers内部提供两个check静态方法,用于校验Headers请求头中的name和value是否有效,checkName(name)方法实现如下,如果加了什么奇怪的name导致失败,最好来这里看看原因。

private fun checkName(name: String) {
      require(name.isNotEmpty()) { "name is empty" }
      for (i in 0 until name.length) {
        val c = name[i]
        require(c in '\u0021'..'\u007e') {
          format("Unexpected char %#04x at %d in header name: %s", c.toInt(), i, name)
        }
      }
    }

checkValue(value ,name)校验value值实现如下:

private fun checkValue(value: String, name: String) {
      for (i in 0 until value.length) {
        val c = value[i]
        require(c == '\t' || c in '\u0020'..'\u007e') {
          format("Unexpected char %#04x at %d in %s value: %s", c.toInt(), i, name, value)
        }
      }
    }

连着两天把Request、RequestBody、Headers都看完了,对其内部的实现都已经非常清楚。接下来就开始撸OkHttp网络请求流程。

相关推荐

精选
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