OkHttp日常使用实战篇

由于示例中使用的代码段都比较简单,详细的代码段以及响应日志都已经贴出,细节不再赘述。

一、初始化
  • 1、初始化OkHttpClient
val mClient = getOkClient()

private fun getOkClient(): OkHttpClient {
        return OkHttpClient.Builder().addInterceptor(LogInterceptor()).build()

}

  • 2、初始化Request
  private var mUrl2 = "https://www.baidu.com/"
  val mRequest = Request.Builder().url(mUrl2).build()
二、同步Get请求

由于网络请求不能在主线程进行,此处使用协程进行请求,防止主线程发生阻塞

  • 1、代码段
  mRequest?.let {
            GlobalScope.launch {
                mClient.newCall(it).execute().use { response ->
                        if (response.isSuccessful) {
                            for ((name, value) in response.headers) {
                                LogUtil.D(log = "name ===$name   value===$value")
                            }
                            LogUtil.D(log = "response body== ${response.body.toString()}")
                        }
                }

            }
        }
  • 2、response日志
2021-07-14 19:37:33.455 31188-31247/com.example.myapplication D/tag: name ===Cache-Control   value===private, no-cache, no-store, proxy-revalidate, no-transform
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Connection   value===keep-alive
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Content-Type   value===text/html
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Date   value===Wed, 14 Jul 2021 11:37:33 GMT
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Last-Modified   value===Mon, 23 Jan 2017 13:23:46 GMT
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Pragma   value===no-cache
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Server   value===bfe/1.0.8.18
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Set-Cookie   value===BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: name ===Transfer-Encoding   value===chunked
2021-07-14 19:37:33.456 31188-31247/com.example.myapplication D/tag: response body== okhttp3.internal.http.RealResponseBody@72ab647

以上可见请求成功并打印了响应头

三、异步Get请求

通常实际项目中,使用异步的方式进行请求,

  • 1、代码段
   mRequest?.let {
            mClient.newCall(it).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    Log.d(TAG, "fail e===${e.message}   cause=== ${e.cause}")
                }

                override fun onResponse(call: Call, response: Response) {
                    Log.d(TAG, " get success  response=== ${response.body.toString()}")
                    updateText(response)
                    response.body?.close()
                }

            })
        }
  • 2、response日志
2021-07-14 19:43:40.671 31188-31469/com.example.myapplication D/tag:  get success  response=== okhttp3.internal.http.RealResponseBody@2b2273f
三、访问头文件
  • 1、设置header

OkHttp设置请求头时,使用header(name, value)设定的唯一键name值value
使用addHeader(name, value)添加一个头时,如果header中存在现有值,新值将覆盖旧值。

  • 2、读取header

读取响应头时,返回最后一次出现的header(name)命名值。通常键唯一,值唯一,并且一一对应。
如果不存在值,header(name)将返回 null。
如果想读取所有的header,可以使用headers(name).

  • 3、代码段
  val mRequest=Request.Builder()
            .url("https://api.github.com/repos/square/okhttp/issues")
            .header("User-Agent", "OkHttp Headers.java")
            .addHeader("Accept", "application/json; q=0.5")
            .addHeader("Accept", "application/vnd.github.v3+json")
            .build()

        GlobalScope.launch {
            mClient.newCall(mRequest).execute().use { response->
                if (!response.isSuccessful)LogUtil.D(log = "response error ==== ${response.code}")

                LogUtil.D(log="Server: ${response.header("Server")}")
                LogUtil.D(log="Date: ${response.header("Date")}")
                LogUtil.D(log="Vary: ${response.headers("Vary")}")
                // 查看header中哈哈哈为键的值
                LogUtil.D(log="哈哈哈: ${response.header("哈哈哈")}")
            }
        }
  • 4、response日志
2021-07-14 20:14:03.909 32142-32180/com.example.myapplication D/tag: Server: GitHub.com
2021-07-14 20:14:03.910 32142-32180/com.example.myapplication D/tag: Date: Wed, 14 Jul 2021 12:14:02 GMT
2021-07-14 20:14:03.910 32142-32180/com.example.myapplication D/tag: Vary: [Accept, Accept-Encoding, Accept, X-Requested-With]
2021-07-14 20:14:03.910 32142-32180/com.example.myapplication D/tag: 哈哈哈: null

通过日志可见,通过header的键获取到了相应的值,由于没有查询到哈哈哈对应的值,返回了null。

四、使用post请求发送string
  • 1、注意点

使用post请求发送请求体到服务端,一般限制不大于1M

  • 2、代码段
    val postBody = """
        |Releases
        |--------
        |
        | * _1.0_ May 6, 2013
        | * _1.1_ June 15, 2013
        | * _1.2_ August 11, 2013
        |""".trimMargin()

        val request = Request.Builder()
            .url("https://api.github.com/markdown/raw")
            .post(postBody.toRequestBody(MEDIA_TYPE_MARKDOWN))
            .build()

        GlobalScope.launch {
            mClient.newCall(request).execute().use { response ->
                if (!response.isSuccessful) throw IOException("Unexpected code $response")

                LogUtil.D(log = response.body!!.string())
            }
        }
  • 3、response响应
2021-07-14 20:31:25.498 32462-32509/com.example.myapplication D/tag: <h2>
    <a id="user-content-releases" class="anchor" href="#releases" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Releases</h2>
    <ul>
    <li>
    <em>1.0</em> May 6, 2013</li>
    <li>
    <em>1.1</em> June 15, 2013</li>
    <li>
    <em>1.2</em> August 11, 2013</li>
    </ul>
五、使用post发送流
  • 1、简介

示例中将请求体通过Okio的buffer sink直接转换成流进行传递,也可以使用OutputStream进行转换传递。

  • 2、代码段
    val requestBody = object : RequestBody() {
                override fun contentType() = MEDIA_TYPE_MARKDOWN
                
                /** Writes the content of this request to [sink]. */
                override fun writeTo(sink: BufferedSink) {
                    sink.writeUtf8("Numbers\n")
                    sink.writeUtf8("-------\n")
                    for (i in 2..10) {
                        sink.writeUtf8(String.format(" * $i = ${factor(i)}\n"))
                    }
                }

                // 因式分解
                private fun factor(n: Int): String {
                    for (i in 2 until n) {
                        val x = n / i
                        if (x * i == n) return "${factor(x)} × $i"
                    }
                    return n.toString()
                }
            }

            val request = Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(requestBody)
                .build()

           GlobalScope.launch {
               mClient.newCall(request).execute().use { response ->
                   if (!response.isSuccessful) throw IOException("Unexpected code $response")

                   LogUtil.D(log=response.body!!.string())
           }

        }
  • 3、response日志
2021-07-14 20:53:51.098 1496-1551/com.example.myapplication D/tag: <h2>
    <a id="user-content-numbers" class="anchor" href="#numbers" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Numbers</h2>
    <ul>
    <li>2 = 2</li>
    <li>3 = 3</li>
    <li>4 = 2 × 2</li>
    <li>5 = 5</li>
    <li>6 = 3 × 2</li>
    <li>7 = 7</li>
    <li>8 = 2 × 2 × 2</li>
    <li>9 = 3 × 3</li>
    <li>10 = 5 × 2</li>
    </ul>
六、post上传文件
  • 1、示例代码
     val fileDir = File(Environment.getExternalStorageDirectory(),"test")
        if (!fileDir.exists()) {
            fileDir.mkdir()
        }
        val fileName="testConnectionFile.txt"
        val file=File(fileDir,fileName)
        val request = Request.Builder()
            .url("https://api.github.com/markdown/raw")
            .post(file.asRequestBody(MEDIA_TYPE_MARKDOWN))
            .build()

            request.let {
                mClient.newCall(it).enqueue(object : Callback {
                    override fun onFailure(call: Call, e: IOException) {
                        LogUtil.D(log = "response error=== ${e.cause} message=== ${e.message}")
                    }

                    override fun onResponse(call: Call, response: Response) {
                        LogUtil.D(log = response.body!!.string())
                    }
                })
        }
  • 2、response日志
// interceptor
2021-07-15 21:01:05.378 12489-12554/com.example.myapplication D/tag: Sending request url== https://api.github.com/markdown/raw  connection== Connection{api.github.com:443, proxy=DIRECT hostAddress=api.github.com/192.30.255.116:443 cipherSuite=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 protocol=h2}  headers==Content-Type: text/x-markdown; charset=utf-8
    Content-Length: 78
    Host: api.github.com
    Connection: Keep-Alive
    Accept-Encoding: gzip
    User-Agent: okhttp/4.9.0
 
 // response
2021-07-15 21:01:05.719 12489-12554/com.example.myapplication D/tag: Received response url== https://api.github.com/markdown/raw  costTime== 342ms header== server: GitHub.com
    date: Thu, 15 Jul 2021 13:01:05 GMT
    content-type: text/html;charset=utf-8
    x-commonmarker-version: 0.21.0
    x-ratelimit-limit: 60
    x-ratelimit-remaining: 58
    x-ratelimit-reset: 1626357649
    x-ratelimit-used: 2
    x-ratelimit-resource: core
    access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
    access-control-allow-origin: *
    strict-transport-security: max-age=31536000; includeSubdomains; preload
    x-frame-options: deny
    x-content-type-options: nosniff
    x-xss-protection: 0
    referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin
    content-security-policy: default-src 'none'
    vary: Accept-Encoding, Accept, X-Requested-With
    content-encoding: gzip
    x-github-request-id: 1964:7252:B6CA20:C2ED42:60F03180
    
2021-07-15 21:01:05.722 12489-12554/com.example.myapplication D/tag: <p>url===== <a href="http://mf.m.bch.leju.com/v70/show/index_feed.json" rel="nofollow">http://mf.m.bch.leju.com/v70/show/index_feed.json</a>   Time===  3438ms</p>
七、post上传form表单
  • 1、示例代码

向百度发送form表单

    val formData=FormBody.Builder()
            .add("search","Jurassic Park")
            .build()

        val request=Request.Builder()
            .url("https://www.baidu.com")
            .post(formData)
            .build()

            mClient.newCall(request).enqueue(object :Callback{
                override fun onFailure(call: Call, e: IOException) {
                    LogUtil.D(log="error  message ==== ${e.message}")
                }

                override fun onResponse(call: Call, response: Response) {
                    LogUtil.D(log="success response== ${response.body.toString()}")
                }

            })
  • 2、日志

通过日志查看请求成功了

// interceptor
2021-07-16 13:58:45.294 6510-6550/com.example.myapplication D/tag: Sending request url== https://www.baidu.com/  connection== Connection{www.baidu.com:443, proxy=DIRECT hostAddress=www.baidu.com/110.242.68.3:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=http/1.1}  headers==Content-Type: application/x-www-form-urlencoded
    Content-Length: 22
    Host: www.baidu.com
    Connection: Keep-Alive
    Accept-Encoding: gzip
    User-Agent: okhttp/4.9.0
2021-07-16 13:58:45.323 6510-6550/com.example.myapplication D/tag: Received response url== https://www.baidu.com/  costTime== 30ms header== Connection: keep-alive
    Content-Length: 17931
    Content-Type: text/html
    Date: Fri, 16 Jul 2021 05:58:45 GMT
    Etag: "54d97483-460b"
    Server: bfe/1.0.8.18
    
    // response
2021-07-16 13:58:45.324 6510-6550/com.example.myapplication D/tag: success response== okhttp3.internal.http.RealResponseBody@c9f4522
八、post发送multipart request
  • 1、简介

MultipartBody.Builder可以构建与 HTML 文件上传表单兼容的复杂请求体。Multipart请求体的每个部分本身就是一个请求体,并且可以分别定义heder.

  • 2、代码示例
  // 将手机上文件通过formData 传递
        val requestBody=MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("title","registry_list")
            .addFormDataPart("text","registry_list.txt",
                getFile("360/authshare","registry_list.txt").asRequestBody(MEDIA_TYPE_MARKDOWN))
            .build()

        val request = Request.Builder()
            .header("Authorization", "Client-ID $IMGUR_CLIENT_ID")
            .url("https://www.baidu.com")
            .post(requestBody)
            .build()

        mClient.newCall(request).enqueue(object :Callback{
            override fun onFailure(call: Call, e: IOException) {
                LogUtil.D(log="error  message ==== ${e.message}")
            }

            override fun onResponse(call: Call, response: Response) {
                LogUtil.D(log="success response   code==== ${response.code}    body===== ${response.body.toString()}")
            }

        })
  • 3、日志打印
// interceptor
2021-07-16 14:53:07.358 7932-7991/com.example.myapplication D/tag: Sending request url== https://www.baidu.com/  connection== Connection{www.baidu.com:443, proxy=DIRECT hostAddress=www.baidu.com/110.242.68.4:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=http/1.1}  headers==Authorization: Client-ID 9199fdef135c122
    Content-Type: multipart/form-data; boundary=83bbd2e3-0504-4096-a833-e162d2ad91e2
    Content-Length: 368
    Host: www.baidu.com
    Connection: Keep-Alive
    Accept-Encoding: gzip
    User-Agent: okhttp/4.9.0
2021-07-16 14:53:07.429 7932-7991/com.example.myapplication D/tag: Received response url== https://www.baidu.com/  costTime== 71ms header== Connection: keep-alive
    Content-Length: 17931
    Content-Type: text/html
    Date: Fri, 16 Jul 2021 06:53:07 GMT
    Etag: "54d97483-460b"
    Server: bfe/1.0.8.18
    
 // response
2021-07-16 14:53:07.430 7932-7991/com.example.myapplication D/tag: success response   code==== 302    body===== okhttp3.internal.http.RealResponseBody@b30f288
九、响应缓存
  • 1、简介

要缓存响应,需要设置一个可以读取和写入的缓存目录,并对缓存大小的进行限制。缓存目录应该是私有的,不受信任的应用应该无法读取其内容

为了方便控制缓存增删,多个缓存同时访问的缓存目录应该独立

大多数应用程序使用同一个OkHttpClient,并配置缓存,应该在任何地方使用相同的实例。否则,这两个缓存实例将相互覆盖,破坏响应缓存,并可能使程序崩溃

可以通过配置缓存Header对缓存进行配置,OkHttp会使用配置的缓存header对缓存进行控制

  • 2、代码实例

代码中将缓存保存在文件夹customCache中

     val directory = File(Environment.getExternalStorageDirectory(),"customCache")
        val cacheClient = OkHttpClient().newBuilder().addNetworkInterceptor(CacheAgeInterceptor())
            .cache(Cache(directory = directory, maxSize = 2 * 1024 * 1024L))
            .build()

        // CacheControl.FORCE_CACHE 强制使用缓存
        // CacheControl.FORCE_NETWORK 强制使用网络

        val cc=CacheControl.Builder()
            // .noCache()  // 不使用缓存,但是会存储缓存数据
            // .noStore() // 不使用缓存,同时不存储缓存
            // .onlyIfCached() // 本地缓存时会使用缓存
            .minFresh(100,TimeUnit.SECONDS) // 10s刷新缓存
            .maxAge(1,TimeUnit.HOURS) // 1h最大有效时间
            .maxStale(50,TimeUnit.SECONDS) // 可以接受超时5s的响应
            .build()

        val request=Request.Builder().url(mUrl).cacheControl(cc).build()

        GlobalScope.launch {
            // 响应1
            val responseOne=cacheClient.newCall(request).execute().use { response ->
                if (response.isSuccessful){
                    LogUtil.D(log="request success   cacheResponse=== ${response.cacheResponse?.body?.contentType()}  " +
                            "networkResponse=== ${response.networkResponse}")
                }else{
                    LogUtil.D(log="requestOne error   errorCode ===${response.code}")
                }
            }

            // 响应2
            val responseTwo=cacheClient.newCall(request).execute().use { response ->
                if (response.isSuccessful){
                    LogUtil.D(log="request successTwo   cacheResponse=== ${response.cacheResponse?.body.toString()} " +
                            " networkResponse=== ${response.networkResponse}")
                }else{
                    LogUtil.D(log="requestTwo  error   errorCode ===${response.code}")
                }
            }

            LogUtil.D(log=" responseOne===responseTwo====${responseOne==responseTwo}")

        }
  • 3、日志查看
2021-07-16 16:40:45.072 13883-13933/com.example.myapplication D/tag: request success   cacheResponse=== null  networkResponse=== Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
2021-07-16 16:40:45.106 13883-13933/com.example.myapplication D/tag: request successTwo   cacheResponse=== null  networkResponse=== null
2021-07-16 16:40:45.106 13883-13933/com.example.myapplication D/tag:  responseOne===responseTwo====true

通过日志查看,两次请求都成功了,但是第二次networkResponse为空,证明OkHttp帮我们使用了缓存

  • 4、缓存文件夹查看及缓存内容查看
  1. 缓存文件夹
    在这里插入图片描述

  2. 缓存数据

https://publicobject.com/helloworld.txt
GET
0
HTTP/1.1 200 OK
11
Server: nginx/1.10.3 (Ubuntu)
Date: Fri, 16 Jul 2021 08:40:44 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes
Cache-Control: max-age=60
OkHttp-Sent-Millis: 1626424844597
OkHttp-Received-Millis: 1626424844986
TLSv1.2
  • 5、注意点
  1. 要防止响应使用缓存,可以使用CacheControl.FORCE_NETWORK. 要防止它使用网络,可以使用CacheControl.FORCE_CACHE.

  2. 如果您使用FORCE_CACHE并且响应需要网络,OkHttp 将返回一个504 Unsatisfiable Request响应。

十、取消请求
  • 1、简介
  1. 可以使用Call.cancel()方法来取消请求操作
  2. 该操作支持同步和异步请求的取消工作
  3. 如果当前取消的请求的线程正在执行写请求和读响应过程中,会报IOException
  • 2、代码实例

例子中使用Executors延迟5s取消请求

  // init
        val executor = Executors.newScheduledThreadPool(1)
        val request = Request.Builder()
            .url("https://www.baidu.com")
            .build()
        val startTime = System.currentTimeMillis()
        val mCall = mClient.newCall(request)

        // 延迟5s
        executor.schedule({
            LogUtil.D(log = "startCallTime=== ${System.currentTimeMillis() - startTime}")
            mCall.cancel()
        }, 5, TimeUnit.SECONDS)

        // 请求
        GlobalScope.launch {
            mCall.execute().use { response ->
                if (response.isSuccessful) {
                    LogUtil.D(log = "call success response=== ${response.body.toString()}")
                } else {
                    LogUtil.D(log = "call fail code is === ${response.code}")
                }
            }
        }
  • 3、日志
// interceptor
2021-07-19 16:16:15.950 31114-31162/com.example.myapplication D/tag: Sending request url== https://www.baidu.com/  connection== Connection{www.baidu.com:443, proxy=DIRECT hostAddress=www.baidu.com/110.242.68.3:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=http/1.1}  headers==Host: www.baidu.com
    Connection: Keep-Alive
    Accept-Encoding: gzip
    User-Agent: okhttp/4.9.0
2021-07-19 16:16:15.977 31114-31162/com.example.myapplication D/tag: Received response url== https://www.baidu.com/  costTime== 27ms header== Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
    Connection: keep-alive
    Content-Encoding: gzip
    Content-Type: text/html
    Date: Mon, 19 Jul 2021 08:16:15 GMT
    Last-Modified: Mon, 23 Jan 2017 13:23:46 GMT
    Pragma: no-cache
    Server: bfe/1.0.8.18
    Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
    Transfer-Encoding: chunked
    
   // response
2021-07-19 16:16:15.979 31114-31162/com.example.myapplication D/tag: call success response=== okhttp3.internal.http.RealResponseBody@f6e4d64
2021-07-19 16:16:20.767 31114-31161/com.example.myapplication D/tag: startCallTime=== 5008
  • 通过日志查看延迟了5008ms,时间接近5s,但是不是很精确
十一、超时时间
  • 1、简介

当请求不可达时,可以使用超时策略来调用失败进行错误处理,OkHttp提供了设置连接、读、写和请求全过程超时时常策略。

  • 2、代码实例

具体的点在代码中都写了注释

 private var mUrl3 = "https://www.nytimes.com/"
 val client: OkHttpClient = OkHttpClient.Builder()
            .addNetworkInterceptor(LogInterceptor())
            .connectTimeout(5, TimeUnit.SECONDS) // 连接超时时间
            .writeTimeout(5, TimeUnit.SECONDS)      // 写超时时间
            .readTimeout(5, TimeUnit.SECONDS)       // 读超时时间
            .callTimeout(10, TimeUnit.SECONDS)      // 全过程超时时间
            .build()

        val request = Request.Builder()
            .url(mUrl3)
            .build()

        GlobalScope.launch {
            client.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    LogUtil.D(log = "Response error: ${e.message}")
                }

                override fun onResponse(call: Call, response: Response) {
                    LogUtil.D(log = "Response completed: ${response.body.toString()}")
                }

            })
        }
  • 3、日志
2021-07-19 16:54:56.181 4170-4240/com.example.myapplication D/tag: Response error: failed to connect to www.nytimes.com/103.252.115.53 (port 443) from /10.208.97.1 (port 47503) after 5000ms

  • 通过日志查看,连接超时
十二、为不同请求添加不同配置
  • 1、简介

OkHttpClient 提供了配置超时、缓存等方法,当需要不同的请求添加不同配置时,可以使用OKHttpClient.newBuilder()来进行配置。这时两个request请求会共享同一个Client的连接池、嗲调度器和配置。

  • 2、代码实例

我们基于mClient创建了两个request,requestOne设置了超时时间为500ms,requestTwo设置的超时时间为3000ms

   val request = Request.Builder()
            .url("https://httpbin.org/delay/1") 
            .build()

        // set timeout 500
        val clientOne = mClient.newBuilder()
            .readTimeout(500, TimeUnit.MILLISECONDS)
            .build()
            
        GlobalScope.launch {
            clientOne.newCall(request).enqueue(object :Callback{
                override fun onFailure(call: Call, e: IOException) {
                    LogUtil.D(log = "Response error: ${e.message}")
                }

                override fun onResponse(call: Call, response: Response) {
                    LogUtil.D(log = "Response completed: ${response.body.toString()}")
                }

            })
        }

        // set timeout 3000
        val clientTwo = mClient.newBuilder()
            .readTimeout(3000, TimeUnit.MILLISECONDS)
            .build()

        GlobalScope.launch {
            clientTwo.newCall(request).enqueue(object :Callback{
                override fun onFailure(call: Call, e: IOException) {
                    LogUtil.D(log = "Response error: ${e.message}")
                }

                override fun onResponse(call: Call, response: Response) {
                    LogUtil.D(log = "Response completed: ${response.body.toString()}")

                }

            })
        }
  • 3、日志
// interceptor
2021-07-19 18:46:35.927 12767-12814/com.example.myapplication D/tag: Sending request url== https://httpbin.org/delay/1  connection== Connection{httpbin.org:443, proxy=DIRECT hostAddress=httpbin.org/18.235.124.214:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=h2}  headers==Host: httpbin.org
    Connection: Keep-Alive
    Accept-Encoding: gzip
    User-Agent: okhttp/4.9.0
2021-07-19 18:46:35.927 12767-12813/com.example.myapplication D/tag: Sending request url== https://httpbin.org/delay/1  connection== Connection{httpbin.org:443, proxy=DIRECT hostAddress=httpbin.org/18.235.124.214:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=h2}  headers==Host: httpbin.org
    Connection: Keep-Alive
    Accept-Encoding: gzip
    User-Agent: okhttp/4.9.0
    
    // response 
    // 超时
2021-07-19 18:46:36.439 12767-12814/com.example.myapplication D/tag: Response error: timeout

2021-07-19 18:46:37.752 12767-12813/com.example.myapplication D/tag: Received response url== https://httpbin.org/delay/1  costTime== 1824ms header== date: Mon, 19 Jul 2021 10:46:37 GMT
    content-type: application/json
    content-length: 315
    server: gunicorn/19.9.0
    access-control-allow-origin: *
    access-control-allow-credentials: true
    
    // response success
2021-07-19 18:46:37.753 12767-12813/com.example.myapplication D/tag: Response completed: okhttp3.internal.http.RealResponseBody@d22b271

通过日志查看,请求全过程耗时1824ms,requestOne超时,requestTwo获取到了响应。

十三、认证处理
  • 1、简介
  1. 通常情况下,当响应码返回401时,OkHttp会自动向认证中心对未认证的请求进行认证重试。

  2. 配置认证代理的方法实现中要提供身份认证凭据,如果提供的身份认证凭据不可用时,返回null并跳过重试。

  3. 可以使用Response.challenges获取schemes和认证信息,当完成基础的challenge后,使用Credentials.basic(username, password)来对请求头进行编码。

  • 2、代码实例

代码中,通过authenticator设置了独立的认证凭据,当response返回code为401时将使用我们提供的认证凭据进行认证。

 val client = OkHttpClient.Builder()
            .authenticator(object : Authenticator {
                @Throws(IOException::class)
                override fun authenticate(route: Route?, response: Response): Request? {
                    if (response.request.header("Authorization") != null) {
                        return null
                    }

                    LogUtil.D(log = "Authenticating for response: $response")
                    LogUtil.D(log = "Challenges: ${response.challenges()}")
                     // 设置认证凭据
                    val credential = Credentials.basic("jesse", "password1")
                    return response.request.newBuilder()
                        .header("Authorization", credential)
                        .build()
                }
            })
            .build()

        val request = Request.Builder()
            .url("https://publicobject.com/secrets/hellosecret.txt")
            .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                LogUtil.D(log = "Response error: ${e.message}")
            }

            override fun onResponse(call: Call, response: Response) {
                LogUtil.D(log = "Response completed: ${response.body.toString()}")
            }

        })
  • 3、日志
// interceptor
2021-07-19 19:26:04.941 13362-13404/com.example.myapplication D/tag: Authenticating for response: Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://publicobject.com/secrets/hellosecret.txt}

//  challenge
2021-07-19 19:26:04.943 13362-13404/com.example.myapplication D/tag: Challenges: [Basic authParams={realm=OkHttp Secrets}]

// response
2021-07-19 19:26:05.130 13362-13404/com.example.myapplication D/tag: Response completed: okhttp3.internal.http.RealResponseBody@52fe3e2

通过日志可见返回了状态码401,使用本地配置的认证凭据进行认证后,获取到了响应。

  • 参考:https://square.github.io/okhttp/recipes/#canceling-a-call-kt-java
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值