OkHttp Mock测试在CI分层中的应用 - 第1节
OkHttp 是什么?先聊聊我的看法
做 Android 开发的朋友,对 OkHttp 应该不陌生。我最早接触它是在 2015 年,那时候项目里还在用 HttpURLConnection,每次写网络请求都挺痛苦的——代码冗余、连接管理得自己搞、遇到重定向还要手动处理。后来换了 OkHttp,说实话,有种“原来网络请求可以这么清爽”的感觉。
OkHttp 是 Square 公司开源的一个 HTTP 客户端。它在 Android 和 Java 圈子里基本是事实标准了。为什么大家愿意用?我总结了几点核心优势。
核心特性一览
| 特性 | 说明 | 我个人觉得最实用的地方 |
|---|---|---|
| HTTP/2 支持 | 同一个主机可以复用连接,减少延迟 | 接口多的页面,加载速度能快一倍 |
| 连接池 | 复用 TCP 连接,减少三次握手开销 | 频繁请求时特别明显,省流量也省时间 |
| Gzip 压缩 | 透明处理压缩,减小传输体积 | 我自己测试过,JSON 数据能压缩 60% 以上 |
| 响应缓存 | 支持 HTTP 缓存策略,避免重复请求 | 配合得当的话,弱网体验能提升很多 |
| 拦截器 | 请求/响应的拦截与修改 | 这是我最常用的功能,日志、重试、Mock 都靠它 |
| 失败重试 | 自动处理连接失败时的重试逻辑 | 注意了,默认是会自动重试的,有些场景要关掉 |
基本使用:GET 请求怎么做?
先看一个最基础的 GET 请求。我习惯把 OkHttp 客户端做成单例,这样连接池和配置可以复用。
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("https://api.example.com/users")
.addHeader("Accept", "application/json")
.build();
// 同步请求(注意:不能在主线程调用)
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
String body = response.body().string();
System.out.println("响应数据:" + body);
}
} catch (IOException e) {
e.printStackTrace();
}
execute() 方法会阻塞当前线程。我在一个老项目里看到有人在主线程直接调用了,结果 ANR 了。一定要放到子线程或者用异步方式。
异步 GET 请求
实际开发中,我更推荐用异步方式。OkHttp 的回调是在子线程执行的,不能直接更新 UI,记得切回主线程。
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 {
if (response.isSuccessful()) {
String body = response.body().string();
// 这里获取到 body 后,切到主线程更新 UI
}
}
});
你想想看,异步请求的好处是什么?不阻塞 UI 线程,用户操作流畅。但要注意一点:response.body().string() 只能调用一次。我刚开始就犯过这个错,第一次打印了日志,后面再取数据就为空了。
基本使用:POST 请求怎么做?
POST 请求稍微复杂一点,主要在于请求体的构建。常用的几种格式我都列出来。
Form 表单提交
RequestBody formBody = new FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build();
Request request = new Request.Builder()
.url("https://api.example.com/login")
.post(formBody)
.build();
JSON 数据提交
这是目前最常用的方式。用 MediaType 指定内容类型,然后用 RequestBody 传 JSON 字符串。
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String json = "{\"name\":\"张三\",\"age\":25}";
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url("https://api.example.com/user")
.post(body)
.addHeader("Content-Type", "application/json")
.build();
文件上传
File file = new File("/sdcard/photo.jpg");
RequestBody fileBody = RequestBody.create(
MediaType.parse("image/jpeg"), file);
MultipartBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", "photo.jpg", fileBody)
.build();
Request request = new Request.Builder()
.url("https://api.example.com/upload")
.post(body)
.build();
OkHttp 的整体结构
为了帮你理清 OkHttp 的内部脉络,我画了一张结构图。没有这张图,后续讲拦截器、连接池、Mock 测试时你会有点晕。
这张图展示了 OkHttp 的核心组件和请求从发起到响应返回的流程。注意看拦截器链条,这是 OkHttp 最灵活的地方,也是我们后续做 Mock 测试的关键切入点。
从这张图你能看到,OkHttp 把整个请求过程拆成了两层:应用层和网络层。中间那层拦截器链,就是它的灵魂。每个拦截器只做一件事,处理完了交给下一个。这种设计让扩展变得特别容易,我们后续课程要讲的 Mock 测试,就是通过自定义拦截器实现的。
几个重要的细节
- OkHttpClient 建议复用。 每次创建新的 Client 会浪费连接池和线程池资源。我之前在循环里 new 过,结果连接池完全没有起作用。
- Response body 需要关闭吗? OkHttp 会自动管理,但如果你用了
string()或bytes(),记得在 finally 块里close()或者用 try-with-resources。其实string()内部会帮你关闭,但为了代码健壮性,我建议显式关闭。 - 超时设置很有必要。 默认超时是 10 秒,有些场景不够。我习惯给 connect 和 read 分别设 30 秒,write 设 60 秒。太短容易误报网络问题,太长又影响用户体验。
- 别忘了添加 User-Agent。 有些服务器会检测 UA,没有的话可能会返回 403。我在一个爬虫项目里踩过这个坑,后来加了个模拟浏览器的 UA 就好了。