Android 网络编程基础回顾
说实话,很多 Android 开发者工作两三年了,还在用 OkHttp 写网络请求,但一问起 HTTP 协议底层的东西——比如请求行、状态码、报文结构——就支支吾吾。我个人习惯是:先把地基打牢,再去谈框架怎么封装。
这节我们就快速过一下 HTTP 协议、URLConnection 和 HttpClient 的对比,以及 AsyncTask 为什么让你“又爱又恨”。这几个基础点,正是后续理解 OkHttp 与 GATT 回调泄漏混合场景的关键铺垫。
1. HTTP 协议,你真的懂了吗?
HTTP 是超文本传输协议,说白了就是客户端和服务器之间约定好的“说话格式”。我见过不少同事,明明返回 404 了还在代码里疯狂抓取 response body,就是没有先检查状态码——这就是协议基础不牢。
核心要点:请求-响应模型
- 请求行:GET /index.html HTTP/1.1
- 请求头:Host、User-Agent、Content-Type 等
- 请求体:POST 时传输的数据(JSON / 表单)
- 状态码:2xx 成功 / 3xx 重定向 / 4xx 客户端错误 / 5xx 服务器错误
- 响应头:Content-Length、Set-Cookie、Cache-Control
💡 我的经验:有一次线上问题,用户反馈图片加载慢,我抓包发现服务端返回的 Cache-Control: no-cache 导致每次都重新下载。如果不懂这些头字段,排查方向就会跑偏。
Android 中的 HTTP 版本支持
| API 级别 | 默认支持的协议 |
|---|---|
| API 23 以下 | HTTP/1.1 + SSL |
| API 23+ | HTTP/1.1 + HTTP/2 (需 OkHttp 或 Cronet) |
| API 28+ | HTTP/2 可通过 android:usesCleartextTraffic 配置 |
你想想看,HTTP/2 多路复用、头部压缩,这些特性对移动端网络优化意义重大——但前提是你得用支持它的客户端。OkHttp 从 3.0 开始就原生支持 HTTP/2,这也是它取代原生 API 的原因之一。
2. URLConnection 与 HttpClient:老将退役
Android 原生网络 API 经历了两个阶段:早期 HttpURLConnection,后来 Google 又加入了 Apache HttpClient。嗯,这两兄弟各有各的槽点。
对比一览
| 特性 | HttpURLConnection | Apache HttpClient |
|---|---|---|
| 易用性 | 代码冗长,需要手动处理流 | 相对简洁,不过 API 混乱 |
| 连接池 | 默认不开启(需自己实现) | 内置连接池,但不够灵活 |
| 重定向 | 自动跟随 | 需手动配置 |
| HTTPS | 支持,但配置复杂 | 支持,但证书校验逻辑可读性差 |
| 性能 | 较慢,无压缩 | 稍好,但多线程下会出现死锁 |
| Android 官方态度 | 推荐(API 28 后仍是底层实现) | Android 6.0 开始移除 |
⚠️ 曾经踩过的坑:我参与过一个遗留项目,还在用 Apache HttpClient 4.x 打请求。某个版本升级后,因为库冲突导致所有 POST 请求都变成 GET,排查了一整天才发现是 Android 9 移除了 httpclient 相关类。从那以后,我只用 OkHttp。
说白了,这两个原生 API 最大的问题就是:没有自动重试、没有连接池、没有拦截器机制。你写一个简单的 JSON 请求可能需要 30 行代码,还得自己关流、处理线程。
// 使用 HttpURLConnection 的典型样板代码
URL url = new URL("https://api.example.com/data");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
}
conn.disconnect();
你看,就一个 GET 请求,光 try-catch 就要写三四层。更别提文件上传、进度回调了——写法简直折磨人。
3. AsyncTask:为什么它被废弃了?
AsyncTask 是 Android 1.6 就引入的线程工具类,初衷是解决「主线程不能做网络请求」的问题。我在早期项目里用它做过图片加载、数据解析,但后来项目越做越大,AsyncTask 的毛病全暴露了。
局限性总结
- 内存泄漏:非静态内部类持有 Activity 引用,Activity 销毁后 AsyncTask 还在跑,导致内存泄漏。
- 串行执行:默认单线程池(AsyncTask 在 Android 11 前是串行),多个任务会排队,无法并发。
- 生命周期不可控:AsyncTask 没有和 Activity/Fragment 生命周期联动,屏幕旋转时 Activity 重建,AsyncTask 的 callback 会更新旧的 View,导致崩溃。
- 缺乏错误处理:doInBackground 里抛异常直接 crash,除非自己 catch 并传参。
- 调试困难:嵌套 AsyncTask 时,执行顺序和线程切换完全靠猜。
💣 血泪教训:我曾经在一个大列表页面里,每项都启动一个 AsyncTask 去加载头像。结果屏幕旋转后,几十个 AsyncTask 同时回调更新已销毁的 ImageView,直接 ANR。这种问题用 AsyncTask 几乎无解,后来改用 RxJava + 生命周期绑定才彻底解决。
所以 Android 官方在 API 30 正式标记 AsyncTask 为废弃,并推荐使用 Kotlin 协程 或 线程池 + Handler。你可能会问:"那我不学 AsyncTask 行不行?"——我的建议是:至少要知道它为什么不行,这样你才能理解 OkHttp 的异步回调设计有多巧妙。
知识框架图:从原生到 OkHttp 的演进
下面这张图展示了 Android 网络编程的基础组件关系,以及 AsyncTask 被抛弃的原因——它本来就不是为网络请求而生的。
小结一下
这一节我们回顾了 HTTP 协议的核心要素,对比了两个原生网络 API 的优劣,也剖析了 AsyncTask 被废弃的真正原因。说白了,这些东西都是你在面试、写代码、排查问题时的基本功。下一节我们会真正进入 OkHttp 的世界——但如果你连「为什么不用 URLConnection」都说不清,那后面的优化技巧你也很难理解到位。
💡 新手特别提示:不要试图用 AsyncTask 封装网络请求!即使你只是写一个小 Demo,也请直接从 OkHttp 或者 Retrofit 开始。养成好习惯,以后重构成本低很多。