OkHttp实战指南:从基础使用到高级特性
本文全面介绍了OkHttp网络库的核心功能和使用技巧,涵盖了同步与异步请求的基本用法、请求构建器与响应处理方法、文件上传下载与流式处理,以及超时控制与重试机制配置。通过详细的代码示例和最佳实践,帮助开发者从基础入门到高级特性掌握OkHttp的使用,提升网络请求的效率和可靠性。
同步与异步请求的基本用法
OkHttp提供了两种主要的请求执行方式:同步请求和异步请求。这两种方式各有其适用场景,理解它们的区别和使用方法对于构建高效的网络应用至关重要。
同步请求:阻塞式执行
同步请求是最直接的执行方式,调用线程会阻塞直到请求完成并返回响应。这种方式简单直观,适合在后台线程或需要等待结果的场景中使用。
Java同步请求示例
OkHttpClient client = new OkHttpClient();
public String fetchDataSync(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败,状态码: " + response.code());
}
return response.body().string();
}
}
Kotlin同步请求示例
val client = OkHttpClient()
fun fetchDataSync(url: String): String {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw IOException("请求失败,状态码: ${response.code}")
}
return response.body.string()
}
}
同步请求的执行流程可以用以下流程图表示:
flowchart TD
A[创建Request对象] --> B[调用execute方法]
B --> C[线程阻塞等待]
C --> D{请求成功?}
D -->|是| E[处理响应数据]
D -->|否| F[抛出异常]
E --> G[返回结果]
F --> G
异步请求:非阻塞式执行
异步请求通过回调机制实现非阻塞执行,调用线程不会被阻塞,请求结果通过回调接口返回。这种方式适合在主线程或需要避免阻塞的场景中使用。
Java异步请求示例
OkHttpClient client = new OkHttpClient();
public void fetchDataAsync(String url) {
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 处理请求失败
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败,状态码: " + response.code());
}
String responseData = responseBody.string();
// 处理响应数据
System.out.println(responseData);
}
}
});
}
Kotlin异步请求示例
val client = OkHttpClient()
fun fetchDataAsync(url: String) {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 处理请求失败
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) {
throw IOException("请求失败,状态码: ${response.code}")
}
val responseData = response.body.string()
// 处理响应数据
println(responseData)
}
}
})
}
异步请求的执行流程如下:
sequenceDiagram
participant MainThread
participant OkHttp
participant BackgroundThread
participant Callback
MainThread->>OkHttp: enqueue(request)
OkHttp->>BackgroundThread: 执行网络请求
BackgroundThread->>Callback: onResponse/onFailure
Callback->>MainThread: 回调处理结果
两种方式的对比与选择
为了帮助开发者更好地选择适合的请求方式,以下是同步和异步请求的详细对比:
| 特性 | 同步请求 | 异步请求 |
|---|---|---|
| 执行线程 | 调用线程阻塞 | 后台线程执行 |
| 响应方式 | 直接返回Response对象 | 通过回调接口返回 |
| 适用场景 | 后台线程、需要等待结果的场景 | 主线程、避免阻塞的场景 |
| 错误处理 | 通过异常抛出 | 通过onFailure回调 |
| 资源管理 | 需要手动关闭Response | 在回调中自动管理 |
| 性能影响 | 可能阻塞调用线程 | 不会阻塞调用线程 |
选择建议
-
使用同步请求的场景:
- 在后台线程中执行网络操作
- 需要等待请求结果才能继续执行的逻辑
- 批量处理多个顺序相关的请求
-
使用异步请求的场景:
- 在Android主线程中执行网络操作
- 需要保持UI响应的场景
- 并行处理多个独立请求
最佳实践与注意事项
资源管理
无论是同步还是异步请求,都需要妥善管理响应资源:
// 正确的资源管理方式
try (Response response = client.newCall(request).execute()) {
// 处理响应
String data = response.body().string();
// 使用数据
}
错误处理
完善的错误处理机制对于网络请求至关重要:
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
when {
e is SocketTimeoutException -> {
// 处理超时错误
Log.e("Network", "请求超时")
}
e is ConnectException -> {
// 处理连接错误
Log.e("Network", "连接失败")
}
else -> {
// 其他网络错误
Log.e("Network", "网络错误: ${e.message}")
}
}
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (response.isSuccessful) {
// 处理成功响应
val data = response.body.string()
processData(data)
} else {
// 处理HTTP错误状态码
Log.e("Network", "HTTP错误: ${response.code}")
}
}
}
})
线程安全考虑
在使用异步请求时,需要注意线程安全问题:
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
// 注意:这个回调在后台线程执行
// 如果需要更新UI,需要切换到主线程
runOnUiThread(() -> {
updateUI(response.body().string());
});
}
@Override
public void onFailure(Call call, IOException e) {
// 同样需要在主线程处理UI更新
runOnUiThread(() -> {
showError(e.getMessage());
});
}
});
通过合理选择同步或异步请求方式,并结合适当的错误处理和资源管理策略,可以构建出既高效又稳定的网络请求功能。
请求构建器与响应处理方法
OkHttp的请求构建器和响应处理是其核心功能之一,提供了灵活且强大的API来构建HTTP请求和处理服务器响应。通过流畅的构建器模式,开发者可以轻松地配置各种请求参数,而响应处理则提供了多种方式来读取和解析返回的数据。
Request.Builder:灵活的请求构建
OkHttp使用构建器模式来创建HTTP请求,Request.Builder类提供了丰富的方法来配置请求的各个方面。下面是一个完整的请求构建示例:
// 创建基本的GET请求
Request getRequest = new Request.Builder()
.url("https://api.example.com/users")
.get()
.build();
// 创建带参数的POST请求
MediaType JSON = MediaType.get("application/json; charset=utf-8");
String jsonBody = "{\"name\":\"John\", \"age\":30}";
RequestBody requestBody = RequestBody.create(jsonBody, JSON);
Request postRequest = new Request.Builder()
.url("https://api.example.com/users")
.post(requestBody)
.addHeader("Authorization", "Bearer token123")
.addHeader("Content-Type", "application/json")
.build();
// 创建带查询参数的请求
HttpUrl url = HttpUrl.parse("https://api.example.com/search")
.newBuilder()
.addQueryParameter("q", "okhttp")
.addQueryParameter("page", "1")
.addQueryParameter("limit", "10")
.build();
Request searchRequest = new Request.Builder()
.url(url)
.get()
.build();
请求构建器的主要方法
| 方法 | 描述 | 示例 |
|---|---|---|
.url() |
设置请求URL | .url("https://example.com") |
.get() |
设置GET方法 | .get() |
.post() |
设置POST方法并添加请求体 | .post(requestBody) |
.put() |
设置PUT方法 | .put(requestBody) |
.delete() |
设置DELETE方法 | .delete() |
.head() |
设置HEAD方法 | .head() |
.patch() |
设置PATCH方法 | .patch(requestBody) |
.addHeader() |
添加请求头 | .addHeader("Accept", "application/json") |
.header() |
设置请求头(覆盖同名头) | .header("User-Agent", "MyApp") |
.removeHeader() |
移除请求头 | .removeHeader("Cache-Control") |
请求体(RequestBody)的类型
OkHttp支持多种类型的请求体,适用于不同的数据格式:
// 1. 字符串请求体
RequestBody stringBody = RequestBody.create("plain text", MediaType.get("text/plain"));
// 2. JSON请求体
String json = "{\"key\":\"value\"}";
RequestBody jsonBody = RequestBody.create(json, MediaType.get("application/json"));
// 3. 表单数据
RequestBody formBody = new FormBody.Builder()
.add("username", "john")
.add("password", "secret")
.build();
// 4. 多部分表单数据(文件上传)
RequestBody multipartBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "My File")
.addFormDataPart("file", "file.txt",
RequestBody.create(new File("path/to/file.txt"), MediaType.get("text/plain")))
.build();
// 5. 流式请求体
RequestBody streamingBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.get("application/octet-stream");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
// 流式写入数据
sink.writeUtf8("Streaming data...");
}
};
响应处理:多种数据读取方式
OkHttp的Response对象提供了多种处理响应数据的方法:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
try (Response response = client.newCall(request).execute()) {
// 检查响应状态
if (!response.isSuccessful()) {
throw new IOException("Unexpected code: " + response);
}
// 方法1:以字符串形式读取响应体
String responseString = response.body().string();
System.out.println("Response as string: " + responseString);
// 方法2:以字节流形式读取
byte[] responseBytes = response.body().bytes();
// 方法3:使用流式处理(适用于大文件)
try (InputStream inputStream = response.body().byteStream()) {
// 处理输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// 方法4:使用Okio的Source(高性能处理)
try (BufferedSource source = response.body().source()) {
while (!source.exhausted()) {
String chunk = source.readUtf8(1024); // 每次读取1KB
System.out.print(chunk);
}
}
}
响应处理的最佳实践
flowchart TD
A[执行请求] --> B{响应是否成功?}
B -->|是| C[处理响应体]
B -->|否| D[处理错误]
C --> E[选择读取方式]
E --> F[小文本: string()]
E --> G[二进制数据: bytes()]
E --> H[大文件: byteStream()]
E --> I[高性能: source()]
F --> J[关闭响应体]
G --> J
H --> J
I --> J
D --> K[记录错误信息]
K --> L[抛出异常或重试]
高级响应处理技巧
1. 响应缓存控制
Response response = client.newCall(request).execute();
// 检查缓存相关信息
if (response.cacheResponse() != null) {
System.out.println("Response came from cache");
} else if (response.networkResponse() != null) {
System.out.println("Response came from network");
}
// 获取缓存控制头
String cacheControl = response.header("Cache-Control");
String expires = response.header("Expires");
2. 重定向处理
// 检查重定向信息
Response response = client.newCall(request).execute();
int redirectCount = 0;
Response priorResponse = response.priorResponse();
while (priorResponse != null) {
redirectCount++;
priorResponse = priorResponse.priorResponse();
}
System.out.println("Number of redirects: " + redirectCount);
3. 响应头处理
Response response = client.newCall(request).execute();
// 获取所有响应头
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
System.out.println(headers.name(i) + ": " + headers.value(i));
}
// 获取特定头信息
String contentType = response.header("Content-Type");
String contentLength = response.header("Content-Length");
String server = response.header("Server");
// 获取日期头(自动解析)
Date date = response.headers().getDate("Date");
错误处理和异常管理
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
// 处理成功响应
String responseData = response.body().string();
processResponseData(responseData);
} else {
// 处理HTTP错误状态
handleHttpError(response.code(), response.message());
}
} catch (IOException e) {
// 处理网络IO异常
if (e instanceof SocketTimeoutException) {
handleTimeoutError();
} else if (e instanceof ConnectException) {
handleConnectionError();
} else {
handleGenericNetworkError(e);
}
} finally {
// 确保资源释放
if (response != null) {
response.close();
}
}
性能优化建议
- 响应体复用:对于大响应,使用流式处理避免内存溢出
- 连接池管理:合理配置OkHttpClient的连接池参数
- 超时设置:根据网络状况设置合适的连接和读取超时
- 缓存策略:利用OkHttp的缓存机制减少网络请求
- GZIP压缩:OkHttp自动处理GZIP压缩,减少数据传输量
通过掌握OkHttp的请求构建和响应处理技巧,开发者可以构建出高效、可靠的网络请求逻辑,满足各种复杂的业务场景需求。
文件上传下载与流式处理
在现代应用开发中,文件的上传和下载是极其常见的需求。OkHttp提供了强大而灵活的工具来处理各种文件传输场景,从简单的文件下载到复杂的多部分表单上传,再到流式处理大文件,都能优雅地应对。
文件下载基础
OkHttp的文件下载非常简单直接。通过构建一个GET请求,我们可以获取服务器上的文件内容:
// Kotlin示例
fun downloadFile(url: String, outputFile: File) {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("下载失败: $response")
response.body?.source()?.use { source ->
outputFile.sink().buffer().use { sink ->
sink.writeAll(source)
}
}
}
}
// Java示例
public void downloadFile(String url, File outputFile) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("下载失败: " + response);
try (BufferedSource source = response.body().source();
BufferedSink sink = Okio.buffer(Okio.sink(outputFile))) {
sink.writeAll(source);
}
}
}
多部分文件上传
对于文件上传,OkHttp提供了MultipartBody类来处理RFC 2387标准的多部分表单数据。这是处理文件上传最常用的方式:
// 单文件上传示例
fun uploadFile(file: File, uploadUrl: String) {
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"file",
file.name,
file.asRequestBody("application/octet-stream".toMediaType())
)
.addFormDataPart("description", "这是一个示例文件")
.build()
val request = Request.Builder()
.url(uploadUrl)
.post(requestBody)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("上传失败: $response")
println("上传成功: ${response.body?.string()}")
}
}
多文件与混合内容上传
OkHttp支持同时上传多个文件和文本字段,甚至可以嵌套多部分内容:
// 多文件上传示例
fun uploadMultipleFiles(files: List<File>, uploadUrl: String) {
val multipartBuilder = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("username", "john_doe")
.addFormDataPart("email", "john@example.com")
files.forEachIndexed { index, file ->
multipartBuilder.addFormDataPart(
"file$index",
file.name,
file.asRequestBody("application/octet-stream".toMediaType())
)
}
val request = Request.Builder()
.url(uploadUrl)
.post(multipartBuilder.build())
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("多文件上传失败")
println("上传成功")
}
}
流式文件处理
对于大文件,OkHttp支持流式处理,避免内存溢出问题:
flowchart TD
A[开始文件下载] --> B[创建请求对象]
B --> C[执行网络请求]
C --> D{请求是否成功?}
D -->|是| E[获取响应体Source]
D -->|否| F[抛出异常]
E --> G[创建文件Sink]
G --> H[流式写入文件]
H --> I[关闭资源]
I --> J[下载完成]
F --> K[错误处理]
// 流式下载大文件
fun streamDownloadLargeFile(url: String, outputFile: File, progressListener: ProgressListener) {
val request = Request.Builder()
.url(url)
.build()
val clientWithProgress = client.newBuilder()
.addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder()
.body(ProgressResponseBody(originalResponse.body!!, progressListener))
.build()
}
.build()
clientWithProgress.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("下载失败")
response.body?.source()?.use { source ->
outputFile.sink().buffer().use { sink ->
sink.writeAll(source)
}
}
}
}
// 进度监听接口
interface ProgressListener {
fun update(bytesRead: Long, contentLength: Long, done: Boolean)
}
// 进度响应体包装器
class ProgressResponseBody(
private val responseBody: ResponseBody,
private val progressListener: ProgressListener
) : ResponseBody() {
private var bufferedSource: BufferedSource? = null
override fun contentType() = responseBody.contentType()
override fun contentLength() = responseBody.contentLength()
override fun source(): BufferedSource {
if (bufferedSource == null) {
bufferedSource = source(responseBody.source()).buffer()
}
return bufferedSource!!
}
private fun source(source: Source): Source = object : ForwardingSource(source) {
var totalBytesRead = 0L
override fun read(sink: Buffer, byteCount: Long): Long {
val bytesRead = super.read(sink, byteCount)
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
return bytesRead
}
}
}
高级文件上传特性
OkHttp还支持一些高级的文件上传特性:
自定义边界和内容类型
// 自定义边界和内容类型
fun uploadWithCustomBoundary(file: File, uploadUrl: String) {
val multipartBody = MultipartBody.Builder("my-custom-boundary-123")
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.name, file.asRequestBody())
.addFormDataPart("timestamp", System.currentTimeMillis().toString())
.build()
val request = Request.Builder()
.url(uploadUrl)
.post(multipartBody)
.build()
client.newCall(request).execute().use { response ->
// 处理响应
}
}
带进度监控的文件上传
// 带进度监控的上传
fun uploadWithProgress(file: File, uploadUrl: String, progressListener: ProgressListener) {
val requestBody = object : RequestBody() {
override fun contentType() = "application/octet-stream".toMediaType()
override fun contentLength() = file.length()
override fun writeTo(sink: BufferedSink) {
var uploaded = 0L
file.source().use { source ->
val buffer = Buffer()
var read: Long
while (source.read(buffer, 2048).also { read = it } != -1L) {
sink.write(buffer, read)
uploaded += read
progressListener.update(uploaded, contentLength(), false)
buffer.clear()
}
}
progressListener.update(uploaded, contentLength(), true)
}
}
val multipartBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.name, requestBody)
.build()
val request = Request.Builder()
.url(uploadUrl)
.post(multipartBody)
.build()
client.newCall(request).execute().use { response ->
// 处理响应
}
}
文件传输最佳实践
在实际项目中,文件上传下载需要注意以下几个最佳实践:
- 内存管理:对于大文件,始终使用流式处理避免内存溢出
- 进度反馈:为用户提供上传下载进度反馈
- 错误处理:妥善处理网络中断、服务器错误等情况
- 重试机制:为重要的文件传输实现重试逻辑
- 取消支持:允许用户取消长时间的文件传输操作
// 支持取消的文件下载
class CancellableDownload(
private val client: OkHttpClient,
private val url: String,
private val outputFile: File
) {
private var call: Call? = null
fun start(progressListener: ProgressListener? = null) {
val request = Request.Builder().url(url).build()
val clientWithProgress = if (progressListener != null) {
client.newBuilder()
.addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder()
.body(ProgressResponseBody(originalResponse.body!!, progressListener))
.build()
}
.build()
} else {
client
}
call = clientWithProgress.newCall(request)
call!!.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 处理失败
}
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful) {
// 处理错误响应
return
}
response.body?.source()?.use { source ->
outputFile.sink().buffer().use { sink ->
sink.writeAll(source)
}
}
// 下载完成
}
})
}
fun cancel() {
call?.cancel()
}
}
通过OkHttp的强大功能,我们可以轻松实现各种复杂的文件传输需求,从简单的下载到复杂的多文件上传,再到带进度监控的流式处理,OkHttp都提供了简洁而强大的API支持。
超时控制与重试机制配置
在网络请求中,超时控制和重试机制是确保应用稳定性和可靠性的关键特性。OkHttp提供了细粒度的超时配置和智能的重试策略,让开发者能够根据具体业务需求进行精确调优。
超时配置详解
OkHttp支持多种类型的超时设置,每种超时控制不同的网络交互阶段:
1. 连接超时(Connect Timeout)
连接超时控制建立TCP连接的最大等待时间,默认值为10秒。这个超时适用于从开始连接到完成TCP握手的过程。
val client = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS) // 设置5秒连接超时
.build()
2. 读取超时(Read Timeout)
读取超时控制从服务器接收数据的最大等待时间,默认值为10秒。这个超时适用于从发送请求到接收完整响应的整个过程。
val client = OkHttpClient.Builder()
.readTimeout(15, TimeUnit.SECONDS) // 设置15秒读取超时
.build()
3. 写入超时(Write Timeout)
写入超时控制向服务器发送数据的最大等待时间,默认值为10秒。这个超时适用于请求体的上传过程。
val client = OkHttpClient.Builder()
.writeTimeout(8, TimeUnit.SECONDS) // 设置8秒写入超时
.build()
4. 完整调用超时(Call Timeout)
完整调用超时控制整个HTTP请求从开始到结束的最大时间,包括重试和重定向。默认情况下此超时未启用。
val client = OkHttpClient.Builder()
.callTimeout(30, TimeUnit.SECONDS) // 设置30秒完整调用超时
.build()
超时配置的最佳实践
不同场景下的超时配置建议:
| 场景类型 | 连接超时 | 读取超时 | 写入超时 | 完整调用超时 |
|---|---|---|---|---|
| 内部API调用 | 3-5秒 | 10-15秒 | 5-8秒 | 30-60秒 |
| 外部API调用 | 5-8秒 | 15-30秒 | 8-15秒 | 60-120秒 |
| 文件上传 | 5秒 | 60-300秒 | 60-300秒 | 300-600秒 |
| 实时通信 | 3秒 | 30秒 | 3秒 | 60秒 |
重试机制配置
OkHttp的自动重试机制能够在连接失败时智能地进行重试,提高请求的成功率。
启用连接失败重试
val client = OkHttpClient.Builder()
.retryOnConnectionFailure(true) // 默认启用
.build()
重试机制的工作原理
OkHttp的重试机制遵循以下规则:
flowchart TD
A[发起请求] --> B{请求失败?}
B -->|否| C[请求成功]
B -->|是| D{失败类型可重试?}
D -->|否| E[失败结束]
D -->|是| F{重试次数<br>小于最大限制?}
F -->|否| E
F -->|是| G[等待短暂间隔]
G --> H[选择新路由]
H --> A
可重试的异常类型
OkHttp会自动重试以下类型的连接失败:
- IO异常:连接超时、读取超时、写入超时
- SSL握手异常:特定的SSL协议错误
- 路由失败:特定IP地址的连接失败
不可重试的情况
以下情况不会自动重试:
- 应用层错误(4xx、5xx状态码)
- SSL证书验证失败
- 协议错误
- 非幂等的HTTP方法(POST、PATCH等)
自定义重试策略
对于更复杂的重试需求,可以通过拦截器实现自定义重试逻辑:
class RetryInterceptor(private val maxRetries: Int) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
var response: Response? = null
var exception: IOException? = null
for (attempt in 1..maxRetries) {
try {
response = chain.proceed(request)
if (response.isSuccessful) {
return response
}
} catch (e: IOException) {
exception = e
if (attempt == maxRetries) {
break
}
// 指数退避策略
Thread.sleep((100 * Math.pow(2.0, attempt.toDouble())).toLong())
}
}
throw exception ?: IOException("Request failed after $maxRetries attempts")
}
}
// 使用自定义重试拦截器
val client = OkHttpClient.Builder()
.addInterceptor(RetryInterceptor(3))
.build()
超时与重试的协同工作
超时和重试机制需要协同配置才能发挥最佳效果:
val client = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS) // 快速失败连接问题
.readTimeout(30, TimeUnit.SECONDS) // 给重试留出足够时间
.callTimeout(120, TimeUnit.SECONDS) // 控制总体时间
.retryOnConnectionFailure(true) // 启用自动重试
.build()
高级配置:按请求设置超时
除了客户端级别的全局配置,还可以为单个请求设置特定的超时:
val request = Request.Builder()
.url("https://api.example.com/data")
.build()
val call = client.newCall(request)
call.timeout().timeout(10, TimeUnit.SECONDS) // 单个请求10秒超时
val response = call.execute()
监控和调试
为了有效监控超时和重试行为,可以添加事件监听器:
class TimeoutEventListener : EventListener() {
override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) {
println("连接开始: ${System.currentTimeMillis()}")
}
override fun connectEnd(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy, protocol: Protocol?) {
println("连接结束: ${System.currentTimeMillis()}")
}
override fun callFailed(call: Call, ioe: IOException) {
if (ioe is SocketTimeoutException) {
println("请求超时: ${ioe.message}")
}
}
}
val client = OkHttpClient.Builder()
.eventListener(TimeoutEventListener())
.build()
性能优化建议
- 连接超时:设置相对较短的值(3-5秒),快速发现网络不可达情况
- 读取超时:根据API响应时间特性设置,通常15-30秒较为合适
- 写入超时:对于大文件上传,需要设置较长的超时时间
- 重试策略:结合指数退避算法,避免对服务器造成雪崩效应
- 监控告警:对频繁超时的请求建立监控和告警机制
通过合理配置超时和重试机制,可以显著提升应用的网络请求成功率和用户体验。OkHttp提供的细粒度控制让开发者能够根据具体业务场景进行精确调优。
OkHttp作为一个强大而灵活的HTTP客户端库,提供了丰富的功能来满足各种网络请求需求。从基本的同步异步请求处理,到复杂的文件传输和流式操作,再到精细的超时控制和重试机制,OkHttp都展现了其卓越的性能和易用性。通过合理配置和最佳实践,开发者可以构建出高效、稳定、可靠的网络应用,提升用户体验和系统性能。
Kimi-K2.5Kimi K2.5 是一款开源的原生多模态智能体模型,它在 Kimi-K2-Base 的基础上,通过对约 15 万亿混合视觉和文本 tokens 进行持续预训练构建而成。该模型将视觉与语言理解、高级智能体能力、即时模式与思考模式,以及对话式与智能体范式无缝融合。Python00- QQwen3-Coder-Next2026年2月4日,正式发布的Qwen3-Coder-Next,一款专为编码智能体和本地开发场景设计的开源语言模型。Python00
xw-cli实现国产算力大模型零门槛部署,一键跑通 Qwen、GLM-4.7、Minimax-2.1、DeepSeek-OCR 等模型Go06
PaddleOCR-VL-1.5PaddleOCR-VL-1.5 是 PaddleOCR-VL 的新一代进阶模型,在 OmniDocBench v1.5 上实现了 94.5% 的全新 state-of-the-art 准确率。 为了严格评估模型在真实物理畸变下的鲁棒性——包括扫描伪影、倾斜、扭曲、屏幕拍摄和光照变化——我们提出了 Real5-OmniDocBench 基准测试集。实验结果表明,该增强模型在新构建的基准测试集上达到了 SOTA 性能。此外,我们通过整合印章识别和文本检测识别(text spotting)任务扩展了模型的能力,同时保持 0.9B 的超紧凑 VLM 规模,具备高效率特性。Python00
KuiklyUI基于KMP技术的高性能、全平台开发框架,具备统一代码库、极致易用性和动态灵活性。 Provide a high-performance, full-platform development framework with unified codebase, ultimate ease of use, and dynamic flexibility. 注意:本仓库为Github仓库镜像,PR或Issue请移步至Github发起,感谢支持!Kotlin08
VLOOKVLOOK™ 是优雅好用的 Typora/Markdown 主题包和增强插件。 VLOOK™ is an elegant and practical THEME PACKAGE × ENHANCEMENT PLUGIN for Typora/Markdown.Less00