一、OkHttp 定位

OkHttp 是由 Square 公司开源的一款高效、轻量级的 HTTP 客户端,支持 Android 和 Java 平台,目前是 Android 开发中最主流的网络请求框架之一(Retrofit 底层也默认依赖 OkHttp 实现网络请求)。

它的设计目标是简化 HTTP 请求操作,提升网络请求性能,处理常见的网络问题(如连接超时、重定向、缓存等),让开发者无需关注底层复杂的网络细节,专注于业务逻辑实现。

二、核心特性

  1. 支持多种 HTTP 协议版本:默认支持 HTTP/1.1、HTTP/2,可选支持 HTTP/3(QUIC),HTTP/2 支持多路复用,可在单个连接上并发多个请求,大幅提升并发性能。
  2. 连接池复用:内置高效的连接池(ConnectionPool),复用已建立的 TCP 连接,避免频繁创建 / 销毁连接带来的性能开销(默认保持 5 个空闲连接,存活时间 5 分钟)。
  3. 透明的数据压缩:自动对请求体和响应体进行 GZIP 压缩,减少网络传输的数据量,提升请求速度。
  4. 强大的请求缓存机制:支持 HTTP 缓存规范,可缓存响应数据,在无网络或缓存未过期时直接读取本地缓存,减少网络请求,提升离线体验。
  5. 灵活的超时与重试机制:支持设置连接超时、读取超时、写入超时,自动处理常见的网络错误(如连接失败、重试可用的请求),提升请求的容错性。
  6. 拦截器机制:提供强大的拦截器链(Interceptor Chain),支持自定义请求 / 响应的处理逻辑(如添加公共请求头、打印日志、Token 刷新、数据加密等)。
  7. 安全的 HTTPS 支持:默认支持 HTTPS,提供证书验证、自定义证书、忽略证书(仅测试环境使用)等功能,保障网络传输的安全性。
  8. 便捷的请求取消:支持单个请求取消或批量请求取消,避免无用请求占用资源。
  9. 建造者模式封装:请求(Request)、客户端(OkHttpClient)均采用建造者模式构建,API 简洁易用,可读性强。

三、核心组件

OkHttp 的核心组件相互配合完成网络请求,关键组件如下:

1. OkHttpClient

  • 核心:OkHttp 的客户端实例,相当于一个 "网络请求工厂",负责创建和执行Call对象。
  • 特性:采用建造者模式(OkHttpClient.Builder)创建,包含连接池、拦截器、超时时间、缓存等所有全局配置。
  • 最佳实践:推荐全局单例使用,因为创建OkHttpClient会初始化连接池、线程池等资源,频繁创建会造成资源浪费和性能下降。

2. Request

  • 核心:表示一个 HTTP 请求对象,包含请求的 URL、请求方法(GET/POST/PUT 等)、请求头、请求体、缓存策略等信息。
  • 特性:同样采用建造者模式(Request.Builder)构建,一个Request对应一个具体的网络请求。
  • 关键属性:
    • url():设置请求 URL。
    • method():设置请求方法(默认 GET)。
    • header()/headers():设置单个 / 多个请求头。
    • body():设置请求体(仅 POST、PUT 等方法需要)。
    • cacheControl():设置缓存策略(如强制缓存、禁止缓存)。

3. Response

  • 核心:表示一个 HTTP 响应对象,是服务器返回的结果封装,包含响应码、响应头、响应体、缓存信息、请求对应的Request等。
  • 关键属性:
    • code():HTTP 响应码(200 表示成功、404 表示未找到、500 表示服务器错误等)。
    • message():响应消息描述。
    • headers():响应头信息。
    • body():响应体(ResponseBody),包含服务器返回的实际数据(如 JSON、文件流等)。
    • cacheResponse():缓存的响应(若命中缓存)。
    • networkResponse():网络获取的响应(若从网络请求)。

4. ResponseBody

  • 核心:封装 HTTP 响应的实际数据,是一次性资源,使用后必须关闭,否则会造成资源泄漏。
  • 常用方法:
    • string():将响应体转换为 UTF-8 编码的字符串(适合小数据,如 JSON)。
    • bytes():将响应体转换为字节数组(适合二进制小数据)。
    • byteStream():获取响应体的输入流(适合大数据,如文件下载)。
    • contentType():获取响应体的媒体类型(如application/jsonimage/jpeg)。
  • 关闭方式:可通过close()手动关闭,或使用 try-with-resources 语法(Java 7+)自动关闭。

5. Call

  • 核心:表示一个已经准备好执行的请求 / 响应会话,是连接RequestResponse的桥梁。
  • 创建方式:通过OkHttpClient.newCall(Request)创建。
  • 两种执行方式:
    • 同步执行:execute(),在当前线程阻塞执行,返回Response不可在 Android 主线程执行(会阻塞 UI)。
    • 异步执行:enqueue(Callback),将请求加入请求队列,在 OkHttp 的线程池中执行,执行结果通过Callback回调返回(不会阻塞当前线程)。
  • 其他方法:cancel()(取消请求)、isCanceled()(判断是否已取消)、request()(获取对应的Request)。

6. Interceptor(拦截器)

  • 核心:拦截器是 OkHttp 的核心扩展点,采用责任链模式,可以在请求发送前、响应返回后对请求 / 响应进行处理。
  • 两种类型的拦截器(核心区别):
    类型 应用拦截器(Application Interceptor) 网络拦截器(Network Interceptor)
    添加方式 OkHttpClient.Builder.addInterceptor() OkHttpClient.Builder.addNetworkInterceptor()
    执行时机 仅在应用层执行,早于所有网络相关逻辑 在网络层执行,介于连接建立后和服务器通信前
    执行次数 无论是否重定向、重试,仅执行 1 次 每次网络请求都会执行(重定向、重试时会多次执行)
    可获取信息 仅能获取应用层的请求 / 响应,无法获取网络连接细节 可获取网络层的细节(如真实的请求 URL、连接信息)
    适用场景 添加公共请求头、打印完整请求日志、Token 刷新、数据加密 监控网络传输数据、修改网络层请求 / 响应、统计网络耗时
  • 核心方法:intercept(Chain chain)Chain表示拦截器链,通过chain.proceed(request)将请求传递给下一个拦截器,最终获取响应。

7. ConnectionPool(连接池)

  • 核心:负责管理 HTTP 连接的复用,减少 TCP 连接的创建开销。
  • 默认配置:最多保持 5 个空闲连接,空闲连接存活时间 5 分钟。
  • 自定义配置:可通过OkHttpClient.Builder.connectionPool(ConnectionPool)修改,如:

    java

    运行

    ConnectionPool connectionPool = new ConnectionPool(10, 30, TimeUnit.SECONDS);
    OkHttpClient client = new OkHttpClient.Builder()
            .connectionPool(connectionPool)
            .build();
    

四、OkHttp 工作流程(核心:拦截器链)

OkHttp 的请求执行核心是拦截器链,一个请求从发起到获取响应,会依次经过以下拦截器(按执行顺序):

  1. 应用拦截器:用户添加的应用拦截器(按添加顺序执行)。
  2. 重定向 / 重试拦截器(RetryAndFollowUpInterceptor:处理请求重定向(如 301、302)和请求重试(如连接失败)。
  3. 桥接拦截器(BridgeInterceptor:处理应用层和网络层的桥接,如添加默认请求头(User-AgentAccept-Encoding)、处理 GZIP 解压、处理缓存头。
  4. 缓存拦截器(CacheInterceptor:处理 HTTP 缓存,判断是否命中缓存、是否需要更新缓存、是否可以直接使用缓存。
  5. 连接拦截器(ConnectInterceptor:从连接池中获取或创建新的 TCP 连接,建立与服务器的连接。
  6. 网络拦截器:用户添加的网络拦截器(按添加顺序执行)。
  7. 服务端通信拦截器(CallServerInterceptor:最终的拦截器,负责向服务器发送请求数据、读取服务器响应数据,完成实际的网络通信。

流程总结

plaintext

请求发起(Call.execute()/enqueue())
→ 应用拦截器链(用户自定义)
→ 重定向/重试处理
→ 桥接处理(默认头、GZIP)
→ 缓存处理(命中缓存则直接返回,否则继续)
→ 连接建立(连接池复用或新建)
→ 网络拦截器链(用户自定义)
→ 与服务器通信(发送请求、接收响应)
→ 反向返回响应(按拦截器链倒序处理,缓存拦截器更新缓存、桥接拦截器解压等)
→ 响应返回给调用者

五、基本使用示例

1. 准备工作:添加依赖

Gradle(Android/Java)

gradle

// 最新版本可查看OkHttp官方仓库:https://github.com/square/okhttp
implementation 'com.squareup.okhttp3:okhttp:4.12.0'

// 若需要打印日志,可添加官方日志拦截器
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
Maven

xml

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>4.12.0</version>
</dependency>

2. 核心步骤

  1. 构建OkHttpClient实例(单例)。
  2. 构建Request实例。
  3. 创建Call实例并执行(同步 / 异步)。
  4. 处理Response响应(关闭ResponseBody)。

3. 具体示例

示例 1:异步 GET 请求(推荐,不阻塞线程)

java

运行

// 1. 构建OkHttpClient单例(全局唯一)
private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS) // 连接超时
        .readTimeout(10, TimeUnit.SECONDS)    // 读取超时
        .writeTimeout(10, TimeUnit.SECONDS)   // 写入超时
        .build();

// 2. 发起异步GET请求
public void asyncGetRequest(String url) {
    // 构建Request
    Request request = new Request.Builder()
            .url(url)
            .get() // 默认就是GET,可省略
            .header("User-Agent", "OkHttp-Demo")
            .build();

    // 3. 创建Call并执行异步请求
    Call call = OK_HTTP_CLIENT.newCall(request);
    call.enqueue(new Callback() {
        // 请求失败回调
        @Override
        public void onFailure(@NonNull Call call, @NonNull IOException e) {
            e.printStackTrace();
            // 处理失败逻辑(如提示用户网络错误)
        }

        // 请求成功回调
        @Override
        public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
            // 4. 处理响应(注意:onResponse在子线程执行,Android中更新UI需切换到主线程)
            if (response.isSuccessful()) { // isSuccessful() 等价于 response.code() >= 200 && response.code() < 300
                // 读取响应体(try-with-resources自动关闭ResponseBody)
                try (ResponseBody responseBody = response.body()) {
                    if (responseBody != null) {
                        String result = responseBody.string();
                        // 处理成功数据(如解析JSON)
                        System.out.println("响应结果:" + result);
                    }
                }
            } else {
                System.out.println("请求失败,响应码:" + response.code());
            }
        }
    });
}
示例 2:同步 POST 请求(表单提交)

java

运行

public String syncPostRequest(String url) throws IOException {
    // 1. 构建表单请求体
    RequestBody formBody = new FormBody.Builder()
            .add("username", "test")
            .add("password", "123456")
            .build();

    // 2. 构建Request
    Request request = new Request.Builder()
            .url(url)
            .post(formBody)
            .build();

    // 3. 同步执行请求(阻塞当前线程,不可在Android主线程执行)
    try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
        // 4. 处理响应
        if (response.isSuccessful() && response.body() != null) {
            return response.body().string();
        } else {
            throw new IOException("请求失败,响应码:" + response.code());
        }
    }
}

六、高级特性

1. 自定义拦截器(日志打印)

java

运行

// 自定义应用拦截器,打印请求/响应日志
public class LoggingInterceptor implements Interceptor {
    @NonNull
    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        // 请求发送前:打印请求信息
        Request request = chain.request();
        long startTime = System.currentTimeMillis();
        System.out.println(String.format("发送请求:%s %s%n请求头:%s",
                request.method(), request.url(), request.headers()));

        // 传递请求到下一个拦截器,获取响应
        Response response = chain.proceed(request);

        // 响应返回后:打印响应信息
        long endTime = System.currentTimeMillis();
        System.out.println(String.format("接收响应:%s %n响应码:%d%n响应头:%s%n耗时:%dms",
                response.request().url(), response.code(), response.headers(), (endTime - startTime)));

        return response;
    }
}

// 添加拦截器到OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new LoggingInterceptor()) // 应用拦截器
        // .addNetworkInterceptor(new LoggingInterceptor()) // 网络拦截器
        .build();

2. 配置缓存

java

运行

// 指定缓存目录和缓存大小(50MB)
File cacheDir = new File(Environment.getExternalStorageDirectory(), "OkHttpCache");
Cache cache = new Cache(cacheDir, 50 * 1024 * 1024); // 50MB

OkHttpClient client = new OkHttpClient.Builder()
        .cache(cache)
        // 自定义缓存策略(强制缓存10分钟)
        .addInterceptor(chain -> {
            Request request = chain.request();
            // 无网络时强制使用缓存
            if (!isNetworkAvailable()) {
                request = request.newBuilder()
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build();
            }
            Response response = chain.proceed(request);
            // 有网络时,缓存10分钟
            CacheControl cacheControl = new CacheControl.Builder()
                    .maxAge(10, TimeUnit.MINUTES)
                    .build();
            return response.newBuilder()
                    .header("Cache-Control", cacheControl.toString())
                    .removeHeader("Pragma") // 移除旧的缓存头
                    .build();
        })
        .build();

3. 文件上传(MultipartBody

java

运行

public void uploadFile(String url, File file) {
    // 构建多部分请求体(表单+文件)
    RequestBody requestBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("description", "测试文件上传")
            .addFormDataPart("file", file.getName(),
                    RequestBody.create(MediaType.parse("application/octet-stream"), file))
            .build();

    Request request = new Request.Builder()
            .url(url)
            .post(requestBody)
            .build();

    OK_HTTP_CLIENT.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(@NonNull Call call, @NonNull IOException e) {
            e.printStackTrace();
        }

        @Override
        public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
            if (response.isSuccessful()) {
                System.out.println("文件上传成功:" + response.body().string());
            }
        }
    });
}

七、最佳实践与注意事项

  1. OkHttpClient 单例化:全局只创建一个OkHttpClient实例,避免重复创建连接池、线程池等资源,造成性能浪费。
  2. 必须关闭ResponseBodyResponseBody是一次性资源,持有底层的 IO 流,使用后必须关闭(推荐使用 try-with-resources 语法),否则会造成内存泄漏和连接池阻塞。
  3. 避免主线程执行同步请求Call.execute()是阻塞方法,在 Android 主线程执行会导致 ANR(应用无响应),仅可在子线程中使用。
  4. 异步回调切换主线程Call.enqueue()的回调方法(onFailure/onResponse)在 OkHttp 的子线程执行,Android 中若需要更新 UI,需通过HandlerLiveDataRxJava等切换到主线程。
  5. 合理配置超时时间:根据业务场景配置连接、读取、写入超时(一般 10-30 秒),避免超时时间过短导致正常请求失败,或过长导致无用请求阻塞。
  6. 拦截器区分使用场景:添加公共请求头、Token 刷新等使用应用拦截器;监控网络传输、统计网络耗时等使用网络拦截器。
  7. HTTPS 安全配置:正式环境中不要忽略证书验证,应使用自定义证书或信任系统默认证书,避免安全风险。
  8. 大文件处理:下载大文件时,使用ResponseBody.byteStream()获取输入流,分块写入本地文件,避免使用string()bytes()直接加载到内存,造成 OOM。

总结

  1. OkHttp 是高效、易用的 HTTP 客户端,支持 Android/Java,是 Retrofit 的底层依赖,核心优势是连接复用、拦截器机制、缓存支持。
  2. 核心组件:OkHttpClient(客户端)、Request(请求)、Response(响应)、Call(请求会话)、Interceptor(拦截器)。
  3. 工作核心:拦截器链,请求依次经过应用拦截器、重定向、桥接、缓存、连接、网络拦截器、服务端通信,反向返回响应。
  4. 最佳实践:单例化OkHttpClient、关闭ResponseBody、避免主线程同步请求、合理使用拦截器。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐