基于Okhttp3做的封装+性能优化

基于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();

一次初始化,随处调用。


版权申明:未经作者同意,禁止转载。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值