添加注释,修改Okhttp单例和多例实现

old-package
lensfrex 2 years ago
parent db1121f6a3
commit e03fb17395
Signed by: lensfrex
GPG Key ID: 0F69A0A2FBEE98A0
  1. 208
      mywust-network-okhttp/src/main/java/cn/linghang/mywust/network/okhttp/SimpleOkhttpRequester.java
  2. 56
      mywust-network/src/main/java/cn/linghang/mywust/network/Requester.java

@ -21,30 +21,89 @@ import java.util.concurrent.TimeUnit;
* <p>使用Okhttp的Requester简单的实现了mywust的需求能应付普通的使用</p> * <p>使用Okhttp的Requester简单的实现了mywust的需求能应付普通的使用</p>
* <p>可以设置OkhttpClient是否使用单例模式以节省部分内存和对象分配的消耗默认不使用多例模式</p> * <p>可以设置OkhttpClient是否使用单例模式以节省部分内存和对象分配的消耗默认不使用多例模式</p>
* <p>按照Okhttp官方的说法以及其他人的测试使用多例Okhttp对性能消耗其实并不是很大可以忽略在多例模式下性能可能也比httpclient要好</p> * <p>按照Okhttp官方的说法以及其他人的测试使用多例Okhttp对性能消耗其实并不是很大可以忽略在多例模式下性能可能也比httpclient要好</p>
* <p>但是如果每次请求的代理或者超时等设置有动态需求的话只能使用多例模式单例模式下OkhttpClient的设置是全局一致的</p> * <p>但是如果每次请求的代理或者超时等设置有动态需求的话<b>只能使用多例模式</b>因为单例模式下OkhttpClient的设置是全局一致的无法定制选项</p>
* <p>详见<a href="https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/">官方OkHttpClient文档</a></p>
* <p>StackOverflow上也有人讨论过<a href="https://stackoverflow.com/questions/42955571/how-to-set-proxy-for-each-request-in-okhttp">How to set proxy for each request in OkHttp?</a></p>
* <p>注意这里的多例并不是每次都new一个builder来build这种情况每个client都有一个独立的连接池和线程多了会导致oom因此如果想要多例应该是rootClient.newBuild()而不是new Builder().build()</p>
* <p>总之看文档就知道了</p>
* <br> * <br>
* <p>如果有更加高级的需求实现也可以自己实现Requester接口</p> * <p>如果有更加高级的需求实现也可以自己实现Requester接口</p>
* *
* @author lensfrex * @author lensfrex
* @create 2022-10-15 09:49 * @create 2022-10-15 09:49
* @edit 2022-10-15 09:49 * @edit 2022-10-19 21:30
*/ */
public class SimpleOkhttpRequester implements Requester { public class SimpleOkhttpRequester implements Requester {
private final boolean singletonClient; private final boolean useSingletonClient;
private static volatile OkHttpClient rootClient;
/**
* 默认的多例模式的SimpleOkhttpRequester构造方法
*/
public SimpleOkhttpRequester() { public SimpleOkhttpRequester() {
singletonClient = false; this(new OkHttpClient(), false);
} }
public SimpleOkhttpRequester(boolean singletonClient) { /**
this.singletonClient = singletonClient; * 指定是否使用单例模式的SimpleOkhttpRequester构造方法
*
* @param useSingletonClient 是否使用单例模式
*/
public SimpleOkhttpRequester(boolean useSingletonClient) {
this(new OkHttpClient(), useSingletonClient);
} }
private static volatile OkHttpClient singletonOkhttp; /**
* 使用特定okhttpClient对象并且指定是否使用单例模式的SimpleOkhttpRequester私有构造方法
*
* @param okHttpClient okhttpClient对象
* @param useSingletonClient 是否使用单例模式
*/
private SimpleOkhttpRequester(OkHttpClient okHttpClient, boolean useSingletonClient) {
this.useSingletonClient = useSingletonClient;
this.setRootClient(okHttpClient);
}
private static OkHttpClient newOkhttpClient(RequestClientOption requestClientOption) { /**
OkHttpClient.Builder builder = new OkHttpClient.Builder() * 指定client参数选项的SimpleOkhttpRequester构造方法默认不使用单例模式
.callTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS) *
* @param requestClientOption client参数选项
*/
public SimpleOkhttpRequester(RequestClientOption requestClientOption) {
this(requestClientOption, false);
}
/**
* 指定client参数选项的SimpleOkhttpRequester构造方法并且指定是否使用单例模式
*
* @param requestClientOption client参数选项
* @param useSingletonClient 是否使用单例模式
*/
public SimpleOkhttpRequester(RequestClientOption requestClientOption, boolean useSingletonClient) {
this.useSingletonClient = useSingletonClient;
this.setRootClient(buildClient(new OkHttpClient.Builder(), requestClientOption));
}
private void setRootClient(OkHttpClient okHttpClient) {
if (rootClient == null) {
synchronized (SimpleOkhttpRequester.class) {
if (rootClient == null) {
rootClient = okHttpClient;
}
}
}
}
/**
* 构建一个okhttpClient
*
* @param builder okhttpClient.Builder
* @param requestClientOption client参数选项
* @return 构建好的client
*/
private OkHttpClient buildClient(OkHttpClient.Builder builder, RequestClientOption requestClientOption) {
builder.callTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS)
.readTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS) .readTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS)
.connectTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS); .connectTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS);
@ -60,34 +119,63 @@ public class SimpleOkhttpRequester implements Requester {
return builder.build(); return builder.build();
} }
private static OkHttpClient getSingletonClientInstant(RequestClientOption options) { /**
if (singletonOkhttp == null) { * 获取client若为单例模式直接返回rootClient
synchronized (SimpleOkhttpRequester.class) { *
if (singletonOkhttp == null) { * @param requestClientOption client参数选项
singletonOkhttp = newOkhttpClient(options); * @return okhttpClient
} */
} private OkHttpClient getOkhttpClient(RequestClientOption requestClientOption) {
} // 单例模式直接使用root client
if (useSingletonClient) {
return singletonOkhttp; return rootClient;
} }
private OkHttpClient getOkhttpClient(RequestClientOption options) { return buildClient(rootClient.newBuilder(), requestClientOption);
if (singletonClient) {
return getSingletonClientInstant(options);
} else {
return newOkhttpClient(options);
}
} }
/**
* json的content-type
*/
private static final String JSON_CONTENT_TYPE = "application/json; charset=utf-8"; private static final String JSON_CONTENT_TYPE = "application/json; charset=utf-8";
/**
* 默认的content-typeform
*/
private static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"; private static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded";
public enum RequestMethod { /**
GET, POST * 请求方法
*/
private enum RequestMethod {
GET, POST, PUT, DELETE
}
/**
* 根据请求header判断并获取MediaType
*
* @param httpRequest 请求
* @return MediaType
*/
private MediaType getMediaType(HttpRequest httpRequest) {
// 按照指定的Content-Type设置MediaType,不指定的话按form处理
String contentType = httpRequest.getHeaders().get("Content-Type");
MediaType mediaType;
if (!"".equals(contentType)) {
return MediaType.get(contentType);
} else {
return MediaType.get(DEFAULT_CONTENT_TYPE);
}
} }
/**
* 根据请求信息来构建Request
*
* @param requestMethod 请求方法可选值GET, POST, PUT, DELETE
* @param url 地址
* @param httpRequest 请求体
* @return Request
*/
private Request buildRequest(RequestMethod requestMethod, URL url, HttpRequest httpRequest) { private Request buildRequest(RequestMethod requestMethod, URL url, HttpRequest httpRequest) {
Request.Builder requestBuilder = new Request.Builder().url(url); Request.Builder requestBuilder = new Request.Builder().url(url);
@ -98,22 +186,32 @@ public class SimpleOkhttpRequester implements Requester {
if (requestMethod == RequestMethod.GET) { if (requestMethod == RequestMethod.GET) {
requestBuilder.get(); requestBuilder.get();
} else if (requestMethod == RequestMethod.POST) { } else if (requestMethod == RequestMethod.POST) {
// 按照指定的Content-Type设置MediaType,不指定的话按form处理 requestBuilder.post(RequestBody.create(this.getMediaType(httpRequest), httpRequest.getData()));
String contentType = httpRequest.getHeaders().get("Content-Type");
MediaType mediaType; } else if (requestMethod == RequestMethod.PUT) {
if (!"".equals(contentType)) { requestBuilder.put(RequestBody.create(this.getMediaType(httpRequest), httpRequest.getData()));
mediaType = MediaType.get(contentType);
} else if (requestMethod == RequestMethod.DELETE) {
if (httpRequest.getData() != null) {
requestBuilder.delete(RequestBody.create(this.getMediaType(httpRequest), httpRequest.getData()));
} else { } else {
mediaType = MediaType.get(DEFAULT_CONTENT_TYPE); requestBuilder.delete();
} }
requestBuilder.post(RequestBody.create(mediaType, httpRequest.getData()));
} }
return requestBuilder.build(); return requestBuilder.build();
} }
/**
* 发送请求并获取响应包装成HttpResponse
*
* @param client okhttpClient
* @param request 请求
* @return 包装好的Http响应
* @throws IOException 如果网络请求有异常
*/
private HttpResponse sendRequest(OkHttpClient client, Request request) throws IOException { private HttpResponse sendRequest(OkHttpClient client, Request request) throws IOException {
try (Response response = client.newCall(request).execute()) { try (Response response = client.newCall(request).execute()) {
HttpResponse httpResponse = new HttpResponse(); HttpResponse httpResponse = new HttpResponse();
@ -126,19 +224,29 @@ public class SimpleOkhttpRequester implements Requester {
ResponseBody body = response.body(); ResponseBody body = response.body();
if (body != null) { if (body != null) {
// 如果响应太大的话会导致oom,但其实大部分的请求都不会oom,因此直接使用bytes() // 如果响应太大的话会导致oom,但其实大部分的请求响应都不会大到导致oom,因此直接使用bytes()
// 如果请求响应特别大的话请用ByteStream // 如果请求响应特别大的话请用ByteStream,或者自造轮子
httpResponse.setBody(body.bytes()); httpResponse.setBody(body.bytes());
} }
httpResponse.setCookies(response.header("Set-Cookie")); httpResponse.setCookies(response.header("Set-Cookie"));
}
return null; return httpResponse;
}
} }
/**
* 执行请求
*
* @param requestMethod 请求方法
* @param url 请求地址
* @param httpRequest 请求体
* @param requestClientOption 请求响应
* @return 包装好的Http响应
* @throws IOException 如果网络请求有异常
*/
private HttpResponse doRequest(RequestMethod requestMethod, URL url, HttpRequest httpRequest, RequestClientOption requestClientOption) throws IOException { private HttpResponse doRequest(RequestMethod requestMethod, URL url, HttpRequest httpRequest, RequestClientOption requestClientOption) throws IOException {
OkHttpClient client = this.getOkhttpClient(requestClientOption); OkHttpClient client = getOkhttpClient(requestClientOption);
Request request = this.buildRequest(requestMethod, url, httpRequest); Request request = this.buildRequest(requestMethod, url, httpRequest);
return this.sendRequest(client, request); return this.sendRequest(client, request);
@ -150,16 +258,26 @@ public class SimpleOkhttpRequester implements Requester {
} }
@Override @Override
public HttpResponse post(URL url, HttpRequest request, RequestClientOption options) throws IOException { public HttpResponse post(URL url, HttpRequest request, RequestClientOption requestClientOption) throws IOException {
return this.doRequest(RequestMethod.POST, url, request, options); return this.doRequest(RequestMethod.POST, url, request, requestClientOption);
} }
@Override @Override
public HttpResponse postJson(URL url, HttpRequest request, Object requestBody, RequestClientOption options) throws IOException { public HttpResponse postJson(URL url, HttpRequest request, Object requestBody, RequestClientOption requestClientOption) throws IOException {
String json = new ObjectMapper().writeValueAsString(requestBody); String json = new ObjectMapper().writeValueAsString(requestBody);
request.setData(json.getBytes(StandardCharsets.UTF_8)); request.setData(json.getBytes(StandardCharsets.UTF_8));
request.getHeaders().put("Content-Type", JSON_CONTENT_TYPE); request.getHeaders().put("Content-Type", JSON_CONTENT_TYPE);
return this.doRequest(RequestMethod.POST, url, request, options); return this.doRequest(RequestMethod.POST, url, request, requestClientOption);
}
@Override
public HttpResponse put(URL url, HttpRequest request, RequestClientOption requestClientOption) throws IOException {
return this.doRequest(RequestMethod.PUT, url, request, requestClientOption);
}
@Override
public HttpResponse delete(URL url, HttpRequest request, RequestClientOption requestClientOption) throws IOException {
return this.doRequest(RequestMethod.DELETE, url, request, requestClientOption);
} }
} }

@ -1,5 +1,6 @@
package cn.linghang.mywust.network; package cn.linghang.mywust.network;
import java.io.IOException;
import java.net.URL; import java.net.URL;
/** /**
@ -14,9 +15,58 @@ import java.net.URL;
*/ */
public interface Requester { public interface Requester {
HttpResponse get(URL url, HttpRequest request, RequestClientOption requestClientOption) throws Exception; /**
* 发送Get请求
*
* @param url 请求地址
* @param request 请求体
* @param requestClientOption 请求选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse get(URL url, HttpRequest request, RequestClientOption requestClientOption) throws IOException;
HttpResponse post(URL url, HttpRequest request, RequestClientOption options) throws Exception; /**
* 发送Post请求
*
* @param url 请求地址
* @param request 请求体
* @param requestClientOption 请求选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse post(URL url, HttpRequest request, RequestClientOption requestClientOption) throws IOException;
HttpResponse postJson(URL url, HttpRequest request, Object requestBody, RequestClientOption options) throws Exception; /**
* 发送Post请求并将对象自动封装成json
*
* @param url 请求地址
* @param request 请求体
* @param requestClientOption 请求选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse postJson(URL url, HttpRequest request, Object requestBody, RequestClientOption requestClientOption) throws IOException;
/**
* 发送Put请求
*
* @param url 请求地址
* @param request 请求体
* @param requestClientOption 请求选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse put(URL url, HttpRequest request, RequestClientOption requestClientOption) throws IOException;
/**
* 发送Delete请求
*
* @param url 请求地址
* @param request 请求体
* @param requestClientOption 请求选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse delete(URL url, HttpRequest request, RequestClientOption requestClientOption) throws IOException;
} }

Loading…
Cancel
Save