转载

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

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

OkHttp 有以下 优点 :

GZIP

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

版本选择

3.12.x vs 3.13+

关键变更

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

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

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

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

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

升级

在 Android 中将 OkHttp 从 3.12.x 升级到 3.13+ 版本,首先确认 minSdkVersion 至少为 21 , Gradle 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
  }
  ...  
}
复制代码

兼容 TLSv1 及 TLSv1.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 变更到了 Kotlin 。 Kotlin 相对于 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.x 中 basic 方法的参数 username 及 password 如果为 null ,则会转成字符串 "null" ,而 4.x 中这两个参数为非空字符串。
  4. HttpUrl.queryParameterValues()
    HttpUrl.queryParameterValues() 方法的返回类型为 List<String?>

新增的 @Deprecated 方法

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

譬如 3.x 中 OkHttpClient 中有方法:

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 IDEA 或 Android 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}")
        }
    })
}
复制代码

步骤和同步请求类似,只是调用了 Call 的 enqueue 方法异步请求,结果通过回调 Callback 的 onResponse 方法及 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...