转载

OkHttp使用篇-01-介绍及基本使用

OkHttpsquare 开源的 Java(Kotlin) 编写的网络库,是 JavaAndroid 开发人员使用最广泛的网络库之一。

OkHttp 有以下 优点 :

GZIP

OkHttp 流行得益于它的良好的架构设计,强大的 拦截器(intercepts) 使得操纵网络十分方便;也得益于强大的生态,大量的流行库都以 OkHttp 作为底层网络框架或提供支持,譬如 RetrofitGlideFrescoMoshiPicasso 等。

版本选择

3.12.x vs 3.13+

关键变更

3.12.x 版本支持 Android 2.3+(API level 9+) 以及 Java 7+

低版本的平台缺乏对 TLSv1.2 的支持。 TLSv1TLSv1.1 及更低版本有已知的安全隐患,不建议使用。主流的Web浏览器都宣布将于2020年早期放弃对低版本的 TLS 的支持,只提供对 TLSv1.2 及更高版本的支持。

因此 OkHttp 官方宣布对 3.12.x 版本只提供关键bug修复,而且这种支持将截止到 2020年12月31日 为止。

3.13+ 支持 Android 5.0+(API level 21+) 以及 Java 8+TLSv1TLSv1.1 默认不再启用。

因此建议升级到 3.13+ 及更高版本(当前2019.11.20最新版本为 3.14.4 ),毕竟更低版本的平台的使用量已经非常少了,而且未来也缺乏支持。

升级

Android 中将 OkHttp3.12.x 升级到 3.13+ 版本,首先确认 minSdkVersion 至少为 21Gradle Plugin 版本至少为 3.2

增加依赖:

dependencies {  
  implementation "com.squareup.okhttp3:okhttp:3.14.4"  
  ...  
}
复制代码

设置 Java 版本为 1.8 以上:

compileJava {
  sourceCompatibility = JavaVersion.VERSION_1_8
  targetCompatibility = JavaVersion.VERSION_1_8
}

android {
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
  ...  
}
复制代码

兼容 TLSv1TLSv1.1

如果希望能享受最新版本的优化及改进,而服务端还没有提供 TLSv1.2 及更高版本的支持,可以使用如下方法以开启兼容:

val client = OkHttpClient.Builder()
    .connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS))
    .build();
复制代码

3.x vs 4.x

目前市面上用的最多的是 3.x 系列,相关的文章、博客等大多以这个系列为基础来介绍 OkHttp 的。

github 上面的 release 记录 可以追踪到, 3.x 系列的第一个正式版本 3.0.0 从2016年1月正式发布,到目前为止(2019.11.20)最新版本为 3.14.4

4.x 系列的第一个正式版本 4.0.0 从2019年6月正式发布,到目前为止(2019.11.20)最新版本为 4.2.2 ,算是比较新了。

关键变更

4.x 系列相对于 3.x 系列,最大的变更是将实现语言从 Java 变更到了 KotlinKotlin 相对于 Java 来说有很多优点,具体这里就不展开了。

OkHttp 团队在实现 4.x 版本的时候,从以下三方面去努力保持和 3.x 版本的兼容性:

  1. 二进制兼容性 :使用 OkHttp 3.x 编译的程序使用 OkHttp 4.x 来运行的能力
  2. Java 源码兼容性 :使用 OkHttp 3.x 编写的 Java 代码升级到 OkHttp 4.x 而不用修改 Java 代码的能力
  3. Kotlin 源码兼容性 :使用 OkHttp 3.x 编译的 Kotlin 程序使用 OkHttp 4.x 而不用修改 Kotlin 代码的能力

OkHttp 对前两种兼容性,除了细微的变更,都实现了兼容;而对第三种却没有实现兼容,但是通过 Kotlin 语言强大的 deprecation 特性能很容易的实现升级。

不兼容的变更

  1. OkHttpClient 中的 final 方法
    OkHttpClient 中的26个访问器方法从 3.x 中的非 final 变更为 final 。如果测试框架需要mock,可以使用 Call.Factory
  2. 内部 API 变更
    okhttp3.internal 包下的内容用于内部实现,会频繁发生变更,并不建议直接使用。官方也会保持一定的向后兼容性,但后续仍然有变更的可能。
  3. Credentials.basic()
    3.xbasic 方法的参数 usernamepassword 如果为 null ,则会转成字符串 "null" ,而 4.x 中这两个参数为非空字符串。
  4. HttpUrl.queryParameterValues()
    HttpUrl.queryParameterValues() 方法的返回类型为 List<String?>

新增的 @Deprecated 方法

4.x 版本中为了向后兼容便于迁移,很多方法仍然保留了下来,但是增加了 @Deprecated 标记,这是因为 Kotlin 语言提供了更加便利的方法。这些方法未来会被移除。

譬如 3.xOkHttpClient 中有方法:

public Authenticator authenticator() {
    return authenticator;
}
复制代码

4.x 中有同样的方法:

@JvmName("-deprecated_authenticator")
@Deprecated(
  message = "moved to val",
  replaceWith = ReplaceWith(expression = "authenticator"),
  level = DeprecationLevel.ERROR)
fun authenticator(): Authenticator = authenticator
复制代码

方法标记为 @Deprecated ,建议直接使用 val 变量。这些方法可以使用 Intellij IDEAAndroid Studio 提供的 Code Cleanup 工具自动替换为建议的方法,工具路径为菜单项: Analyze -> Code Cleanup

扩展函数

4.x 中将很多静态函数变更为扩展函数,充分利用了 Kotlin 语言的特性。譬如 Handshake.get(SSLSession) 变更为 SSLSession.handshake() 等,可以查看源代码或者看 官方文档 。

SAM 转换

接口中如果只定义了一个抽象方法,这个方法就叫做 单抽象方法(Single Abstract Method, SAM)

譬如, Runnable 接口的 run 方法就是最常见的 SAM

package java.lang;
public interface Runnable {

    public abstract void run();
}
复制代码

Kotlin 中调用 Java 接口中定义的 SAM 时,可以像使用 lambda 方法一样。但是同样的使用方式却不适用于 Kotlin 中定义的 SAM

Kotlin 调用 OkHttp 3.x :

val client = OkHttpClient.Builder()
    .dns { hostname -> InetAddress.getAllByName(hostname).toList() }
    .build()
复制代码

OkHttp 升级到 4.x 后,在 Kotlin 中调用就需要修改为:

val client = OkHttpClient.Builder()
    .dns(object : Dns {
      override fun lookup(hostname: String) =
          InetAddress.getAllByName(hostname).toList()
    })
    .build()
复制代码

OkHttp 中还有很多地方会遇到这个问题, Jetbrain 官方正在努力实现 Kotlin 接口的 SAM 转换,不过目前的版本还不支持。

Companion 的导入

Java 的静态方法在 Kotlin 中的等效实现就是 Companion 对象方法,它们的字节码是一样的,但是导入的时候写法有区别。

譬如 OkHttp 3.x 中有:

import okhttp3.CipherSuite.forJavaName
复制代码

那么升级到 OkHttp 4.x 中,就要修改为:

import okhttp3.CipherSuite.Companion.forJavaName
复制代码

使用

GET 请求

同步请求

示例如下,这个是官方的示例代码的 Kotlin 版:

val client = OkHttpClient()

fun run(url: String): String? {
    val request: Request = Request.Builder()
        .url(url)
        .build()
    client.newCall(request).execute().use { response -> return response.body?.string() }
}
复制代码

发送同步 GET 请求很简单:

  1. 创建 OkHttpClient 实例 client
  2. 通过 Request.Builder 构建一个 Request 请求实例 request
  3. 通过 client.newCall(request) 创建一个 Call 的实例
  4. Call 的实例调用 execute 方法发送同步请求
  5. 请求返回的 response 转换为 String 类型返回

异步请求

val client = OkHttpClient()

fun run(url: String) {
    val request: Request = Request.Builder()
        .url(url)
        .build()
    client.newCall(request).enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {
            println("onResponse: ${response.body.toString()}")
        }

        override fun onFailure(call: Call, e: IOException) {
            println("onFailure: ${e.message}")
        }
    })
}
复制代码

步骤和同步请求类似,只是调用了 Callenqueue 方法异步请求,结果通过回调 CallbackonResponse 方法及 onFailure 方法处理。

POST 请求

同步请求

下面也是官方示例的 Kotlin 版:

import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

val JSON: MediaType = "application/json; charset=utf-8".toMediaType()
val client = OkHttpClient()

fun post(url: String, json: String): String? {
    val body: RequestBody = json.toRequestBody(JSON)
    val request: Request = Request.Builder()
        .url(url)
        .post(body)
        .build()
    client.newCall(request).execute().use { response -> return response.body?.string() }
}
复制代码

GET 同步请求类似,只是创建 Request 时通过 Request.Builder.post() 方法设置请求类型为 POST 请求并设置了请求体。

异步请求

import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

val JSON: MediaType = "application/json; charset=utf-8".toMediaType()
val client = OkHttpClient()

fun post(url: String, json: String) {
    val body: RequestBody = json.toRequestBody(JSON)
    val request: Request = Request.Builder()
        .url(url)
        .post(body)
        .build()
    client.newCall(request).enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {
            println("onResponse: ${response.body.toString()}")
        }

        override fun onFailure(call: Call, e: IOException) {
            println("onFailure: ${e.message}")
        }
    })
}
复制代码

GET 异步请求类似,同样是创建 Request 时通过 Request.Builder.post() 方法设置请求类型为 POST 请求并设置了请求体。

原文  https://juejin.im/post/5dd5077ef265da47b60c0248
正文到此结束
Loading...