在现代移动应用和后端服务开发领域,OkHttp作为Square公司开源的HTTP客户端库,已经成为Java和Android平台上处理网络请求的事实标准。这个高效、可靠的HTTP客户端通过其简洁的API设计和强大的功能特性,彻底简化了网络通信的复杂性。在电商应用中,OkHttp处理着商品图片的异步加载;在社交媒体平台中,它支撑着用户动态的实时更新;在金融应用中,它确保着交易请求的安全传输。从简单的GET请求到复杂的多部分文件上传,从HTTP/2支持到连接池优化,从自动重试机制到拦截器链设计,OkHttp都在为现代应用的网络通信提供着稳定可靠的基础设施。
核心架构与设计理念
1. 异步请求与连接池管理
OkHttp的核心优势在于其高效的异步处理机制和智能的连接池管理。
java
// 基础OkHttp客户端配置
public class OkHttpClientFactory {
private static volatile OkHttpClient instance;
public static OkHttpClient getInstance() {
if (instance == null) {
synchronized (OkHttpClientFactory.class) {
if (instance == null) {
instance = createDefaultClient();
}
}
}
return instance;
}
private static OkHttpClient createDefaultClient() {
return new OkHttpClient.Builder()
// 连接池配置
.connectionPool(new ConnectionPool(
5, // 最大空闲连接数
5, // 保活时间(分钟)
TimeUnit.MINUTES))
// 超时配置
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.callTimeout(60, TimeUnit.SECONDS) // 整个调用超时
// 重试和重定向
.retryOnConnectionFailure(true)
.followRedirects(true)
.followSslRedirects(true)
// 缓存配置
.cache(new Cache(
new File("/tmp/okhttp-cache"),
50 * 1024 * 1024)) // 50MB缓存
// 拦截器
.addInterceptor(new LoggingInterceptor())
.addInterceptor(new RetryInterceptor(3))
.addNetworkInterceptor(new StethoInterceptor())
// 事件监听器
.eventListenerFactory(EventListener.Factory)
// DNS配置
.dns(new Dns() {
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
// 自定义DNS解析,可以添加缓存或备用DNS
List<InetAddress> addresses = SYSTEM.lookup(hostname);
if (addresses.isEmpty()) {
// 使用备用DNS
return BACKUP_DNS.lookup(hostname);
}
return addresses;
}
})
// 代理配置
.proxySelector(new ProxySelector() {
@Override
public List<Proxy> select(URI uri) {
// 根据URI选择代理
return PROXY_CONFIG.getProxies(uri);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// 连接失败处理
PROXY_CONFIG.markFailed(sa);
}
})
// SSL/TLS配置
.sslSocketFactory(createSSLSocketFactory(), createTrustManager())
.hostnameVerifier((hostname, session) -> {
// 主机名验证
return HOSTNAME_VERIFIER.verify(hostname, session);
})
// 证书锁定
.certificatePinner(new CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build())
// Cookie管理
.cookieJar(new PersistentCookieJar())
.build();
}
// 创建支持所有TLS版本的SSLSocketFactory
private static SSLSocketFactory createSSLSocketFactory() throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
return sslContext.getSocketFactory();
}
private static X509TrustManager createTrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
}
// 基础请求执行
public class HttpRequestExecutor {
private final OkHttpClient client;
public HttpRequestExecutor(OkHttpClient client) {
this.client = client;
}
// 同步GET请求
public String syncGet(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.get()
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败: " + response.code());
}
ResponseBody body = response.body();
return body != null ? body.string() : null;
}
}
// 异步GET请求
public void asyncGet(String url, Callback callback) {
Request request = new Request.Builder()
.url(url)
.get()
.build();
client.newCall(request).enqueue(callback);
}
// 带参数的POST请求
public String postForm(String url, Map<String, String> params) throws IOException {
FormBody.Builder builder = new FormBody.Builder();
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
Request request = new Request.Builder()
.url(url)
.post(builder.build())
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败: " + response.code());
}
ResponseBody body = response.body();
return body != null ? body.string() : null;
}
}
// JSON POST请求
public String postJson(String url, String json) throws IOException {
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(json, JSON))
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败: " + response.code());
}
ResponseBody body = response.body();
return body != null ? body.string() : null;
}
}
// 多部分文件上传
public String uploadFile(String url, File file, Map<String, String> params) throws IOException {
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM);
// 添加文件
builder.addFormDataPart("file", file.getName(),
RequestBody.create(file, MediaType.parse("application/octet-stream")));
// 添加其他参数
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.addFormDataPart(entry.getKey(), entry.getValue());
}
Request request = new Request.Builder()
.url(url)
.post(builder.build())
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("上传失败: " + response.code());
}
ResponseBody body = response.body();
return body != null ? body.string() : null;
}
}
// 下载文件
public void downloadFile(String url, File destination, ProgressListener listener) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("下载失败: " + response.code());
}
ResponseBody body = response.body();
if (body == null) {
throw new IOException("响应体为空");
}
long contentLength = body.contentLength();
InputStream inputStream = body.byteStream();
try (FileOutputStream outputStream = new FileOutputStream(destination)) {
byte[] buffer = new byte[4096];
long totalRead = 0;
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalRead += bytesRead;
if (listener != null && contentLength > 0) {
int progress = (int) ((totalRead * 100) / contentLength);
listener.onProgress(progress);
}
}
}
}
}
}
2. 拦截器与自定义功能扩展
java
// 自定义日志拦截器
public class CustomLoggingInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(CustomLoggingInterceptor.class);
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
// 记录请求信息
logRequest(request);
Response response;
try {
response = chain.proceed(request);
} catch (IOException e) {
logger.error("请求失败: {} {}", request.method(), request.url(), e);
throw e;
}
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1_000_000; // 毫秒
// 记录响应信息
logResponse(response, duration);
return response;
}
private void logRequest(Request request) {
HttpUrl url = request.url();
String method = request.method();
Headers headers = request.headers();
StringBuilder log = new StringBuilder();
log.append("--> ").append(method).append(" ").append(url).append("\n");
// 记录请求头
for (int i = 0; i < headers.size(); i++) {
log.append(headers.name(i)).append(": ").append(headers.value(i)).append("\n");
}
// 记录请求体(如果是可读的)
RequestBody body = request.body();
if (body != null) {
if (body.contentType() != null &&
body.contentType().toString().contains("application/json")) {
Buffer buffer = new Buffer();
try {
body.writeTo(buffer);
String bodyString = buffer.readUtf8();
log.append("\n").append(bodyString).append("\n");
} catch (IOException e) {
log.append("\n[无法读取请求体]").append("\n");
}
}
}
log.append("--> END ").append(method);
logger.debug(log.toString());
}
private void logResponse(Response response, long duration) {
String method = response.request().method();
int code = response.code();
String message = response.message();
StringBuilder log = new StringBuilder();
log.append("<-- ").append(code).append(" ").append(message)
.append(" ").append(response.request().url())
.append(" (").append(duration).append("ms)").append("\n");
// 记录响应头
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
log.append(headers.name(i)).append(": ").append(headers.value(i)).append("\n");
}
// 记录响应体
ResponseBody body = response.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null && contentType.toString().contains("application/json")) {
try {
String bodyString = body.string();
log.append("\n").append(bodyString).append("\n");
// 重新创建ResponseBody,因为string()方法会消耗流
response = response.newBuilder()
.body(ResponseBody.create(bodyString, contentType))
.build();
} catch (IOException e) {
log.append("\n[无法读取响应体]").append("\n");
}
}
}
log.append("<-- END HTTP");
logger.debug(log.toString());
}
}
// 重试拦截器
public class RetryInterceptor implements Interceptor {
private final int maxRetries;
private final long retryDelayMillis;
public RetryInterceptor(int maxRetries) {
this(maxRetries, 1000);
}
public RetryInterceptor(int maxRetries, long retryDelayMillis) {
this.maxRetries = maxRetries;
this.retryDelayMillis = retryDelayMillis;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
IOException exception = null;
for (int retryCount = 0; retryCount <= maxRetries; retryCount++) {
try {
if (retryCount > 0) {
// 添加重试延迟
Thread.sleep(calculateBackoff(retryCount, retryDelayMillis));
// 重新创建请求(某些请求体不能重复使用)
request = recreateRequest(request);
}
response = chain.proceed(request);
// 检查是否需要重试
if (!shouldRetry(response, retryCount)) {
return response;
}
// 关闭响应体
if (response.body() != null) {
response.body().close();
}
} catch (IOException e) {
exception = e;
// 检查是否应该重试
if (!shouldRetry(e, retryCount)) {
throw e;
}
logger.warn("请求失败,准备重试 ({}): {}", retryCount + 1, e.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("重试被中断", e);
}
}
// 所有重试都失败
if (exception != null) {
throw exception;
}
throw new IOException("所有重试尝试都失败");
}
private boolean shouldRetry(Response response, int retryCount) {
// 只对特定状态码重试
int code = response.code();
return retryCount < maxRetries &&
(code == 408 || // 请求超时
code == 429 || // 太多请求
code == 500 || // 服务器内部错误
code == 502 || // 网关错误
code == 503 || // 服务不可用
code == 504); // 网关超时
}
private boolean shouldRetry(IOException exception, int retryCount) {
return retryCount < maxRetries &&
(exception instanceof SocketTimeoutException ||
exception instanceof ConnectException ||
exception instanceof SSLHandshakeException);
}
private long calculateBackoff(int retryCount, long baseDelay) {
// 指数退避算法
return (long) (baseDelay * Math.pow(2, retryCount - 1));
}
private Request recreateRequest(Request original) {
// 某些请求体不能重复使用,需要重新创建
RequestBody body = original.body();
if (body != null && !body.isOneShot()) {
return original;
}
return original.newBuilder().build();
}
}
// 认证拦截器
public class AuthInterceptor implements Interceptor {
private final AuthTokenProvider tokenProvider;
public AuthInterceptor(AuthTokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
// 获取认证令牌
String token = tokenProvider.getToken();
// 如果令牌为空,直接继续
if (token == null || token.isEmpty()) {
return chain.proceed(originalRequest);
}
// 添加认证头
Request authenticatedRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer " + token)
.build();
Response response = chain.proceed(authenticatedRequest);
// 检查令牌是否过期
if (response.code() == 401) { // 未授权
// 尝试刷新令牌
String newToken = tokenProvider.refreshToken();
if (newToken != null && !newToken.equals(token)) {
// 使用新令牌重试请求
Request newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer " + newToken)
.build();
// 关闭之前的响应体
response.close();
// 重新发送请求
return chain.proceed(newRequest);
}
}
return response;
}
}
高级特性与应用
1. WebSocket实时通信
java
// WebSocket客户端
public class WebSocketClient {
private final OkHttpClient client;
private WebSocket webSocket;
private final String url;
private WebSocketListener listener;
private volatile boolean connected = false;
public WebSocketClient(String url) {
this.url = url;
this.client = new OkHttpClient.Builder()
.pingInterval(30, TimeUnit.SECONDS) // WebSocket心跳
.build();
}
public void connect() {
if (connected) {
return;
}
Request request = new Request.Builder()
.url(url)
.build();
listener = new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
connected = true;
WebSocketClient.this.webSocket = webSocket;
logger.info("WebSocket连接已建立");
// 发送连接确认
sendMessage("{\"type\":\"connect\",\"timestamp\":" +
System.currentTimeMillis() + "}");
}
@Override
public void onMessage(WebSocket webSocket, String text) {
handleMessage(text);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
handleBinaryMessage(bytes);
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
logger.info("WebSocket关闭中: {}, {}", code, reason);
connected = false;
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
logger.info("WebSocket已关闭: {}, {}", code, reason);
connected = false;
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
logger.error("WebSocket连接失败", t);
connected = false;
// 尝试重连
scheduleReconnect();
}
};
client.newWebSocket(request, listener);
client.dispatcher().executorService().shutdown();
}
public void sendMessage(String message) {
if (webSocket != null && connected) {
webSocket.send(message);
}
}
public void sendBinaryMessage(byte[] data) {
if (webSocket != null && connected) {
webSocket.send(ByteString.of(data));
}
}
public void disconnect() {
if (webSocket != null) {
webSocket.close(1000, "正常关闭");
}
connected = false;
}
private void handleMessage(String text) {
try {
// 解析JSON消息
JsonObject json = JsonParser.parseString(text).getAsJsonObject();
String type = json.get("type").getAsString();
switch (type) {
case "chat":
handleChatMessage(json);
break;
case "notification":
handleNotification(json);
break;
case "heartbeat":
handleHeartbeat(json);
break;
default:
logger.warn("未知的消息类型: {}", type);
}
} catch (Exception e) {
logger.error("处理消息失败", e);
}
}
private void handleBinaryMessage(ByteString bytes) {
try {
// 处理二进制消息(如图片、文件等)
byte[] data = bytes.toByteArray();
// ... 处理逻辑
} catch (Exception e) {
logger.error("处理二进制消息失败", e);
}
}
private void handleChatMessage(JsonObject json) {
String from = json.get("from").getAsString();
String content = json.get("content").getAsString();
long timestamp = json.get("timestamp").getAsLong();
// 分发到UI线程
EventBus.getDefault().post(new ChatMessageEvent(from, content, timestamp));
}
private void scheduleReconnect() {
// 指数退避重连
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.schedule(this::connect, 5, TimeUnit.SECONDS);
}
}
2. 文件下载与断点续传
java
// 支持断点续传的文件下载器
public class ResumableDownloader {
private final OkHttpClient client;
private Call currentCall;
private File destinationFile;
private ProgressListener progressListener;
public ResumableDownloader() {
this.client = new OkHttpClient.Builder()
.addNetworkInterceptor(new ProgressInterceptor())
.build();
}
public void download(String url, File destination, ProgressListener listener) {
this.destinationFile = destination;
this.progressListener = listener;
// 检查是否支持断点续传
long downloadedBytes = 0;
if (destination.exists()) {
downloadedBytes = destination.length();
}
Request request = new Request.Builder()
.url(url)
.header("Range", "bytes=" + downloadedBytes + "-")
.build();
currentCall = client.newCall(request);
currentCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
if (progressListener != null) {
progressListener.onError(e);
}
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
if (progressListener != null) {
progressListener.onError(new IOException("下载失败: " + response.code()));
}
return;
}
// 检查是否支持断点续传
boolean isResumed = response.code() == 206; // Partial Content
long totalBytes = getContentLength(response);
long downloadedSoFar = destinationFile.length();
try (ResponseBody body = response.body();
InputStream input = body.byteStream();
FileOutputStream output = new FileOutputStream(destinationFile, isResumed)) {
byte[] buffer = new byte[8192];
long totalRead = downloadedSoFar;
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
totalRead += bytesRead;
if (progressListener != null && totalBytes > 0) {
int progress = (int) ((totalRead * 100) / totalBytes);
progressListener.onProgress(progress, totalRead, totalBytes);
}
}
if (progressListener != null) {
progressListener.onComplete(destinationFile);
}
} catch (IOException e) {
if (progressListener != null) {
progressListener.onError(e);
}
}
}
});
}
public void cancel() {
if (currentCall != null && !currentCall.isCanceled()) {
currentCall.cancel();
}
}
private long getContentLength(Response response) {
// 从响应头获取文件总大小
String contentRange = response.header("Content-Range");
if (contentRange != null) {
// 格式: bytes 0-999/1000
String total = contentRange.substring(contentRange.lastIndexOf('/') + 1);
try {
return Long.parseLong(total);
} catch (NumberFormatException e) {
// 忽略
}
}
// 从Content-Length获取
ResponseBody body = response.body();
return body != null ? body.contentLength() : -1;
}
// 进度拦截器
private class ProgressInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (progressListener == null) {
return originalResponse;
}
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), progressListener))
.build();
}
}
// 进度响应体
private static class ProgressResponseBody extends ResponseBody {
private final ResponseBody responseBody;
private final ProgressListener progressListener;
private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
this.responseBody = responseBody;
this.progressListener = progressListener;
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
long contentLength = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
if (contentLength == 0) {
contentLength = contentLength();
}
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
if (progressListener != null) {
progressListener.onProgress(
(int) ((totalBytesRead * 100) / contentLength),
totalBytesRead,
contentLength
);
}
return bytesRead;
}
};
}
}
public interface ProgressListener {
void onProgress(int percent, long downloaded, long total);
void onComplete(File file);
void onError(Exception e);
}
}
实战案例:电商应用网络层架构
下面通过一个完整的电商应用案例,展示OkHttp在复杂移动应用场景中的应用。
java
// 电商API客户端
public class EcommerceApiClient {
private final OkHttpClient client;
private final Gson gson;
private final String baseUrl;
private final AuthManager authManager;
private final CacheManager cacheManager;
public EcommerceApiClient(String baseUrl, Context context) {
this.baseUrl = baseUrl;
this.gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
this.authManager = new AuthManager(context);
this.cacheManager = new CacheManager(context);
this.client = createHttpClient();
}
private OkHttpClient createHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// 基础配置
builder.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true);
// 缓存配置
File cacheDir = new File(cacheManager.getCacheDir(), "http-cache");
Cache cache = new Cache(cacheDir, 50 * 1024 * 1024); // 50MB
builder.cache(cache);
// 拦截器
builder.addInterceptor(new HeaderInterceptor());
builder.addInterceptor(new AuthInterceptor(authManager));
builder.addInterceptor(new LoggingInterceptor());
builder.addNetworkInterceptor(new CacheInterceptor());
builder.addNetworkInterceptor(new StethoInterceptor()); // Facebook调试工具
// Cookie管理
builder.cookieJar(new PersistentCookieJar());
// SSL配置
configureSSL(builder);
return builder.build();
}
// 商品API
public Observable<ProductListResponse> getProducts(int page, int pageSize, String category) {
HttpUrl url = HttpUrl.parse(baseUrl + "/api/v1/products")
.newBuilder()
.addQueryParameter("page", String.valueOf(page))
.addQueryParameter("size", String.valueOf(pageSize))
.addQueryParameter("category", category)
.build();
Request request = new Request.Builder()
.url(url)
.get()
.cacheControl(new CacheControl.Builder()
.maxAge(5, TimeUnit.MINUTES) // 缓存5分钟
.build())
.build();
return executeRequest(request, ProductListResponse.class);
}
public Observable<ProductDetail> getProductDetail(long productId) {
String url = baseUrl + "/api/v1/products/" + productId;
Request request = new Request.Builder()
.url(url)
.get()
.cacheControl(new CacheControl.Builder()
.maxAge(30, TimeUnit.MINUTES) // 缓存30分钟
.build())
.build();
return executeRequest(request, ProductDetail.class);
}
public Observable<ProductDetail> searchProducts(String keyword, int page) {
HttpUrl url = HttpUrl.parse(baseUrl + "/api/v1/products/search")
.newBuilder()
.addQueryParameter("q", keyword)
.addQueryParameter("page", String.valueOf(page))
.addQueryParameter("size", "20")
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
return executeRequest(request, ProductDetail.class);
}
// 订单API
public Observable<OrderResponse> createOrder(CreateOrderRequest orderRequest) {
String url = baseUrl + "/api/v1/orders";
String json = gson.toJson(orderRequest);
RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
return executeRequest(request, OrderResponse.class);
}
public Observable<OrderListResponse> getOrders(int page, int pageSize) {
HttpUrl url = HttpUrl.parse(baseUrl + "/api/v1/orders")
.newBuilder()
.addQueryParameter("page", String.valueOf(page))
.addQueryParameter("size", String.valueOf(pageSize))
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
return executeRequest(request, OrderListResponse.class);
}
public Observable<OrderDetail> getOrderDetail(String orderId) {
String url = baseUrl + "/api/v1/orders/" + orderId;
Request request = new Request.Builder()
.url(url)
.get()
.build();
return executeRequest(request, OrderDetail.class);
}
// 用户API
public Observable<UserProfile> getUserProfile() {
String url = baseUrl + "/api/v1/users/profile";
Request request = new Request.Builder()
.url(url)
.get()
.build();
return executeRequest(request, UserProfile.class);
}
public Observable<ApiResponse> updateUserProfile(UpdateProfileRequest requestData) {
String url = baseUrl + "/api/v1/users/profile";
String json = gson.toJson(requestData);
RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(url)
.put(body)
.build();
return executeRequest(request, ApiResponse.class);
}
// 文件上传API
public Observable<UploadResponse> uploadAvatar(File imageFile) {
String url = baseUrl + "/api/v1/users/avatar";
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("avatar", imageFile.getName(),
RequestBody.create(imageFile, MediaType.parse("image/*")))
.build();
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
return executeRequest(request, UploadResponse.class);
}
// 图片加载(使用OkHttp的图片加载扩展)
public void loadImage(String imageUrl, ImageView imageView) {
// 创建图片请求
Request request = new Request.Builder()
.url(imageUrl)
.build();
// 使用OkHttp的图片加载扩展
Picasso.get()
.load(imageUrl)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.transform(new CircleTransform())
.into(imageView);
}
// 执行请求
private <T> Observable<T> executeRequest(Request request, Class<T> responseType) {
return Observable.create(emitter -> {
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
if (!emitter.isDisposed()) {
emitter.onError(new ApiException("网络请求失败", e));
}
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!emitter.isDisposed()) {
try {
if (!response.isSuccessful()) {
handleErrorResponse(response, emitter);
return;
}
ResponseBody body = response.body();
if (body == null) {
emitter.onError(new ApiException("响应体为空"));
return;
}
String json = body.string();
T result = gson.fromJson(json, responseType);
emitter.onNext(result);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(new ApiException("解析响应失败", e));
} finally {
response.close();
}
}
}
});
// 设置取消回调
emitter.setCancellable(call::cancel);
});
}
private void handleErrorResponse(Response response, ObservableEmitter<?> emitter) {
int code = response.code();
String message = "请求失败";
try {
ResponseBody body = response.body();
if (body != null) {
String errorJson = body.string();
ErrorResponse error = gson.fromJson(errorJson, ErrorResponse.class);
message = error.getMessage();
}
} catch (Exception e) {
// 忽略解析错误
}
switch (code) {
case 401:
emitter.onError(new UnauthorizedException(message));
break;
case 403:
emitter.onError(new ForbiddenException(message));
break;
case 404:
emitter.onError(new NotFoundException(message));
break;
case 429:
emitter.onError(new RateLimitException(message));
break;
case 500:
case 502:
case 503:
emitter.onError(new ServerException(message));
break;
default:
emitter.onError(new ApiException(message + " (" + code + ")"));
}
}
// 自定义拦截器
private class HeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder builder = original.newBuilder();
// 添加公共请求头
builder.header("User-Agent", "Ecommerce-App/1.0.0");
builder.header("Accept", "application/json");
builder.header("Accept-Language", Locale.getDefault().getLanguage());
builder.header("X-App-Version", BuildConfig.VERSION_NAME);
builder.header("X-Device-Id", getDeviceId());
builder.header("X-Platform", "Android");
builder.header("X-OS-Version", Build.VERSION.RELEASE);
// 添加时间戳
builder.header("X-Timestamp", String.valueOf(System.currentTimeMillis()));
return chain.proceed(builder.build());
}
}
private class AuthInterceptor implements Interceptor {
private final AuthManager authManager;
public AuthInterceptor(AuthManager authManager) {
this.authManager = authManager;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// 排除登录和注册接口
if (original.url().toString().contains("/auth/")) {
return chain.proceed(original);
}
String token = authManager.getAccessToken();
if (token == null || token.isEmpty()) {
// 重定向到登录
throw new UnauthorizedException("需要登录");
}
Request authenticated = original.newBuilder()
.header("Authorization", "Bearer " + token)
.build();
Response response = chain.proceed(authenticated);
// 检查token是否过期
if (response.code() == 401) {
// 尝试刷新token
boolean refreshed = authManager.refreshToken();
if (refreshed) {
String newToken = authManager.getAccessToken();
Request newRequest = original.newBuilder()
.header("Authorization", "Bearer " + newToken)
.build();
response.close();
return chain.proceed(newRequest);
}
}
return response;
}
}
private class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response originalResponse = chain.proceed(request);
// 根据响应头设置缓存策略
String cacheControl = originalResponse.header("Cache-Control");
if (cacheControl == null || cacheControl.contains("no-store") ||
cacheControl.contains("no-cache")) {
return originalResponse;
}
// 对于GET请求,添加缓存头
if (request.method().equals("GET")) {
return originalResponse.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, max-age=" + 60) // 60秒缓存
.build();
}
return originalResponse;
}
}
}
OkHttp的真正价值在于它将HTTP客户端从简单的请求响应工具提升到了现代网络通信框架的层次。通过其精心设计的拦截器链、智能的连接池管理和强大的功能扩展,OkHttp为移动应用和后端服务提供了稳定高效的网络通信能力。特别是在移动端应用中,OkHttp的性能优化和资源管理特性对于提升用户体验和节省用户流量具有重要意义。
然而,要充分发挥OkHttp的能力,需要深入理解其内部机制并进行合理的配置。从连接池大小到超时设置,从缓存策略到拦截器设计,每一个细节都可能影响应用的网络性能。在实际项目中,应该根据具体的业务场景和网络环境,定制适合的OkHttp配置策略。
看完这篇文章,你是否在项目中使用过OkHttp处理复杂的网络请求?或者你在OkHttp性能优化和错误处理方面有什么独特的经验?欢迎在评论区分享你的OkHttp实战心得,也欢迎提出关于移动网络编程的任何技术问题,让我们一起探讨如何更好地构建稳定、高效的网络通信体系!