4. 使用OkHttp发起同步GET请求并打印响应头
OkHttp 同步 GET 请求:从依赖到响应头解析
OkHttp 的同步请求通过 Call.execute() 在当前线程阻塞执行,返回 Response 对象。与 HttpURLConnection 相比,OkHttp 把连接复用、重定向、GZIP 压缩等细节封装在内部,调用方只需关注请求构造与响应读取。本例聚焦一个明确任务:用同步方式发起 GET 请求,并准确取出响应头中的 Content-Type 与缓存控制字段。
同步请求会阻塞调用线程。在 Android 中,主线程(UI 线程)禁止网络操作,因此
execute() 必须放在子线程或工作线程中执行,否则会抛出 NetworkOnMainThreadException。依赖引入
在模块级 build.gradle 的 dependencies 中加入 OkHttp。版本号可按项目实际情况调整,此处使用 4.12.0 作为示例:
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
OkHttp 4.x 使用 Kotlin 编写,但提供完整的 Java 互操作 API,Java 与 Kotlin 项目均可直接调用。同步引入后无需额外配置,Gradle 会自动拉取 okio 等传递依赖。
同步 GET 请求的完整实现
下面是一段可直接放入 Android 项目、在子线程中调用的完整代码。它构造请求、执行同步调用、读取响应头,并在结束时关闭响应体:
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Headers;
import java.io.IOException;
public class SyncGetDemo {
// 复用同一个 OkHttpClient 实例,避免重复创建连接池与线程池
private static final OkHttpClient client = new OkHttpClient();
public static void fetchHeaders(String url) {
Request request = new Request.Builder()
.url(url)
.get() // 默认即为 GET,显式写出便于阅读
.header("Accept", "application/json")
.build();
// execute() 为同步调用,会阻塞当前线程
try (Response response = client.newCall(request).execute()) {
// 1. 基础状态
System.out.println("HTTP 状态码: " + response.code());
System.out.println("是否成功: " + response.isSuccessful());
// 2. 读取全部响应头
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
System.out.println(headers.name(i) + ": " + headers.value(i));
}
// 3. 解析 Content-Type
String contentType = response.header("Content-Type");
System.out.println("Content-Type = " + contentType);
// 4. 解析缓存控制字段
String cacheControl = response.header("Cache-Control");
System.out.println("Cache-Control = " + cacheControl);
// 5. 读取响应体(此处仅取长度,避免大响应占用内存)
if (response.body() != null) {
System.out.println("响应体字节数: " + response.body().contentLength());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
调用方式(在子线程中):
new Thread(() -> SyncGetDemo.fetchHeaders("https://httpbin.org/get")).start();
响应头解析要点
response.header(name)返回首个匹配值,大小写不敏感;response.headers(name)返回该名称的全部值列表,适合处理可能重复出现的头字段。Content-Type常见形式为application/json; charset=utf-8,分号前是 MIME 类型,分号后是字符集参数,解析时需按分号拆分。Cache-Control可能包含max-age、no-cache、no-store等指令,多个指令以逗号分隔,可据此判断响应是否可缓存及缓存有效期。- 响应体必须关闭。使用 try-with-resources 可确保
Response在代码块结束时自动关闭,避免连接泄漏。
结果判读与验证
以 https://httpbin.org/get 为例,服务端会回显请求信息。执行后预期可观察到:
| 观察项 | 预期结果 | 判断依据 |
|---|---|---|
| 状态码 | 200 | response.code() == 200,isSuccessful() 为 true |
| Content-Type | application/json | 包含 application/json 子串 |
| Cache-Control | 可能不存在或为 no-cache | httpbin 默认不强制缓存,需按实际返回值判断 |
| 响应体长度 | 大于 0 | contentLength() 返回正数或 -1(分块传输) |
若
Content-Type 返回 null,说明服务端未设置该头字段。此时不应直接按 JSON 解析响应体,而应先根据实际内容或与接口约定确认数据格式,避免解析异常。常见问题排查
- NetworkOnMainThreadException:同步请求写在了主线程。将
execute()移入Thread、ExecutorService或协程的 IO 调度器中。 - 响应体未关闭导致连接泄漏:始终使用 try-with-resources 包裹
Response,或手动在 finally 中调用response.close()。 - 重复创建 OkHttpClient:每个 OkHttpClient 都持有连接池和线程池,应作为静态单例复用,否则会造成资源浪费。
- header 名称大小写:HTTP 头字段名不区分大小写,
response.header("content-type")与response.header("Content-Type")等价。