当前位置:   article > 正文

android okhttp打印请求头_Android-Okhttp3源码解析

okhtttp 打印header数据

网络库的选择

理论上来说几乎所有的网络库都是基于Socket实现的,在Socket的基础上可以实现各种应用层的通信协议,比http、ftp等。Java实现的网络库理论上来说都可以在Android端上使用,但由于Android这种嵌入式系统的特性,对网络请求库的要求可能会更严格。

HttpUrlConnection和HttpClient在API23之后已经从系统源码中移除了,开发时如果要使用到网络请求,可能需要额外引入一些第三方库。目前常用的网络请求库包括android-async-http、Volley、Okhttp以及Retrofit。Retrofit其实并不是一个网络请求库,更准确地说它是一个网络封装库,能够将底层的网络请求库封装成RESTful API设计风格。其中android-async-http是基于HttpClient封装的,主要是封装了异步线程与main线程之间的切换以及智能请求重试,持久化、cookie保存到SP等。Vollery是曾经最有名的Android请求库,基于HttpUrlConnection,支持图片加载,网络请求排序,优先级处理缓存与Activity生命周期联运等。其实Vollery的底层网络请求也可以使用其他网络请求库替换。OkHttp是近几年来最受欢迎的网络库了,现在几乎所有的app的网络请求库都使用OkHttp或者基于OkHttp来定制的。OkHttp已经不是基于HttpUrlConnection了,而是使用Socket来实现了一套网络请求。OkHttp支持同步、异步,封装了线程池、数据转换、参数使用,错误处理等。并且Socket还使用了NIO,在非阻塞的技术下更大程度地提高网络请求的性能。

OkHttp的demo使用

  1. package com.benson.android.network
  2. import android.graphics.Color
  3. import android.os.Bundle
  4. import android.view.Gravity
  5. import android.view.ViewGroup
  6. import android.widget.*
  7. import androidx.appcompat.app.AppCompatActivity
  8. import kotlinx.coroutines.GlobalScope
  9. import kotlinx.coroutines.launch
  10. import okhttp3.OkHttpClient
  11. import okhttp3.Request
  12. import java.lang.StringBuilder
  13. import java.util.concurrent.TimeUnit
  14. class Okhttp3DemoActivity: AppCompatActivity() {
  15. val client by lazy { OkHttpClient.Builder()
  16. .retryOnConnectionFailure(true) // 连接超时重试
  17. .connectTimeout(2L, TimeUnit.MINUTES) // 2s 连接超时
  18. .readTimeout(2L, TimeUnit.MINUTES) // 2s 读超时
  19. .writeTimeout(2L, TimeUnit.MINUTES) // 2s 写超时
  20. .build()
  21. }
  22. override fun onCreate(savedInstanceState: Bundle?) {
  23. super.onCreate(savedInstanceState)
  24. val content = LinearLayout(this)
  25. content.orientation = LinearLayout.VERTICAL
  26. initView(content)
  27. setContentView(content)
  28. }
  29. private fun initView(content: ViewGroup) {
  30. val searchBar = LinearLayout(this)
  31. searchBar.orientation = LinearLayout.HORIZONTAL
  32. searchBar.gravity = gravity.center is For Sale
  33. val input = EditText(this)
  34. input.textSize = DisplayUtil.sp2px(this, 10.0F) * 1.0F
  35. searchBar.addView(input, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 7.0F))
  36. val searchBtn = Button(this)
  37. searchBtn.text = "search"
  38. searchBtn.textSize = DisplayUtil.sp2px(this, 10.0F) * 1.0F
  39. searchBar.addView(searchBtn, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 2.0F))
  40. content.addView(searchBar, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0F))
  41. val result = TextView(this)
  42. result.setBackgroundColor(color.black - 这个网站可出售。 - 最佳的color 来源和相关信息。)
  43. result.setTextColor(Color.WHITE)
  44. result.textSize = DisplayUtil.sp2px(this, 15.0F) * 1.0F
  45. val scrollView = ScrollView(this)
  46. scrollView.addView(result)
  47. content.addView(scrollView, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 10.0F))
  48. searchBtn.setOnClickListener {
  49. search(result, input.text.toString())
  50. }
  51. }
  52. private fun search(result: TextView, url: String) {
  53. GlobalScope.launch {
  54. val request = Request.Builder()
  55. .url(url)
  56. .get()
  57. .build()
  58. val response = client.newCall(request).execute()
  59. val content = response.body?.string()
  60. val resultStr = StringBuilder()
  61. resultStr.append("code=").append(response.code).append("n")
  62. .append("msg=").append(response.message).append("n")
  63. .append("headers=").append(response.headers).append("n")
  64. .append(content)
  65. result.post {
  66. result.text = resultStr.toString()
  67. }
  68. }
  69. }
  70. }

执行之后效果图如下

18f3b9e70f1eb0b0bdbd7d07feda8ccf.png

这里是使用了同步调用,执行了execute返回一个Response对象,在这个Response中就可以得到Http请求结果的参数。

源码解析——OkHttpClient的创建

OkHttp3的请求过程比较简单,使用也很方便。那还是以创建过程和请求过程两个方面来剖析OkHttp3的原理吧。

OkHttp的Manager对象是OkHttpClient,也是使用Builder模式创建出来的。以下是OkHttpClient.Builder的属性

  1. // 异步请求调度器
  2. internal var dispatcher: Dispatcher = Dispatcher()
  3. // 请求连接池
  4. internal var connectionPool: ConnectionPool = ConnectionPool()
  5. // 各种拦截器
  6. internal val interceptors: MutableList<Interceptor> = mutableListOf()
  7. // 网络拦截器,非websocket的请求才会使用
  8. internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
  9. // 事件监听器工厂
  10. internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
  11. // 是否支持连接失败重试
  12. internal var retryOnConnectionFailure = true
  13. // 校验相关
  14. internal var authenticator: Authenticator = Authenticator.NONE
  15. internal var followRedirects = true
  16. internal var followSslRedirects = true
  17. internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
  18. internal var cache: Cache? = null
  19. internal var dns: Dns = Dns.SYSTEM
  20. // 代理相关
  21. internal var proxy: Proxy? = null
  22. internal var proxySelector: ProxySelector? = null
  23. internal var proxyAuthenticator: Authenticator = Authenticator.NONE
  24. internal var socketFactory: SocketFactory = SocketFactory.getDefault()
  25. internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
  26. internal var x509TrustManagerOrNull: X509TrustManager? = null
  27. internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
  28. internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
  29. internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
  30. internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
  31. internal var certificateChainCleaner: CertificateChainCleaner? = null
  32. internal var callTimeout = 0
  33. // 连接超时
  34. internal var connectTimeout = 10_000
  35. // 读取数据超时
  36. internal var readTimeout = 10_000
  37. // 写数据超时
  38. internal var writeTimeout = 10_000
  39. internal var pingInterval = 0
  40. internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
  41. internal var routeDatabase: RouteDatabase? = null

每个属性的意义如注释所示,这些属性用Builder构造出一个OkHttpClient之后,相当于就是一个配置了,每次执行请求时都会应用这些配置。

源码解析——Request的创建

Request的创建过程也是使用了Builder模式,主要包括url、method、headers以及body四个参数,这也是http的request的参数了。url主要是请求的地址,可以包含GET请求的参数,method表示http请求类型,一般有GET、POST、PUT、DELETE等。header就是请求头了,主要是包含一些cookie和校验之类的信息,body一般用于POST请求的请求体,例如文件上传。Request也只是一个配置类,真正请求时会读取这些配置。

源码解析——请求过程

OkHttpClient首先使用request构造出一个Call对象,Call接口的实现类是RealCall。这个Call对象就有同步和异步两种执行方式了,分别是调用execute和enqueue方法。RealCall中execute实现代码如下

  1. override fun execute(): Response {
  2. check(executed.compareAndSet(false, true)) { "Already Executed" }
  3. timeout.enter()
  4. callStart()
  5. try {
  6. client.dispatcher.executed(this)
  7. return getResponseWithInterceptorChain()
  8. } finally {
  9. client.dispatcher.finished(this)
  10. }
  11. }

首先检查executed是否为false,如果为false则置为true,否则抛异步,这里是保存每个Call对象只能执行一次。然后调用超时计时器接着调用callStart,这是为了通知事件监听,已经调用了callStart。execute执行的重点是最后三个调用,调用OkHttpClient的dispatcher的executed方法,将Call对象添加到runningSyncCalls队列中,getResponseWithInterceptorChain方法取得请求结果对象,在finally中调用OkHttpClient的dispatcher的finished方法,将call对象从call队列中移除,同时执行idleCallback的run方法。在这三个方法调用中getResponseWithInterceptorChain方法又是最重要的那一个。

看下RealCall中getResponseWithInterceptorChain的实现

  1. @Throws(IOException::class)
  2. internal fun getResponseWithInterceptorChain(): Response {
  3. // Build a full stack of interceptors.
  4. val interceptors = mutableListOf<Interceptor>()
  5. interceptors += client.interceptors
  6. interceptors += RetryAndFollowUpInterceptor(client)
  7. interceptors += BridgeInterceptor(client.cookieJar)
  8. interceptors += CacheInterceptor(client.cache)
  9. interceptors += ConnectInterceptor
  10. if (!forWebSocket) {
  11. interceptors += client.networkInterceptors
  12. }
  13. interceptors += CallServerInterceptor(forWebSocket)
  14. val chain = RealInterceptorChain(
  15. call = this,
  16. interceptors = interceptors,
  17. index = 0,
  18. exchange = null,
  19. request = originalRequest,
  20. connectTimeoutMillis = client.connectTimeoutMillis,
  21. readTimeoutMillis = client.readTimeoutMillis,
  22. writeTimeoutMillis = client.writeTimeoutMillis
  23. )
  24. var calledNoMoreExchanges = false
  25. try {
  26. val response = chain.proceed(originalRequest)
  27. if (isCanceled()) {
  28. response.closeQuietly()
  29. throw IOException("Canceled")
  30. }
  31. return response
  32. } catch (e: IOException) {
  33. calledNoMoreExchanges = true
  34. throw noMoreExchanges(e) as Throwable
  35. } finally {
  36. if (!calledNoMoreExchanges) {
  37. noMoreExchanges(null)
  38. }
  39. }
  40. }

首先创建了一个Interceptor的列表,将OkHttpClient创建时传入的interceptor添加进来,同时还添加请求重定向拦截器RetryAndFollowUpInterceptor、cookie处理拦截器BridgeInterceptor、缓存拦截器CacheInterceptor、以及网络连接拦截器ConnectInterceptor,如果这个请求不是WebSocket请求,还会把OkHttpClient中的networkInterceptors添加到拦截器列表中,最后一个是CallServerInterceptor,是为了真正执行网络请求,并解析请求结果的。拦截器列表构造好后,new出一个拦截器链RealInterceptorChain,调用RealInterceptorChain的proceed方法开始执行逐个拦截器的intercept方法。RealInterceptorChain的proceed方法中每次都会调用copy方法再创建出一个新的RealInterceptorChain对象,使用这个新的chain对象继续执行下一个Interceptor,我理解这里是为了让每个Interceptor都拥有一个新的执行环境,这个chain里就包含了拦截器的执行环境,这样每个interceptor执行都不会相互影响。

总体上来看,chain的拦截器执行是个职责链模式,每个拦截器都可以实现网络请求过程中的部分功能,最后由CallInterceptor收尾,进行最终的网络请求,每个中间interceptor在拦截处理方法中都最终调用chain的proceed方法返回结果,这就相当于递归调用,让职责链一节一节地执行。那OkHttp的请求执行过程其实就蕴含在提供的几个默认拦截器中了。

OkHttp的默认拦截器

首先是请求重定向拦截器RetryAndFollowUpInterceptor,这个拦截器还会处理请求错误重试的操作。在这个拦截器的intercept方法中有个while(true)循环,在循环体中调用chain的proceed方法,也就是会不断地执行后面的拦截器列表。proceed被catch起来,在catch中调用了RetryAndFollowUpInterceptor的recover方法,这个recover是用来判断是否还可以进行重试的。如果能执行重试,就调用followUpRequest,判断是否可重定向,floowUpRequest方法中取出了response中的code参数,如果code是HPPT_PROXY_AUTH(407),表示使用了代理,如果代理不是HTTP请求,则抛异常,否则使用代码认证并返回一个request对象。如果code是HTTP_UNAUTHORIZED(401)则表示要用户认证。如果code是HTTP_PERM_REDIRECT(308), HTTP_TEMP_REDIRECT(307), HTTP_MULT_CHOICE(300), HTTP_MOVED_PERM(301), HTTP_MOVED_TEMP(302), HTTP_SEE_OTHER(303)则表示重定向,调用buildRedirectRequest构造出一个新的重定向Request对象。如果code是HTTP_CLIENT_TIMEOUT(408)并且未命中过,则执行返回response的request。followUpRequest得到一个新的request对象后,继续while循环中的请求过程,把后面的拦截器再执行一遍。这个while循环中没有看到break关键字,所以这个while循环只有抛出异常和return两条出路。return就是返回一个response给开始的execute,抛异常则有多种,比如不支持重试并且请求异常了,或者重试次数达到最大限制数了,这里的最大重试数据为MAX_FOLLOW_UPS=20。

然后是BridgeInterceptor,这个拦截器是把OkHttp定义的字段转化成Http协议的字段同时处理cookie,这里主要是给request添加一些http请求的默认参数,当请求构造时未传入这些参数时,就会使用BridgeInterceptor的默认参数,以此来构造一个Http协议完整的请求头和请求体。在BridgeInterceptor的intercept中间位置,调用了chain.proceed继续执行后面的拦截器并得到一个Response,这个response被BridgeInterceptor进一步解析。因此这个BridgeInterceptor的真正作用就是在真正请求前给request添加一些默认参数,并将request中传入的参数转化为http协议的参数,例如将content_type转化为header中的"Content-Type",将请求后的结果进行进一步地解析处理。

第三个是缓存相关的CacheInterceptor,在其intercept方法中首先就从传入的cache对象中根据request取出一个cacheCandidate对象,而cache是个Cache对象,默认是以url作为缓存的key标识。然后使用CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()取出缓存的request和response。这里如果request为null并且response也为null,则表示本应从缓存中取,但缓存没有了,那就给一个空的response。如果request为null而response不为null,则表示从缓存中取成功,用缓存的response构造出最终的response。如果request也不为null,表表示还是要进行网络请求,执行chain.proceed,如果response的code是HTTP_NOT_MODIFIED(304),表示服务端告诉客户端,缓存还可以使用,则response还是使用缓存的response来构造,否则就使用网络请求回来的response,并将request和response存入缓存中。

ConnectInterceptor的作用就比较简单了,只是开启一个网络连接,这个网络连接可能会被已经返回的response使用,也可能会被失败了的缓存response使用,OkHttp框架的网络连接使用Exchange对象来表示。

最后就是CallServierInterceptor,最终会使用这个拦截器进行真正的网络请求。

  1. override fun intercept(chain: Interceptor.Chain): Response {
  2. val realChain = chain as RealInterceptorChain
  3. val exchange = realChain.exchange!!
  4. val request = realChain.request
  5. val requestBody = request.body
  6. val sentRequestMillis = System.currentTimeMillis()
  7. exchange.writeRequestHeaders(request)
  8. var invokeStartEvent = true
  9. var responseBuilder: Response.Builder? = null
  10. if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
  11. // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
  12. // Continue" response before transmitting the request body. If we don't get that, return
  13. // what we did get (such as a 4xx response) without ever transmitting the request body.
  14. if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
  15. exchange.flushRequest()
  16. responseBuilder = exchange.readResponseHeaders(expectContinue = true)
  17. exchange.responseHeadersStart()
  18. invokeStartEvent = false
  19. }
  20. if (responseBuilder == null) {
  21. if (requestBody.isDuplex()) {
  22. // Prepare a duplex body so that the application can send a request body later.
  23. exchange.flushRequest()
  24. val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
  25. requestBody.writeTo(bufferedRequestBody)
  26. } else {
  27. // Write the request body if the "Expect: 100-continue" expectation was met.
  28. val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
  29. requestBody.writeTo(bufferedRequestBody)
  30. bufferedRequestBody.close()
  31. }
  32. } else {
  33. exchange.noRequestBody()
  34. if (!exchange.connection.isMultiplexed) {
  35. // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
  36. // from being reused. Otherwise we're still obligated to transmit the request body to
  37. // leave the connection in a consistent state.
  38. exchange.noNewExchangesOnConnection()
  39. }
  40. }
  41. } else {
  42. exchange.noRequestBody()
  43. }
  44. if (requestBody == null || !requestBody.isDuplex()) {
  45. exchange.finishRequest()
  46. }
  47. if (responseBuilder == null) {
  48. responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
  49. if (invokeStartEvent) {
  50. exchange.responseHeadersStart()
  51. invokeStartEvent = false
  52. }
  53. }
  54. var response = responseBuilder
  55. .request(request)
  56. .handshake(exchange.connection.handshake())
  57. .sentRequestAtMillis(sentRequestMillis)
  58. .receivedResponseAtMillis(System.currentTimeMillis())
  59. .build()
  60. var code = response.code
  61. if (code == 100) {
  62. // Server sent a 100-continue even though we did not request one. Try again to read the actual
  63. // response status.
  64. responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
  65. if (invokeStartEvent) {
  66. exchange.responseHeadersStart()
  67. }
  68. response = responseBuilder
  69. .request(request)
  70. .handshake(exchange.connection.handshake())
  71. .sentRequestAtMillis(sentRequestMillis)
  72. .receivedResponseAtMillis(System.currentTimeMillis())
  73. .build()
  74. code = response.code
  75. }
  76. exchange.responseHeadersEnd(response)
  77. response = if (forWebSocket && code == 101) {
  78. // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
  79. response.newBuilder()
  80. .body(EMPTY_RESPONSE)
  81. .build()
  82. } else {
  83. response.newBuilder()
  84. .body(exchange.openResponseBody(response))
  85. .build()
  86. }
  87. if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
  88. "close".equals(response.header("Connection"), ignoreCase = true)) {
  89. exchange.noNewExchangesOnConnection()
  90. }
  91. if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
  92. throw ProtocolException(
  93. "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
  94. }
  95. return response
  96. }

首先调用了exchange.writeRequestHeaders将请求头写入到连接,如果请求不是GET或者PUT的,就要先处理请求体,使用exchange创建Sink对象并将requestBody写入到Sink中去。

这里需要再回到ConnectInterceptor中去看如果创建的一个连接,一个连接的底层是用什么实现的。前面说到过,interceptor chain中的exchange就是一个连接的抽象,在ConnectInterceptor中就对exchange进行了初始化。调用了RealCall的initExchange方法,在initExchange方法中使用了exchangeFinder,这个finder中拥有一个连接池和一个地址,地址由请求的url构建,而连接池是OkHttpClient的连接池,也就是说同一个OkHttpClient发出的请求,共用一个连接池,当同一个OkHttpClient对一个地址发送多个请求时,可能就可以复用连接。使用exchangeFinder调用其find方法是为创建一个ExchangeCodec对象,在find方法中调用findHealthyConnection方法先获取一个连接对象,根据方法名就可以看出,这个方法会去连接池找可以复用的连接。

findHealthConnection方法中在一个while循环中调用findConnection方法获取一个连接,如果判断连接isHealth,则return。继续跟到findConnection方法中去,首先判断这个call对象中是否已有connection,如果有则判断其可用的话,就复用这个connection。否则再调用RealConnectionPool的callAcquirePooledConnection方法从连接池中获取,连接池中有个RealConnection的ConcurrentLinkedQueue用来保存连接,循环判断连接池中的连接可以被传入的地址使用的话,就将这个连接设置给call对象并将这个连接对象返回。如果从连接池中取不到连接,则新建一个RealConnection对象,并调用其connect方法新建一个连接。connect方法中调用了connectSocket方法创建了Socket对象,并将Socket保存在RealConnection的rawSocket属性中,创建Socket后调用其connect方法与服务端建立一个网络连接。

再回到CallServerInterceptor中,当请求体写完后,调用exchanged的flushRequest方法,flushRequest方法中调用了BufferSink的flush方法,这个方法里最终调用了Socket的OutputStream的write,将数据写到服务端连接中去。

写完请求头数据了就需要开始读服务端返回的http结果了,先调用exchange.readResponseHeaders读取响应头,读取响应头时,把response的code、message、protocol也同时给解析了。如果判断响应体为null,则将请求体数据写到服务端。将读取到的responseBuilder构造成一个response对象,判断如果不是websocket并且code不是101,则还需要请求响应体数据,这样一个请求Response才算完全构造成功,最后将这个response返回给拦截链。

总结

OkHttp框架基于职责链模式,将一个请求的过程分拆为多个小过程,每个小过程由一个Interceptor来完成,并且使用者可以在所有内置interceptor之前插入自定义的interceptor对请求过程加以扩展。网络连接部分OkHttp框架使用Exchange对象予以抽象,Interceptor只实现请求逻辑,具体的数据读写由Exchange来完成,因此理论来说底层的数据请求我们还可以替换。整个框架对于逻辑处理与数据处理耦合较低,扩展性较强。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/210732
推荐阅读
相关标签
  

闽ICP备14008679号