19、网络请求:Retrofit + OkHttp、RESTful API设计、离线缓存
车载多媒体应用,说白了就是一辆车上的「信息中枢」。导航、音乐、语音助手、车辆状态……这些功能全都离不开网络请求。你想想看,用户开车时最怕什么?网络断了,地图加载不出来,音乐卡顿,那体验简直灾难。
所以这一章,我打算跟你聊聊车载场景下网络请求的那些事。我会结合我这些年做车载项目的经验,把 Retrofit + OkHttp 这套组合拳讲透,顺便聊聊 RESTful API 怎么设计才靠谱,以及离线缓存到底该怎么搞。
核心要点:车载网络请求不是简单的「发请求、收响应」。它要应对弱网、断网、高延迟、低功耗等极端场景。选对工具、设计好架构,才能让应用在各种网络条件下都「稳如老狗」。
19.1 为什么是 Retrofit + OkHttp?
Android 原生网络请求,有 HttpURLConnection、Volley、AsyncHttpClient 等等。但我个人习惯,只要不是写 Demo,一律用 Retrofit + OkHttp。为什么?
- Retrofit:把 HTTP API 声明成 Java 接口,用注解定义请求方式、参数、路径。代码干净,可读性强。
- OkHttp:底层网络库,负责连接、复用、超时、拦截器。性能强悍,支持 HTTP/2、WebSocket。
- 两者结合:Retrofit 依赖 OkHttp 做实际传输,OkHttp 提供拦截器机制,方便我们做日志、缓存、重试等操作。
我在项目中遇到过一个问题:某次 OTA 升级时,大量车辆同时请求服务器,导致连接池耗尽。后来用 OkHttp 的 ConnectionPool 配置了合适的最大空闲连接数和存活时间,问题就解决了。嗯,细节决定成败。
19.2 快速集成与基础用法
先加依赖。在 build.gradle 里加上:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
然后定义一个 API 接口:
public interface CarApi {
@GET("api/vehicle/status")
Call<VehicleStatus> getVehicleStatus(@Query("vin") String vin);
@POST("api/ota/check")
Call<OtaResponse> checkOtaUpdate(@Body OtaRequest request);
}
创建 Retrofit 实例:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.yourcar.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
CarApi api = retrofit.create(CarApi.class);
调用接口:
api.getVehicleStatus("LSVA1234567890").enqueue(new Callback<VehicleStatus>() {
@Override
public void onResponse(Call<VehicleStatus> call, Response<VehicleStatus> response) {
// 处理成功响应
}
@Override
public void onFailure(Call<VehicleStatus> call, Throwable t) {
// 处理网络错误
}
});
小技巧:车载环境下,网络延迟波动大。建议把 connectTimeout 设短一点(5-10秒),readTimeout 设长一点(15-30秒)。这样能快速发现连接失败,同时给慢速响应留足时间。
19.3 RESTful API 设计:车载场景下的最佳实践
RESTful API 设计,说白了就是「用 HTTP 方法表达资源操作」。但在车载场景下,有几个点需要特别注意。
19.3.1 资源命名与版本控制
我建议用名词复数表示资源,比如 /api/vehicles、/api/ota/packages。版本号放在 URL 里,比如 /api/v2/vehicles。为什么?因为车载系统升级周期长,不同版本的车机可能共存,API 版本控制能避免兼容性问题。
19.3.2 状态码与错误处理
不要只返回 200 和 500。我见过一个项目,所有错误都返回 200,然后在 body 里塞一个 errorCode。这种做法,说实话,很坑。调试时根本分不清是网络问题还是业务问题。
正确的做法:
- 200:成功
- 400:参数错误
- 401:鉴权失败
- 404:资源不存在
- 429:请求太频繁(限流)
- 500:服务器内部错误
19.3.3 分页与批量操作
车载日志上传、轨迹查询等场景,数据量可能很大。用分页参数 ?page=1&size=20 来控制。响应里带上 total、hasMore 字段,方便客户端判断是否继续请求。
注意:车载网络流量通常比较贵(尤其是通过 T-Box 走蜂窝网络)。不要频繁轮询,尽量用长连接或 WebSocket 推送。如果必须轮询,间隔建议不少于 30 秒。
19.4 离线缓存:让应用在无网时也能用
车载场景下,离线缓存不是「锦上添花」,而是「刚需」。你想想看,车辆进入隧道、地下车库,网络瞬间断开。如果应用直接崩掉或白屏,用户会怎么想?
OkHttp 内置了缓存机制,配合 Retrofit 可以轻松实现离线缓存。
19.4.1 配置 OkHttp 缓存
File cacheFile = new File(context.getCacheDir(), "okhttp_cache");
Cache cache = new Cache(cacheFile, 10 * 1024 * 1024); // 10MB 缓存空间
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(new CacheInterceptor()) // 自定义缓存拦截器
.build();
19.4.2 自定义缓存拦截器
我曾经踩过一个坑:默认缓存策略在弱网下会返回过期的缓存数据。后来我写了一个拦截器,根据网络状态动态调整缓存策略。
public class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 无网络时,强制使用缓存
if (!NetworkUtil.isConnected(context)) {
request = request.newBuilder()
.header("Cache-Control", "only-if-cached, max-stale=" + 60 * 60 * 24 * 7) // 7天内的缓存
.build();
}
Response response = chain.proceed(request);
// 有网络时,设置缓存有效期
if (NetworkUtil.isConnected(context)) {
response = response.newBuilder()
.header("Cache-Control", "public, max-age=" + 60 * 10) // 10分钟缓存
.removeHeader("Pragma")
.build();
}
return response;
}
}
19.4.3 缓存策略选择
| 场景 | 缓存策略 | 说明 |
|---|---|---|
| 车辆状态(速度、电量) | 不缓存或短缓存(30秒) | 实时性要求高 |
| 地图数据、POI 列表 | 长缓存(1天-7天) | 数据变化慢,离线可用 |
| OTA 升级包信息 | 缓存到下次请求 | 避免重复下载 |
| 用户设置、偏好 | 本地持久化(Room/SharedPreferences) | 不依赖网络 |
避坑指南:我曾经在某个项目中,把所有 API 响应都缓存了 24 小时。结果用户改了车辆昵称,过了两天才刷新出来。后来我学乖了:写操作(POST/PUT/DELETE)不要缓存,读操作(GET)根据数据特性设置不同的缓存时间。
19.5 知识体系总览
下面这张图,是我梳理的本章知识体系。你可以把它当作一个「导航图」,方便回顾。
19.6 写在最后
网络请求这块,说难不难,说简单也不简单。关键是你要理解车载场景的特殊性:网络不稳定、流量贵、实时性要求高。选对工具(Retrofit + OkHttp),设计好 API,再加上合理的离线缓存策略,你的应用就能在各种网络条件下「游刃有余」。
我个人习惯,每次写完网络层,都会做一次「弱网测试」:用 Charles 或 Fiddler 模拟 3G、2G、甚至断网场景,看看应用表现如何。嗯,这个习惯帮我避免了好几次线上事故。