1. OkHttp 简介与基本使用

刚接触 Android 网络请求那会儿,我最先用的其实是 HttpURLConnection —— 每次写起来都像在绣花,又长又容易出错。后来项目里引进了 OkHttp,我才发现原来网络请求可以写得这么清爽。今天我就跟你聊聊 OkHttp 的发展史、它擅长做什么、怎么集成,以及最基础的 GET 和 POST 请求怎么写。

1.1 发展史 – 一个连接协议的进化史

OkHttp 是 Square 公司(就是做 Retrofit、EventBus 那家)早在 2013 年开源的高性能 HTTP 库。我记得当年 Android 4.x 还占主导地位,HttpURLConnection 各种 bug 加上 API 设计蛋疼,Square 直接扔出了这个替代品。为什么它能火?因为 OkHttp 生来就解决了几个痛点:连接复用(避免三次握手反复来)、自动重定向、Gzip 解压、超时控制,而且它天然支持 SPDY 和 HTTP/2。

到现在,OkHttp 已经迭代了 4.x 甚至 5.x(还在预览)。每次升级都不忘向后兼容,这一点我必须给 Square 点赞。其实你现在去看任何主流 Android 项目,只要是涉及到网络请求的,底层十有八九是 OkHttp 在干活。

一点个人经验: 刚开始用 OkHttp 时,我总觉得拦截器没必要,后来有一次线上反馈接口偶发 302 重定向失败,排查了半天才发现是默认的 FollowRedirects 我没关。嗯,从那以后我学乖了 —— 至少要先理解一下 OkHttp 内部有哪些“开关”。

1.2 应用场景 – 它不仅仅是个 HTTP 客户端

说白了,OkHttp 就是为了让你的 App 跟服务器又快又稳地对话。下面这些场景我几乎每天都在用:

  • RESTful API 调用:GET 查询、POST 创建、PUT 更新、DELETE 删除,一个 OkHttpClient 全搞定。
  • 文件上传 / 下载:配合 MultipartBody 和 ResponseBody,断点续传也不难实现。
  • WebSocket 长连接:OkHttp 原生支持 WebSocket,适合聊天、实时推送。
  • 拦截器链做无侵入式功能:比如统一加 、打印日志、缓存策略,这些我通常写在拦截器里,而不是每个请求手写。
  • 性能测试 / Mock 数据:搭配 MockWebServer(Square 的另一个库)可以轻松模拟服务端响应。

关键结论: 无论你的 App 用什么架构(MVP、MVVM、MVI),网络层最终都绕不开 OkHttp 或它上面的封装(如 Retrofit)。

1.3 集成方式 – 一行依赖的事

在 build.gradle 里加上下面这行就行,记得版本号我习惯用最新稳定版。

dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}

如果你还需要日志打印,再加个日志拦截器:

implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'

嗯,还有一件事:使用 OkHttp 需要 INTERNET 权限,写在 AndroidManifest.xml 里:

<uses-permission android:name="android.permission.INTERNET" />

注意: 如果你的 App 目标 API 28+ 并且使用明文 HTTP,还需要在 manifest 中配置 android:usesCleartextTraffic="true" 或者在 network_security_config.xml 里单独放行。我曾经因为这个忘记配置,导致线上所有 HTTP 请求直接 404,排查了半小时才发现 —— 很惨,但印象深刻。

1.4 基本 GET 请求 – 同步与异步

OkHttp 的请求流程其实特别线型:构建 Client → 构建 Request → 发起 Call → 处理 Response。

同步 GET 示例:

// 1. 创建全局 OkHttpClient(复用连接池)
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .build();

// 2. 构造 Request
Request request = new Request.Builder()
        .url("https://api.example.com/users")
        .get()
        .build();

// 3. 发起同步请求(会阻塞当前线程)
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        String body = response.body().string();
        // 处理数据
    }
} catch (IOException e) {
    e.printStackTrace();
}

避坑指南: 同步请求不能在主线程执行,否则直接崩溃。我一开始写 demo 时偷懒放在 UI 线程,结果 ANR 警告弹得飞起。异步才是 Android 世界的常态。

异步 GET 示例:

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 网络异常、超时等
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
            String body = response.body().string();
            // 注意:这里在子线程,更新 UI 需要 post 到主线程
        }
    }
});

1.5 基本 POST 请求 – 表单与 JSON

POST 请求的套路和 GET 差不多,只是多了一个 RequestBody。

提交表单:

RequestBody formBody = new FormBody.Builder()
        .add("username", "张三")
        .add("password", "123456")
        .build();

Request request = new Request.Builder()
        .url("https://api.example.com/login")
        .post(formBody)
        .build();

提交 JSON:

MediaType JSON = MediaType.get("application/json; charset=utf-8");
String json = "{\"username\":\"张三\",\"password\":\"123456\"}";
RequestBody jsonBody = RequestBody.create(json, JSON);

Request request = new Request.Builder()
        .url("https://api.example.com/login")
        .post(jsonBody)
        .build();

你可能会问:每次都要手动拼 JSON 不累吗?没错,所以我后来项目里都上了 Retrofit,它内部默认用 OkHttp,只需写个接口定义就行。但底层原理还是这些,弄明白 OkHttp 的基本用法才能用好上层框架。

1.6 知识结构总览 – 一张图看清 OkHttp

为了帮你快速抓住脉络,我画了一张 OkHttp 的核心请求流程与组件架构。图中包含了客户端、请求、调用、拦截器链、网络层以及响应,你可以看到它们之间的依赖关系。

OkHttp 请求流程与核心组件 Application (App) OkHttpClient Request Call (execute/enqueue) 拦截器链 (Interceptor Chain) Network (HTTP/2 等) Response 应用层 App 构建 Client 与 Request 同步/异步 Call 核心层 拦截器链(重定向、缓存、日志…) 连接池复用 网络层 Socket / HTTP/2 DNS / TLS

1.7 本章要点回顾

知识点一句话总结
OkHttp 是什么Square 开源的 HTTP 客户端,支持 HTTP/2、连接复用、拦截器链
应用场景REST API、文件上传下载、WebSocket、拦截器增强
集成方式一行 gradle 依赖 + AndroidManifest 中的网络权限
同步 GETclient.newCall(request).execute()(需子线程)
异步 GETclient.newCall(request).enqueue(callback)(回调在子线程)
POST 表单使用 FormBody 构建参数
POST JSON使用 RequestBody.create(json, MediaType)

我的习惯: 会在 Application 中只初始化一个 OkHttpClient 实例,加上连接超时、读超时、写超时以及一个全局的日志拦截器。这样所有请求复用连接池,性能更好,而且统一维护拦截器也方便。你可以在后面的章节看到我是怎么用拦截器处理 自动刷新和重试的。