4. 使用OkHttp发起同步GET请求并打印响应头

使用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 在代码块结束时自动关闭,避免连接泄漏。
OkHttp 同步 GET 请求与响应头解析链路 Request 构造 url("https://httpbin.org/get") .get() .header("Accept", "application/json") .build() 不可变对象,线程安全 Call 同步执行 client.newCall(request) .execute() 阻塞当前线程 返回 Response 对象 Response 解析 response.code() response.headers() response.header("Content-Type") response.header("Cache-Control") try-with-resources 自动关闭 关键响应头字段解析 Content-Type 示例值:application/json; charset=utf-8 MIME 类型:application/json 字符集参数:charset=utf-8 Cache-Control 示例值:max-age=3600, public max-age=3600:缓存有效期 3600 秒 public:允许共享缓存存储

结果判读与验证

以 https://httpbin.org/get 为例,服务端会回显请求信息。执行后预期可观察到:

观察项预期结果判断依据
状态码200response.code() == 200,isSuccessful() 为 true
Content-Typeapplication/json包含 application/json 子串
Cache-Control可能不存在或为 no-cachehttpbin 默认不强制缓存,需按实际返回值判断
响应体长度大于 0contentLength() 返回正数或 -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") 等价。