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网络请求流程。