升级到 OkHttp 4¶
OkHttp 4.x 将实现语言从 Java 升级到 Kotlin,同时保持其他一切不变。我们选择 Kotlin 是因为它提供了强大的新功能,并且能与 Java 紧密集成。
我们在保持与 OkHttp 3.x 的严格兼容性上投入了大量时间和精力。我们甚至保留了相同的包名:okhttp3
!
我们关注三种兼容性
-
二进制兼容性是指针对 OkHttp 3.x 编译的程序能够针对 OkHttp 4.x 运行。我们通过优秀的 japicmp 库及其 Gradle 插件来强制执行二进制兼容性。
-
Java 源代码兼容性是指在不修改
.java
文件的情况下,将使用 OkHttp 3.x 的 Java 代码升级到 4.x。 -
Kotlin 源代码兼容性是指在不修改
.kt
文件的情况下,将使用 OkHttp 3.x 的 Kotlin 代码升级到 4.x。
除了一些小的例外(如下所述),OkHttp 4.x 与 OkHttp 3.x 具有二进制和 Java 源代码兼容性。您可以将 OkHttp 4.x 的 .jar 文件用于为 OkHttp 3.x 构建的应用程序或库。
OkHttp 与 Kotlin 调用者不具备源代码兼容性,但由于 Kotlin 强大的废弃功能,升级应该是自动化的。大多数开发者可以使用 IntelliJ 的 代码清理 功能来实现安全快速的升级。
向后不兼容的变更¶
OkHttpClient final 方法¶
OkHttpClient
有 26 个访问器(例如 interceptors()
和 writeTimeoutMillis()
),它们在 OkHttp 3.x 中不是 final,但在 4.x 中是 final。这些访问器在 OkHttp 3.x 中设置为非 final 是为了与 Mockito 等 mocking 框架一起使用。我们认为继承 OkHttpClient
不是使用 OkHttp 进行测试的正确方法。如果必须进行模拟,请模拟 Call.Factory
接口,OkHttpClient
就是实现了这个接口。
内部 API 变更¶
okhttp3.internal
包不是公开的 API,我们经常在没有任何警告的情况下对其进行更改。依赖此包中的代码是不好的做法,在任何升级过程中都会给您带来问题!但是对于那些从该包导入的开发者来说,4.x 将会特别痛苦!我们做了很多改动来利用 Kotlin 的强大特性。
Credentials.basic()¶
Credentials.basic()
方法的用户名和密码参数现在是非空字符串。在 OkHttp 3.x 中,null 会产生用户名或密码为“null”。
HttpUrl.queryParameterValues()¶
HttpUrl.queryParameterValues()
的返回类型是 List<String?>
。可能包含 null 的列表不常见,Kotlin 调用者可能错误地将结果赋值给了 List<String>
。
代码清理¶
IntelliJ 和 Android Studio 提供了代码清理功能,可以自动将已废弃的 API 更新为新的 API。可以通过 搜索任意位置 对话框(双击 shift)或 分析 菜单访问此功能。
我们在 OkHttp 4.0 中包含了已废弃的 API,因为它们可以简化迁移。我们将在未来的版本中移除它们!如果您正在跳过版本,最好先升级到 OkHttp 4.0 作为中间步骤,这样会更容易。
Vars 和 Vals¶
Java 没有对属性的语言支持,因此开发者使用 getter 和 setter。Kotlin 拥有属性,我们在 OkHttp 中利用了它们。
- Address:certificatePinner, connectionSpecs, dns, hostnameVerifier, protocols, proxy, proxyAuthenticator, proxySelector, socketFactory, sslSocketFactory, url
- Cache:directory
- CacheControl:immutable, maxAgeSeconds, maxStaleSeconds, minFreshSeconds, mustRevalidate, noCache, noStore, noTransform, onlyIfCached, sMaxAgeSeconds
- Challenge:authParams, charset, realm, scheme
- CipherSuite:javaName
- ConnectionSpec:cipherSuites, supportsTlsExtensions, tlsVersions
- Cookie:domain, expiresAt, hostOnly, httpOnly, name, path, persistent, value
- Dispatcher:executorService
- FormBody:size
- Handshake:cipherSuite, localCertificates, localPrincipal, peerCertificates, peerPrincipal, tlsVersion
- HandshakeCertificates:keyManager, trustManager
- Headers:size
- HeldCertificate:certificate, keyPair
- HttpLoggingInterceptor:level
- HttpUrl:encodedFragment, encodedPassword, encodedPath, encodedPathSegments, encodedQuery, encodedUsername, fragment, host, password, pathSegments, pathSize, port, query, queryParameterNames, querySize, scheme, username
- MockResponse:headers, http2ErrorCode, socketPolicy, status, trailers
- MockWebServer:bodyLimit, port, protocolNegotiationEnabled, protocols, requestCount, serverSocketFactory
- MultipartBody.Part:body, headers
- MultipartBody.:boundary, parts, size, type
- OkHttpClient:authenticator, cache, callTimeoutMillis, certificatePinner, connectTimeoutMillis, connectionPool, connectionSpecs, cookieJar, dispatcher, dns, eventListenerFactory, followRedirects, followSslRedirects, hostnameVerifier, interceptors, networkInterceptors, pingIntervalMillis, protocols, proxy, proxyAuthenticator, proxySelector, readTimeoutMillis, retryOnConnectionFailure, socketFactory, sslSocketFactory, writeTimeoutMillis
- PushPromise:headers, method, path, response
- Request:body, cacheControl, headers, method, url
- Response:body, cacheControl, cacheResponse, code, handshake, headers, message, networkResponse, priorResponse, protocol, receivedResponseAtMillis, request, sentRequestAtMillis
- Route:address, proxy, socketAddress
- TlsVersion:javaName
函数重命名¶
- Headers.of():为了与
listOf()
,setOf()
等保持对称性,我们将Headers.of(String...)
替换为headersOf(vararg String)
。
扩展函数¶
在我们认为合适的地方,我们将静态函数迁移到了扩展函数。
Java | Kotlin |
---|---|
Handshake.get(SSLSession) | SSLSession.handshake() |
Headers.of(Map |
Map |
HttpUrl.get(String) | String.toHttpUrl() |
HttpUrl.get(URI) | URI.toHttpUrlOrNull() |
HttpUrl.get(URL) | URL.toHttpUrlOrNull() |
HttpUrl.parse(String) | String.toHttpUrlOrNull() |
HttpUrl.uri() | HttpUrl.toUri() |
HttpUrl.url() | HttpUrl.toUrl() |
MediaType.get(String) | String.toMediaType() |
MediaType.parse(String) | String.toMediaTypeOrNull() |
RequestBody.create(ByteArray) | ByteArray.toRequestBody() |
RequestBody.create(ByteString) | ByteString.toRequestBody() |
RequestBody.create(File) | File.asRequestBody() |
RequestBody.create(String) | String.toRequestBody() |
ResponseBody.create(BufferedSource) | BufferedSource.asResponseBody() |
ResponseBody.create(ByteArray) | ByteArray.toResponseBody() |
ResponseBody.create(ByteString) | ByteString.toResponseBody() |
ResponseBody.create(String) | String.toResponseBody() |
SAM 转换¶
当您在 Kotlin 中使用 Java API 时,可以将 Java 接口视为 Kotlin lambda 来操作。此功能适用于定义了单个抽象方法 (SAM) 的接口。
但是当您在 Kotlin 中使用 Kotlin API 时,没有自动转换。在 OkHttp 3.x 中使用了 SAM lambda 的代码:在 OkHttp 4.x 中必须使用 object :
Kotlin 调用 OkHttp 3.x
val client = OkHttpClient.Builder()
.dns { hostname -> InetAddress.getAllByName(hostname).toList() }
.build()
Kotlin 调用 OkHttp 4.x
val client = OkHttpClient.Builder()
.dns(object : Dns {
override fun lookup(hostname: String) =
InetAddress.getAllByName(hostname).toList()
})
.build()
SAM 转换影响以下 API
- Authenticator
- Dispatcher.setIdleCallback(Runnable)
- Dns
- EventListener.Factory
- HttpLoggingInterceptor.Logger
- LoggingEventListener.Factory
- OkHttpClient.Builder.hostnameVerifier(HostnameVerifier)
JetBrains 正在开发 Kotlin 接口的 SAM 转换。预计将在未来的 Kotlin 语言版本中推出。
伴生对象导入¶
Kotlin 中与 Java 的静态方法等效的是伴生对象函数。字节码是相同的,但 .kt
文件现在需要在 import 中包含 Companion
。
这在 OkHttp 3.x 中有效
import okhttp3.CipherSuite.forJavaName
但 OkHttp 4.x 需要 Companion
import okhttp3.CipherSuite.Companion.forJavaName
万一您有很多此类情况,请运行此命令
sed -i "" \
's/^\(import okhttp3\.[^.]*\)\.\([a-z][a-zA-Z]*\)$/\1.Companion.\2/g' \
`find . -name "*.kt"`
高级性能分析¶
Android Studio 的高级性能分析功能会重写 OkHttp 字节码进行 instrumentation。不幸的是,它在 OkHttp 4.x 的字节码上会崩溃。在 Google 的错误 修复之前,您必须在 Android Studio 中禁用高级性能分析。
R8 / ProGuard¶
R8 和 ProGuard 都是用于 .class
文件的代码优化器。
R8 是 Android Studio 3.4 及更新版本中的默认优化器。它与所有 OkHttp 版本都能很好地配合使用。
ProGuard 是之前的默认优化器。我们正在跟踪 ProGuard、OkHttp 4.x 和 Kotlin 生成的 .class
文件之间的交互问题。如果您正在使用 ProGuard,请确保使用最新版本。
Gradle¶
OkHttp 4 的最低要求是 Java 8+ 和 Android 5+。这些要求是随 OkHttp 3.13 首次引入的。
以下是您需要在 build.gradle
中配置的内容,以便 Kotlin、Java 和 Android 插件分别面向 Java 8 字节码。
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "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
}
}