OkHttpClient
是一个由 Square 公司开发的 HTTP 客户端库,用于在 Android 和 Java 应用中进行网络请求。它支持同步和异步请求、连接池、超时设置、拦截器等功能,适合用于高性能网络请求,特别是在需要处理复杂的网络操作时。
一、OKHttpClient介绍
主要特点
-
同步和异步请求:
-
同步请求会在当前线程等待响应,适合不需要并发的简单请求。
-
异步请求会将网络操作交由后台线程处理,不会阻塞主线程,适合需要并发处理或在 Android 等环境中使用。
-
-
连接池: OkHttp 默认会使用连接池来复用 HTTP 连接,从而提高性能,减少连接的建立和关闭的开销。
-
拦截器 (Interceptor): 拦截器允许在请求和响应时进行操作,例如可以在请求发送前添加认证信息,或在响应到达后进行日志记录。
-
自动处理 HTTP/2 和 SPDY: OkHttp 默认支持 HTTP/2 协议,可以提升多路复用性能,使多个请求共享一个 TCP 连接。
-
缓存机制: OkHttp 提供了默认的缓存机制,可以根据 HTTP 响应头自动缓存请求结果,减少重复网络请求。
-
超时控制: 可以对连接、读取和写入操作分别设置超时,避免长时间无响应的请求卡住应用。
二、在实际项目中的应用
(1)引入maven配置
<!-- ok的Http连接池 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
(2)自定义HttpUtils工具类
import com.alibaba.fastjson.JSONObject;
import com.yan.project.httpUtils.okHttp2.HttpRequestBody;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Slf4j
public class OkHttpUtils {
private OkHttpUtils() {
throw new IllegalArgumentException("Utility class");
}
private static final String MIME_JSON = "application/json; charset=utf-8";
private static OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(3, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.addInterceptor((Interceptor.Chain chain) -> {
Request req = chain.request();
try {
Response res = chain.proceed(req);
return res;
} catch (Exception ex) {
throw ex;
}
})
.build();
// GET-无参
public static String getJson(String url) throws IOException {
return basicGet(url, null);
}
// GET-有参
public static String get(String url, Map<String, Object> paramMap) throws Exception {
StringBuilder sb = new StringBuilder(url);
if (paramMap != null && !paramMap.isEmpty()) {
Set<String> keySet = paramMap.keySet();
for (String key : keySet) {
sb.append(sb.toString().contains("?") ? "&" : "?");
sb.append(key).append("=").append(paramMap.get(key));
}
url = sb.toString();
}
return basicGet(url, null);
}
// GET-有参、有请求头
public static String getWithHeades(String url, Map<String, Object> paramMap, Map<String, String> headers) throws Exception {
StringBuilder sb = new StringBuilder(url);
if (paramMap != null && !paramMap.isEmpty()) {
Set<String> keySet = paramMap.keySet();
for (String key : keySet) {
sb.append(sb.toString().contains("?") ? "&" : "?");
sb.append(key).append("=").append(paramMap.get(key));
}
url = sb.toString();
}
return basicGet(url, headers);
}
// GET-json
public static String getJsonWithHeader(String url, Map<String, String> headerMap) throws IOException {
Request.Builder request = new Request.Builder().url(url).get().header("Accept", MIME_JSON);
if (headerMap != null) {
headerMap.forEach(request::header);
}
try (Response response = httpClient.newCall(request.build()).execute()) {
log.info("response_code:{}, response_body:{}", response.code(), response.body().string());
return response.body().string();
}
}
// POST-json传参
public static String postJsonWithHeader(String url, Object reqBody, Map<String, String> headers) throws IOException {
Request.Builder request = new Request.Builder()
.url(url)
.header("Accept", MIME_JSON)
.post(FormBody.create(MediaType.parse(MIME_JSON), JSONObject.toJSONString(reqBody)));
// 遍历并添加 header
if (headers != null) {
headers.forEach(request::header);
}
try (Response response = httpClient.newCall(request.build()).execute()) {
log.info("response_code:{}, response_body:{}", response.code(), response.body().string());
return response.body().string();
}
}
// POST-param传参
public static String postParamWithHeader(String url, Map<String, Object> paramMap, Map<String, String> headers) throws IOException {
FormBody.Builder formBody = new FormBody.Builder();
if (Objects.nonNull(paramMap)) {
paramMap.forEach((k, v) -> formBody.add(k, (String) v));
}
Request.Builder request = new Request.Builder()
.url(url)
.header("Accept", MIME_JSON)
.post(formBody.build());
// 遍历并添加 header
if (headers != null) {
headers.forEach(request::header);
}
try (Response response = httpClient.newCall(request.build()).execute()) {
log.info("response_code:{}, response_body:{}", response.code(), response.body().string());
return response.body().string();
}
}
public static final String octet_stream_str = "application/octet-stream";
public static final MediaType octet_stream = MediaType.parse(octet_stream_str);
public static void putStream(String url, InputStream inputStream, long contentLength) {
Response response;
try {
RequestBody requestBody2 = new HttpRequestBody(inputStream, contentLength);
Request request = new Request.Builder().url(url)
.addHeader("Content-Type", octet_stream_str)
.put(requestBody2)
.build();
response = httpClient.newCall(request).execute();
String result = response.body().string();
if (response.code() != 200) {
throw new Exception("Saturn SDK stream upload failed, url:" + url + "response.code():" + response.code() + ", msg:" + result);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeQuietly(inputStream);
}
}
public static String putFile(String url, File file) throws Exception {
RequestBody requestBody = RequestBody.create(octet_stream, file);
Request request = new Request.Builder().url(url)
.addHeader("Content-Type", octet_stream_str)
.put(requestBody).build();
Response response = httpClient.newCall(request).execute();
String result = response.body().string();
int code = response.code();
log.info("putFile end, url:{}, response.code:{}, result:{}", url, code, result);
if (code != 200) {
throw new Exception("putFile failed, response.code():" + code + ", result:" + result);
}
return result;
}
private static String basicGet(String url, Map<String, String> headers) throws IOException {
Request.Builder builder = new Request.Builder()
.url(url)
.get();
// 遍历并添加 header
if (headers != null) {
headers.forEach(builder::header);
}
try (Response response = httpClient.newCall(builder.build()).execute()) {
return response.body().string();
}
}
private static String basicDelete(String url, Headers headers) throws IOException {
Request.Builder builder = new Request.Builder().url(url).delete();
if (headers != null) {
builder.headers(headers);
}
try (Response response = httpClient.newCall(builder.build()).execute()) {
return response.body().string();
}
}
public static void closeQuietly(Closeable is) {
if (is != null) {
try {
is.close();
} catch (Exception ex) {
log.error("Resources encounter an exception when closing,ex:{}", ex.getMessage());
}
}
}
}
这个
OkHttpUtils
类封装了OkHttpClient
来进行 HTTP 请求,支持 GET、POST、PUT 等常见的 HTTP 方法,并提供了对参数、请求头、文件上传等功能的支持。以下是它的主要功能和使用方法的解释:
OkHttpClient 实例
使用
OkHttpClient.Builder()
创建,设置了超时时间(连接超时 3 秒,读写超时 5 秒),并添加了拦截器。静态方法
getJson
,get
,getWithHeades
,getJsonWithHeader
:用于 GET 请求,支持无参、有参数和请求头的场景。
postJsonWithHeader
,postParamWithHeader
:用于 POST 请求,支持 JSON 数据或表单数据传递。
putStream
,putFile
:用于 PUT 请求,支持流和文件上传。
basicGet
,basicDelete
:内部通用方法,分别处理 GET 和 DELETE 请求。
三、拓展和使用建议
- 增强错误处理:当前仅在拦截器中简单处理了异常,可以考虑在各个方法中增加详细的异常处理机制。
- 连接池管理:默认情况下,OkHttp 使用连接池来提升性能,类内部也可以进一步定制连接池策略来优化并发性能。
- 异步支持:所有请求均为同步请求,适合使用时可以考虑用 OkHttp 提供的
enqueue()
方法进行异步操作,防止阻塞主线程。
通过这些封装,OkHttpUtils
能够方便地发送 HTTP 请求并处理响应。在实际项目中,你可以根据需要调整超时设置、缓存机制等配置。