文章目录
基于Okhttp3做的封装+性能优化
package dev.ksc.okhttp;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* Created by kowalski
* on 20-3-7.
* okhttp3
*/
public class OkHttpClientUtils {
private static OkHttpClient okHttpClient = null;
private static OkHttpClientUtils okHttpUtils = null;
private final OkHttpClient.Builder builder;
private OkHttpClientUtils(Context context) {
okHttpClient = getOkHttpSingletonInstance();
builder = okHttpClient.newBuilder();
//开启响应缓存
builder.cookieJar(new PersistenceCookieJar());
//设置缓存目录和大小
int cacheSize = 10 << 20; // 10 MiB空间
Cache cache = new Cache(context.getCacheDir(), cacheSize);
builder.cache(cache)
.connectTimeout(15, TimeUnit.SECONDS) //设置合理的超时
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
//以下验证不设置,那么默认就已经设置了验证
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
// 持久化Cookie,运行时缓存了Cookie,当App退出的时候Cookie就不存在了
private class PersistenceCookieJar implements CookieJar {
List<Cookie> cache = new ArrayList<>();
//Http请求结束,Response中有Cookie时候回调
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
//内存中缓存Cookie
cache.addAll(cookies);
}
//Http发送请求前回调,Request中设置Cookie
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
//过期的Cookie
List<Cookie> invalidCookies = new ArrayList<>();
//有效的Cookie
List<Cookie> validCookies = new ArrayList<>();
for (Cookie cookie : cache) {
if (cookie.expiresAt() < System.currentTimeMillis()) {
//判断是否过期
invalidCookies.add(cookie);
} else if (cookie.matches(url)) {
//匹配Cookie对应url
validCookies.add(cookie);
}
}
//缓存中移除过期的Cookie
cache.removeAll(invalidCookies);
//返回List<Cookie>让Request进行设置
return validCookies;
}
}
// 保持okHttpClient唯一实例
public static OkHttpClientUtils getOkHttpClientUtils(Context context) {
if (okHttpUtils == null) {
synchronized (OkHttpClientUtils.class) {
if (okHttpUtils == null) {
okHttpUtils = new OkHttpClientUtils(context);
}
}
}
return okHttpUtils;
}
public static OkHttpClient getOkHttpSingletonInstance() {
if (okHttpClient == null) {
synchronized (OkHttpClient.class) {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient();
}
}
}
return okHttpClient;
}
///
// GET方式网络访问
///
/**
* 基方法,返回Request对象
*
* @param urlString
* @param tag
* @return
*/
private Request buildGetRequest(String urlString, Object tag) {
Request.Builder builder = new Request.Builder();
builder.url(urlString);
if (tag != null) {
builder.tag(tag);
}
return builder.build();
}
/**
* 自定义方法,返回Response对象
*
* @param urlString
* @return
* @throws IOException
*/
private Response buildResponse(String urlString, Object tag) throws IOException {
Request request = buildGetRequest(urlString, tag);
Response response = okHttpClient.newCall(request).execute();
return response;
}
//基础方法,返回ResponseBody对象
private ResponseBody buildResponseBody(String urlString, Object tag) throws IOException {
Response response = buildResponse(urlString, tag);
if (response.isSuccessful()) {
return response.body();
}
return null;
}
/**
* 作用:实现网络访问文件,将获取到数据储存在文件流中
*
* @param urlString :访问网络的url地址
* @return InputStream
*/
public static InputStream getStreamFromURL(Context context, String urlString, Object tag) throws IOException {
ResponseBody body = getOkHttpClientUtils(context).buildResponseBody(urlString, tag);
if (body != null) {
return body.byteStream();
}
return null;
}
/**
* 作用:实现网络访问文件,将获取到的数据存在字节数组中
*
* @param urlString :访问网络的url地址
* @return byte[]
*/
public static byte[] getBytesFromURL(Context context, String urlString, Object tag) throws IOException {
ResponseBody body = getOkHttpClientUtils(context).buildResponseBody(urlString, tag);
if (body != null) {
return body.bytes();
}
return null;
}
/**
* 作用:实现网络访问文件,将获取到的数据存在字符串中
*
* @param urlString :访问网络的url地址
* @return String
*/
public static String getStringFromURL(Context context, String urlString, Object tag) throws IOException {
ResponseBody body = getOkHttpClientUtils(context).buildResponseBody(urlString, tag);
if (body != null) {
return body.string();
}
return null;
}
///
// POST方式访问网络
///
/**
* 基方法,返回Request对象
*
* @param urlString
* @param tag
* @return
*/
private Request buildPostRequest(String urlString, RequestBody requestBody, Object tag) {
Request.Builder builder = new Request.Builder();
builder.url(urlString).post(requestBody);
//builder.addHeader("Accept", "application/json; q=0.5");
if (tag != null) {
builder.tag(tag);
}
return builder.build();
}
/**
* 作用:post提交数据,返回服务器端返回的字节数组
*
* @param urlString :访问网络的url地址
* @return byte[]
*/
private String postRequestBody(String urlString, RequestBody requestBody, Object tag) {
Request request = buildPostRequest(urlString, requestBody, tag);
try {
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 作用:POST提交键值对,再返回相应的数据
*
* @param urlString :访问网络的url地址
* @param map :访问url时,需要传递给服务器的键值对数据。
* @return String
*/
public static String postKeyValuePair(Context context, String urlString, Map<String, String> map, Object tag) {
FormBody.Builder formBuilder = new FormBody.Builder();
//往FormBody对象中放置键值对
if (map != null && !map.isEmpty()) {
for (Map.Entry<String, String> entry : map.entrySet()) {
formBuilder.add(entry.getKey(), entry.getValue());
}
}
//生成请求体对象
RequestBody requestBody = formBuilder.build();
//将请求提放置到请求对象中
return getOkHttpClientUtils(context).postRequestBody(urlString, requestBody, tag);
}
/**
* 作用:POST提交Json字符串,再返回相应的数据
*
* @param urlString :访问网络的url地址
* @param jsonString :访问url时,需要传递给服务器的json字符串
* @return byte[]
*/
public static String postJsonString(Context context, String urlString, String jsonString, Object tag) {
//定义mimetype对象
/*String MEDIA_TYPE_STREAM = "application/octet-stream;charset=utf-8";
String MEDIA_TYPE_STRING = "text/plain;charset=utf-8";*/
String MEDIA_TYPE_JSON = "application/json;charset=utf-8";
MediaType JSON = MediaType.parse(MEDIA_TYPE_JSON);
RequestBody requestBody = RequestBody.create(JSON, jsonString);
return getOkHttpClientUtils(context).postRequestBody(urlString, requestBody, tag);
}
///
// 异步网络访问
///
/**
* 开启异步线程访问网络,通过回调方法实现数据加载
* 如果第二个参数为null,空callback, 则说明不在意返回结果
*
* @param urlString
* @param callback
*/
public static void getDataAsync(Context context, String urlString, Callback callback, Object tag) {
Request request = getOkHttpClientUtils(context).buildGetRequest(urlString, tag);
if (callback == null) {
new Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
};
}
getOkHttpSingletonInstance().newCall(request).enqueue(callback);
}
/**
* 作用:post提交数据,返回服务器端返回的字节数组
*
* @param urlString :访问网络的url地址
*/
private void postRequestBodyAsync(String urlString, RequestBody requestBody, Callback callback, Object tag) {
Request request = buildPostRequest(urlString, requestBody, tag);
if (callback == null) {
new Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
};
}
okHttpClient.newCall(request).enqueue(callback);
}
/**
* 作用:POST提交键值对,再返回相应的数据
*
* @param urlString :访问网络的url地址
* @param map :访问url时,需要传递给服务器的键值对数据。
*/
public static void postKeyValuePairAsync(Context context, String urlString, Map<String, String> map, Callback callback, Object tag) {
//往FormEncodingBuilder对象中放置键值对
FormBody.Builder formBuilder = new FormBody.Builder();
if (map != null && !map.isEmpty()) {
for (Map.Entry<String, String> entry : map.entrySet()) {
formBuilder.add(entry.getKey(), entry.getValue());
}
}
//生成请求体对象
RequestBody requestBody = formBuilder.build();
//将请求提放置到请求对象中
getOkHttpClientUtils(context).postRequestBodyAsync(urlString, requestBody, callback, tag);
}
/**
* 作用:post异步上传文件,提交分块请求
*
* @param urlString 网络地址
* @param map 提交给服务器的表单信息键值对
* @param files 提交的文件
* @param formFieldName 每个需要提交的文件对应的文件input的name值
* @param callback 异步上传回调方法
* @throws IOException
*/
public static void postUploadFilesAsync(Context context, String urlString, Map<String, String> map, File[] files, String[] formFieldName, Callback callback, Object tag) throws IOException {
RequestBody requestBody = getOkHttpClientUtils(context).buildRequestBody(map, files, formFieldName);
getOkHttpClientUtils(context).postRequestBodyAsync(urlString, requestBody, callback, tag);
}
///
// POST方式提交分块请求,实现文件上传
///
/**
* 同步基于post的文件上传:上传多个文件以及携带key-value对:主方法
*
* @param urlString
* @param formFiledName
* @param files
* @param map
* @param tag
* @return String
* @throws IOException
*/
public static String postUploadFiles(Context context, String urlString, Map<String, String> map, File[] files, String[] formFiledName, Object tag) throws IOException {
RequestBody requestBody = getOkHttpClientUtils(context).buildRequestBody(map, files, formFiledName);
return getOkHttpClientUtils(context).postRequestBody(urlString, requestBody, tag);
}
/**
* 创建post上传附件的request对象
* Post方式提交分块请求——上传文件及其它表单数据
*
* @param files
* @param formFiledName
* @param map
* @return
*/
private RequestBody buildRequestBody(Map<String, String> map, File[] files, String[] formFiledName) {
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
//往MultipartBuilder对象中添加普通input控件的内容
if (map != null) {
for (Map.Entry<String, String> entry : map.entrySet()) {
//添加普通input块的数据
builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + entry.getKey() + "\""),
RequestBody.create(null, entry.getValue()));
}
}
//往MultipartBuilder对象中添加file input控件的内容
if (files != null && formFiledName != null) {
for (int i = 0; i < files.length; i++) {
File file = files[i];
String fileName = file.getName();
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
//添加file input块的数据
builder.addPart(Headers.of("Content-Disposition",
"form-data; name=\"" + formFiledName[i] + "\"; filename=\"" + fileName + "\""), requestBody);
}
}
//生成RequestBody对象
return builder.build();
}
/**
* 获取Mime类型
*
* @param filename
* @return
*/
private static String getMimeType(String filename) {
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String contentTypeFor = fileNameMap.getContentTypeFor(filename);
if (contentTypeFor == null) {
contentTypeFor = "application/octet-stream";
}
return contentTypeFor;
}
public static void cancelCall(Context context, Object tag) {
if (tag == null) {
return;
}
synchronized (okHttpClient.dispatcher().getClass()) {
for (Call call : okHttpClient.dispatcher().queuedCalls()) {
if (tag.equals(call.request().tag())) call.cancel();
}
for (Call call : okHttpClient.dispatcher().runningCalls()) {
if (tag.equals(call.request().tag())) call.cancel();
}
}
}
}
在Application初始化时最好这样处理,
OkHttpClient okHttpClient = OkHttpClientUtils.getOkHttpSingletonInstance();
一次初始化,随处调用。
版权申明:未经作者同意,禁止转载。