Android/Java中okhttp用法介绍

34 篇文章 1 订阅

最近Android老项目改造, 之前老代码一直用AndroidHttpClient实现网络请求,存在以下问题

1. 版本太老旧, 最近无更新;

2.  网络请求模块代码太散乱,不好管理,

3. 不支持SSL/TLS1.2以上版本

反正就是旧时代的产物, 需要进行整改.

于是就开始网络请求模块重构, 组训以下原则:

1. 修改网络请求不能修改之前的业务到吗逻辑,包括其他人的代码, 避免引入bug;

2.网络请求只能用一两个类实现, 统一管理整个http请求功能, 便于逻辑控制;

选择用当下比较流行的okhttp进行改造.

1. 历史上Http请求库优缺点

借用oncealong的介绍

在讲述OkHttp之前, 我们看下没有OkHttp的时代, 我们是如何完成http请求的.
在没有OkHttp的日子, 我们使用HttpURLConnection或者HttpClient. 那么这两者都有什么优缺点呢? 为什么不在继续使用下去呢?
HttpClient是Apache基金会的一个开源网络库, 功能十分强大, API数量众多, 但是正是由于庞大的API数量使得我们很难在不破坏兼容性的情况下对它进行升级和扩展, 所以Android团队在提升和优化HttpClient方面的工作态度并不积极.
HttpURLConnection是一种多用途, 轻量极的HTTP客户端, 提供的API比较简单, 可以容易地去使用和扩展. 不过在Android 2.2版本之前, HttpURLConnection一直存在着一些令人厌烦的bug. 比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:

// 这是一个2.2版本之前的bug    
    if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {    
        System.setProperty("http.keepAlive", "false");    
    }

因此, 一般的推荐是在2.2之前, 使用HttpClient, 因为其bug较少. 在2.2之后, 推荐使用HttpURLConnection, 因为API简单, 体积小, 并且有压缩和缓存机制, 并且Android团队后续会继续优化HttpURLConnection.

但是, 上面两个类库和OkHttp比起来就弱爆了, 因为OkHttp不仅具有高效的请求效率, 并且提供了很多开箱即用的网络疑难杂症解决方案.

  • 支持HTTP/2, HTTP/2通过使用多路复用技术在一个单独的TCP连接上支持并发, 通过在一个连接上一次性发送多个请求来发送或接收数据
  • 如果HTTP/2不可用, 连接池复用技术也可以极大减少延时
  • 支持GZIP, 可以压缩下载体积
  • 响应缓存可以直接避免重复请求
  • 会从很多常用的连接问题中自动恢复
  • 如果您的服务器配置了多个IP地址, 当第一个IP连接失败的时候, OkHttp会自动尝试下一个IP
  • OkHttp还处理了代理服务器问题和SSL握手失败问题

使用 OkHttp 无需重写您程序中的网络代码。OkHttp实现了几乎和java.net.HttpURLConnection一样的API。如果你用了 Apache HttpClient,则OkHttp也提供了一个对应的okhttp-apache 模块。

作者:oncealong
链接:https://www.jianshu.com/p/ca8a982a116b

由于目前网络上关于okhttp讲理论的人比较多, 笔者就不赘述, 直接上代码, 函数都有详细注释, 观众可以直接复制使用; 我会把完整的module到吗放到个人资源,可以直接下载: https://download.csdn.net/download/zhanghao_Hulk/16020524

 基本的okhttp实现

2.1.1 使用SSL证书验证的 Okhttp client

 适用于https 代码如下, 目前用的比较多, 因为大部分网站和服务器都是https了.

其中使用的sslSocketFactory和TrustManager参考下文的 SSLUtils


    /**
     * 创键天机的服务器请求的 OK http client builder
     * <p>其中包含: 天机服务器的证书验证, token和参数验证等等.
     * @return
     */
    public static OkHttpClient.Builder createSslOkHttpBuilder() {
        return new OkHttpClient.Builder()
                .cookieJar(sMyCookieJar)
                .connectTimeout(CONNECTED_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                .sslSocketFactory(getSSLSocketFactory(), getTrustManager())
                .hostnameVerifier(getCustomHostnameVerifier())
                .authenticator(getAuthenticator())
                .addInterceptor(getHeaderInterceptor())
                .addInterceptor(getUrlParamInterceptor());
         // 其中的拦截器请参考完整代码中举例,如果用不到也可以删掉,或者自定义
    }

    /**
     * 获取天机的服务器请求的 OK http client builder(优先使用缓存)
     * <p>其中包含: 天机服务器的证书验证, token和参数验证等等.
     * @return
     */
    public static OkHttpClient.Builder getOkHttpBuilder() {
        if (sOkHttpBuilder == null) {
            sOkHttpBuilder = createSslOkHttpBuilder();
        }
        return sOkHttpBuilder;
    }

    /**
     * 获取天机的服务器请求的 OK http client (金莲使用缓存)
     * <p>其中包含: 天机服务器的证书验证, token和参数验证等等.
     * @return
     */
    public static OkHttpClient getOkHttpClient() {
        if (sOkHttpClient == null) {
            synchronized (OkHttpManager.class) {
                if (sOkHttpClient == null) {
                    sOkHttpClient = getOkHttpBuilder()
                            //通用接口请求需要打印response body 日志,
                            // 但是在文件下载响应中不能使用Level.BODY,否则会导致下载请求被卡主,
                            // 直到文件下载完成后, execute()才返回,或者异步回调onResponse()或者,
                            // 导致事件非常耗时,无法实现下载精度和断点续传
                            .addInterceptor(sLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY))
                            .build();
                }
            }
        }
        return sOkHttpClient;
    }

    /**
     * 获取下载 OK http client
     * <p>下载需要使用进度监听器,不能使用缓存单利的 ok client, 不许创建一个新的 client
     * @param listener
     * @return
     */
    public static OkHttpClient getDownloadSslClient(ProgressListener listener) {
        //下载因为需要使用进度监听器,不能使用单利的 client he builder
        // 必须每次创键一个新的client,传入回调接口,
        // 避免进度拦截器越加越多,后面的的下载进度篡改前面的下载进度
        OkHttpClient.Builder clientBuilder = createSslOkHttpBuilder();
        if (listener != null) {
            clientBuilder.addNetworkInterceptor(new ProgressInterceptor(listener));
        }
        return clientBuilder.build();
    }

    public static OkHttpClient getDownloadSslClient() {
        return getDownloadSslClient(null);
    }

 

2.1.2 普通的 Okhttp client, 适用于http

   函数方法都有介绍


    /**
     * 创键极简素版本 OK http client builder.
     * @param listener
     * @return
     */
    public static OkHttpClient.Builder createPlainHttpBuilder(ProgressListener listener) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .cookieJar(sMyCookieJar)
                .connectTimeout(CONNECTED_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS);
        if (listener != null) {
            builder.addNetworkInterceptor(new ProgressInterceptor(listener));
        }
        return builder;
    }

    /**
     * 浏览器下载OK http client, 不含有证书和域名等等验证
     * 浏览器下载可能其他服务器,不是天机服务器,不能验证生疏等等
     * @return
     */
    public static OkHttpClient getBrowserDownloadClient(ProgressListener listener) {
        OkHttpClient.Builder clientBuilder = getPlainHttpBuilder(listener);
        return clientBuilder.build();
    }

    public static OkHttpClient getBrowserDownloadClient() {
        return getBrowserDownloadClient(null);
    }

    /**
     * 创键极简素版本 OK http client builder.
     * @return
     */
    public static OkHttpClient.Builder getPlainHttpBuilder() {
        if (sPlainHttpBuilder == null) {
            sPlainHttpBuilder = createPlainHttpBuilder();
        }
        return sPlainHttpBuilder;
    }

    /**
     * 创键极简素版本 OK http client builder.
     * @return
     */
    public static OkHttpClient.Builder getPlainHttpBuilder(ProgressListener listener) {
        if (listener != null) {
            return createPlainHttpBuilder(listener);
        } else {
            return getPlainHttpBuilder();
        }
    }

    /**
     * 创键极简素版本 OK http client builder.
     * @return
     */
    public static OkHttpClient.Builder createPlainHttpBuilder() {
        return createPlainHttpBuilder(null);
    }

2.2 下载进度进本实现

使用response精度拦截器可实现监控数据管道进度, 从而实现下载进度

package com.hulk.android.http.ok;

import android.util.Log;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Response;

/**
 * 下载进度拦截器
 * @author: zhanghao
 * @Time: 2021-02-24 22:04
 */
public class ProgressInterceptor implements Interceptor {

    private static final String TAG = "ProgressInterceptor";
    ProgressListener listener;

    public ProgressInterceptor(ProgressListener listener) {
        this.listener = listener;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        if (listener == null) {
            Log.i(TAG, "intercept: listener is null");
            return response;
        }
        Log.i(TAG, "intercept: listener is " + listener);
        //这里将ResponseBody包装成我们的ProgressResponseBody
        return response.newBuilder()
                .body(new ProgressResponseBody(response, listener))
                .build();
    }
}

响应数据管道监控实现类

package com.hulk.android.http.ok;

import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;

/**
 * 下载进度响应
 * @author: zhanghao
 * @Time: 2021-02-24 21:58
 */
public class ProgressResponseBody extends ResponseBody {
    private final ResponseBody responseBody;
    private final ProgressListener progressListener;
    private BufferedSource bufferedSource;
    private Response response;

    public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener){
        this.responseBody = responseBody;
        this.progressListener = progressListener;
        onPreExecute();
    }

    public ProgressResponseBody(Response response, ProgressListener progressListener){
        this.response = response;
        this.responseBody = response.body();
        this.progressListener = progressListener;
        onPreExecute();
    }

    private void onPreExecute() {
        if (progressListener != null) {
            progressListener.onPreExecute(contentLength(), response);
        }
    }

    @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;
    }

    /**
     * 回调下载精度函数
     * @param source
     * @return
     */
    private Source source(Source source){
        return new ForwardingSource(source) {
            long totalBytesRead = 0L;
            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long bytesRead = 0;
                try {
                    bytesRead = super.read(sink,byteCount);
                    //不断统计当前下载好的数据
                    totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                    boolean done = bytesRead == -1;
                    //接口回调
                    if (progressListener != null) {
                        progressListener.update(totalBytesRead, done, response);
                    }
                } catch (IOException e) {
                    if (progressListener != null) {
                        progressListener.onError(e, response);
                    }
                    throw e;
                }
                return bytesRead;
            }
        };
    }
}

2.3  okhttp中以上代码的函数注释

    /**
     * 创键okhttp Call,用于执行Request
     * @param request
     * @return
     */
    public static Call createCall(Request request) {
        OkHttpClient okHttpClient = getOkHttpClient();
        Call call = okHttpClient.newCall(request);
        return call;
    }

    /**
     * 创建请求POST/GET
     * @param request
     * @return
     * @throws IOException
     */
    public static Response executeRequest(Request request) throws IOException {
        Call call = createCall(request);
        return call.execute();
    }

    /**
     * 请求服务器, eg: 上传文件
     * @param url
     * @param requestBody
     * @return
     * @throws IOException
     */
    public static Response executeRequest(String url, RequestBody requestBody) throws IOException {
        if (requestBody == null) {
            logw("executeRequest: requestBody is null");
            return null;
        }
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Response response = OkHttpManager.executeRequest(request);
        boolean isSuccessful = response != null ? response.isSuccessful() : false;
        if (isSuccessful) {
            logi("executeRequest response: " + response);
        } else {
            logw("executeRequest Failed response: " + response);
        }
        return response;
    }

    /**
     * 请求服务器, eg: 上传文件
     * @param url
     * @param multipartBody 表格提交数据 eg: 文件上传
     * @return
     * @throws IOException
     */
    public static Response uploadMultipartData(String url, MultipartBody multipartBody) throws IOException {
        if (multipartBody == null) {
            logw("uploadMultipartData: multipartBody is null");
            return null;
        }
        return executeRequest(url, multipartBody);
    }

2.4 常用封装好的方法工具类


/**
 * Ok http 请求工具类
 * @author hulk
 */
public class OkHttpUtils {

    private static final String TAG = "OkHttpUtils";
    public static final int REQUEST_TIMEOUT = 30 * 1000;
    public static final String CONTENT_TYPE_PROTOBUF = OkHttpManager.CONTENT_TYPE_PROTOBUF;

    /**
     * 发起 http het 请求
     * <p>可用于文下载和普通接口请求
     * <p>使用完成一定要记得关闭 ResponseBody(close)
     * @param url
     * @return
     * @throws IOException
     * @throws HttpException
     */
    public static Response sendOkHttpGetRequest(String url) throws IOException {
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .get();
        logi("sendOkHttpGetRequest url: " + url);
        Response response = OkHttpManager.executeRequest(requestBuilder.build());
        return response;
    }

    /**
     * 发起 http het 请求
     * <p>可用于文下载和普通接口请求
     * <p>使用完成一定要记得关闭 ResponseBody(close)
     * @param url
     * @return
     * @throws IOException
     * @throws HttpException
     */
    public static ResponseBody sendHttpGetRequest(String url)
            throws IOException, HttpException {
        Response response = sendOkHttpGetRequest(url);
        int code = response.code();
        if (code != OkHttpStatus.SC_OK) {
            LogUtil.e(TAG, "sendHttpGetRequest: Failed HttpException code= " + code);
            throw new HttpException(code, url);
        } else {
            LogUtil.i(TAG, "sendHttpGetRequest: SC_OK");
        }
        ResponseBody responseBody = response.body();
        return responseBody;
    }

    /**
     * 发起 http het 请求
     * <p>可用于文下载和普通接口请求
     * <p>使用完成一定要记得关闭输入流(close)
     * @param url
     * @return
     * @throws IOException
     * @throws HttpException
     */
    public static InputStream sendInputGetRequest(String url)
            throws IOException, HttpException {
        ResponseBody responseBody = sendHttpGetRequest(url);
        if (responseBody != null) {
            InputStream inputStream = responseBody.byteStream();
            return inputStream;
        } else {
            LogUtil.e(TAG, "sendInputGetRequest responseBody is null ");
        }
        return null;
    }

    /**
     * Get方式请求网络接口,返回byte数组,可以使json字字符串,或者图片视频等等
     * @param url
     * @return
     * @throws IOException
     * @throws HttpException
     */
    public static byte[] sendDataGetRequest(String url) throws IOException, HttpException {
        InputStream inputStream = sendInputGetRequest(url);
        if (inputStream != null) {
            try {
                byte[] data = StreamTool.read(inputStream);
                if (data != null) {
                    logi("sendDataGetRequest success response data.length= " + data.length);
                    return data;
                } else {
                    logw("sendDataGetRequest failed to read inputStream ");
                }
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
            }
        } else {
            LogUtil.w(TAG, "sendGetDataRequest Content inputStream is null ");
        }
        return null;
    }

    /**
     * Get方式请求网络接口,返回 String
     * @param url
     * @return
     * @throws IOException
     * @throws HttpException
     */
    public static String sendStringGetRequest(String url) throws IOException, HttpException {
        byte[] data = sendDataGetRequest(url);
        if (data != null) {
            String str = new String(data);
            return str;
        } else {
            LogUtil.w(TAG, "sendStringGetRequest: data is null ");
        }
        return null;
    }

    /**
     * Get方式请求网络接口,返回 String
     * @param url
     * @return
     * @throws IOException
     * @throws HttpException
     */
    public static HttpResult<String> sendResultGetRequest(String url) throws IOException, HttpException {
        HttpResult<String> result = new HttpResult<>(-1, "");
        byte[] data = sendDataGetRequest(url);
        if (data != null) {
            try {
                result = parseJsonData(data);
            } catch (JSONException e) {
                Log.w(TAG, "sendResultGetRequest failed: " + e, e);
            }
        } else {
            result = new HttpResult<>(-1, "data is null");
            LogUtil.w(TAG, "sendGetResultRequest: data is null ");
        }
        return result;
    }

    /**
     * 解析服务器返回的通用json数据(存在错误码和错误信息)
     * @param resultData
     * @return
     * @throws JSONException
     */
    public static HttpResult<String> parseJsonData(byte[] resultData) throws JSONException {
        HttpResult<String> result = new HttpResult<>(-1, "");
        String resultStr = new String(resultData);
        result.data = resultStr;
        if (!TextUtils.isEmpty(resultStr)) {
            JSONObject resultJson = new JSONObject(resultStr);
            result.code = resultJson.optInt("code");
            result.msg = resultJson.optString("errorMessage");
            if (result.code != 0) {
                result.detail = resultStr;
                LogUtil.w(TAG, "parseJsonData failedresult: " + result);
            } else {
                LogUtil.i(TAG, "parseJsonData result: " + result);
            }
        } else {
            LogUtil.w(TAG, "parseJsonData: resultStr is empty: " + result);
        }
        return result;
    }

    /**
     * Post方式请求网络接口,返回byte数组,可以使json字字符串,或者图片视频等等
     * @param url 服务器接口地址
     * @param mediaType okhttp3.MediaType onject eg: "application/json" or CONTENT_TYPE_PROTOBUF...
     * @param postData
     * @return
     * @throws RuntimeException
     * @throws IOException
     * @throws HttpException
     */
    public static byte[] sendHttpPostRequest(String url, MediaType mediaType, byte[] postData)
            throws IOException, RuntimeException, HttpException {
        if (TextUtils.isEmpty(url)) {
            logw("sendHttpPostRequest: failed for invalid url: " + url);
            throw new IllegalArgumentException("url is null");
        }
        if (TextUtils.isEmpty(url)) {
            logw("sendHttpPostRequest: postData is null");
            throw new IllegalArgumentException("postData is null");
        }
        if (mediaType == null) {
            logw("sendHttpPostRequest: mediaType is null");
            throw new IllegalArgumentException("mediaType is null");
        }
        RequestBody requestBody = RequestBody.create(mediaType, postData);
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .post(requestBody);
        Response response = OkHttpManager.executeRequest(requestBuilder.build());
        ResponseBody responseBody = null;
        logw("sendHttpPostRequest url: " + url + ", postData length: " + postData.length);
        try {
            int code = response.code();
            logw("sendHttpPostRequest Http Status Code " + code);
            if (code != OkHttpStatus.SC_OK) {
                LogUtil.e(TAG, "Throw HttpException code= " + code + ", url path: " + UrlParser.getUrlPath(url));
                throw new HttpException(code, url);
            }
            responseBody = response.body();
            if (responseBody != null) {
                InputStream inputStream = responseBody.byteStream();
                if (inputStream != null) {
                    byte[] data = StreamTool.read(inputStream);
                    if (data != null) {
                        //logi("sendHttpGetRequest success response data: " + Arrays.toString(data));
                        logi("sendHttpPostRequest success response data.length= " + data.length);
                        return data;
                    } else {
                        logw("sendHttpPostRequest failed to read inputStream ");
                    }
                } else {
                    LogUtil.w(TAG, "sendHttpPostRequest Content inputStream is null ");
                }
            } else {
                LogUtil.e(TAG, "sendHttpPostRequest HttpEntity is null ");
            }
        } finally {
            if (responseBody != null) {
                responseBody.close();
            }
        }
        return null;
    }

    /**
     * Post方式请求网络接口,返回byte数组,可以使json字字符串,或者图片视频等等
     * @param url 服务器地址
     * @param contentType "application/json" or CONTENT_TYPE_PROTOBUF...
     * @param url
     * @return
     * @throws RuntimeException
     * @throws IOException
     * @throws HttpException
     */
    public static byte[] sendHttpPostRequest(String url, String contentType, byte[] postData)
            throws IOException, RuntimeException, HttpException {
        MediaType mediaType = MediaType.parse(contentType);
        return sendHttpPostRequest(url, mediaType, postData);
    }

    /**
     * Post方式请求网络接口,返回byte数组
     * <p>contentType = CONTENT_TYPE_PROTOBUF;
     * @param url 服务器地址
     * @param url
     * @return
     * @throws RuntimeException
     * @throws IOException
     * @throws HttpException
     */
    public static byte[] sendProtoPostRequest(String url, byte[] requestData)
            throws IOException, RuntimeException, HttpException {
        String contentType = CONTENT_TYPE_PROTOBUF;
        return sendHttpPostRequest(url, contentType, requestData);
    }

    /**
     * Post方式请求网络接口,返回byte数组
     * <p>contentType = "application/json";
     * @param url 服务器地址
     * @param url
     * @param requestData
     * @return
     * @throws IOException
     * @throws RuntimeException
     * @throws HttpException
     */
    public static byte[] sendJsonPostRequest(String url, byte[] requestData)
            throws IOException, RuntimeException, HttpException {
        String contentType = OkHttpManager.CONTENT_TYPE_JSON;
        return sendHttpPostRequest(url, contentType, requestData);
    }

    /**
     * Post方式请求网络接口,返回 String
     * <p>contentType = "application/json";
     * @param url 服务器地址
     * @param url
     * @param requestData
     * @return
     * @throws IOException
     * @throws RuntimeException
     * @throws HttpException
     */
    public static String sendStringJsonPostRequest(String url, byte[] requestData)
            throws IOException, RuntimeException, HttpException {
        byte[] res = sendJsonPostRequest(url, requestData);
        return new String(res, "UTF-8");
    }

    /**
     * Post发送json数据 (通用)
     * @param url
     * @param requestData
     * @return  HttpResult<String> 对象,其中data为json eg: {"code":0,"errorMessage":"Success"}
     */
    public static HttpResult<String> sendJsonPostRequestCommon(String url, byte[] requestData) {
        HttpResult<String> result = new HttpResult<>(-1, "");
        try {
            byte[] resultData = sendJsonPostRequest(url, requestData);
            if (resultData != null) {
                try {
                    result = parseJsonData(resultData);
                } catch (JSONException e) {
                    Log.w(TAG, "sendGetResultRequest failed: " + e, e);
                }
            } else {
                result = new HttpResult<>(-1, "data is null");
                LogUtil.w(TAG, "sendGetResultRequest: data is null ");
            }
        } catch (Exception e) {
            String detail = e + ", url= " + url;
            if (e instanceof HttpException) {
                result.code = ((HttpException)e).code();
            }
            result.msg = e.getMessage();
            result.detail = detail;
            result.error = e;
            LogUtil.e(TAG, "sendJsonPostRequestCommon failed: " + e + ", detail= " + detail, e);
        }
        return result;
    }

    /**
     * 请求服务器, eg: 上传文件
     * @param url
     * @param requestBody
     * @return
     * @throws IOException
     */
    public static Response executeRequest(String url, RequestBody requestBody) throws IOException {
        return OkHttpManager.executeRequest(url, requestBody);
    }

    /**
     * 请求服务器, eg: 上传文件
     * @param url
     * @param multipartBody 表格提交数据 eg: 文件上传
     * @return
     * @throws IOException
     */
    public static Response uploadMultipartData(String url, MultipartBody multipartBody) throws IOException {
        return OkHttpManager.uploadMultipartData(url, multipartBody);
    }

    /**
     * 上传文件(可多个)
     * @param url
     * @param formDataName 表数据自丢按名称,自定义, 与服务端协商 eg: file, data ...
     * @param filePaths
     * @param url
     * @param formDataName
     * @param filePaths
     * @return
     * @throws IOException
     */
    public static Response uploadFiles(String url, String formDataName, String... filePaths) throws IOException {
        MultipartBody.Builder multipartBuilder = OkRequestUtils.createMultipartBody(formDataName, filePaths);
        if (multipartBuilder == null) {
            logw("uploadFiles: multipartBuilder si null");
            return null;
        }
        MultipartBody requestBody = multipartBuilder.build();
        Response response = uploadMultipartData(url, requestBody);
        return response;
    }

    /**
     * 上传文件
     * @param url
     * @param formDataName
     * @param filePath
     * @return
     * @throws IOException
     */
    public static String uploadFile(String url, String formDataName, String filePath) throws IOException {
        logi("uploadFile: formDataName=" + formDataName + ", filePath= " + filePath);
        Response response = uploadFiles(url, formDataName, filePath);
        ResponseBody responseBody = null;
        try {
            if (response != null) {
                responseBody = response.body();
                String resStr = new String(responseBody.bytes());
                logw("uploadFile: " + resStr);
                return resStr;
            } else {
                logw("uploadFile: response is null");
            }
        } finally {
            if (responseBody != null) {
                responseBody.close();
            }
        }

        return "";
    }

    private static void logw(String msg) {
        LogUtil.w(TAG, msg + ", date: " + new Date().toLocaleString());
    }

    private static void logi(String msg) {
        LogUtil.i(TAG, msg + ", date: " + new Date().toLocaleString());
    }
}

2.5.  SSL证书相关的工具类 SSLUtils



public class SSLUtils {

    private static final String TAG = "SSLUtils" ;

    private static Context sContext;

    static HostnameVerifier sCustomHostnameVerifier = null;

    /**
     * 证书验证开关: https 和 mqtt 连接是否验证服务端证书
     */
    private static boolean sServerCertVerifyEnabled = true;

    public static void setContext(Context context) {
        SSLUtils.sContext = context;
    }

    /**
     * 设置是否需要验证服务器证书
     * @param serverCertVerifyEnabled
     */
    public static void setServerCertVerifyEnabled(boolean serverCertVerifyEnabled) {
        sServerCertVerifyEnabled = serverCertVerifyEnabled;
    }

    /**
     * 是否需要验证服务器证书
     * @return
     */
    public static boolean isServerCertVerifyEnabled() {
        return sServerCertVerifyEnabled;
    }

    public static HostnameVerifier getCustomHostnameVerifier() {
        if (sCustomHostnameVerifier == null) {
            sCustomHostnameVerifier = new NoneHostnameVerifier();
        }
        return sCustomHostnameVerifier;
    }

    /**
     * 默认信任所有的证书
     * <p>最好加上证书认证,主流App都有自己的证书
     * @return
     */
    public static SSLSocketFactory createSSLSocketFactory(X509TrustManager trustManager) {
        SSLSocketFactory sslSocketFactory = null;
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "createSSLSocketFactory: NoSuchAlgorithmException", e);
        } catch (KeyManagementException e) {
            Log.e(TAG, "createSSLSocketFactory: KeyManagementException", e);
        } catch (Exception e) {
            Log.e(TAG, "createSSLSocketFactory: ", e);
        }
        return sslSocketFactory;
    }

    public static SSLSocketFactory createNoneSSLSocketFactory() {
        return createSSLSocketFactory(createTrustNoneManager());
    }

    public static SSLSocketFactory getSSLSocketFactory() {
        return getSSLSocketFactory(sContext);
    }

    /**
     * 信任指定的证书的工厂
     * @param context
     * @return
     */
    public static SSLSocketFactory getSSLSocketFactory(Context context) {
        if (context == null) {
            throw new NullPointerException("context == null, please call setContext() at first.");
        }
        try {
            CustomTrustManager manager = new CustomTrustManager(context);
            manager.setServerCertVerifyEnabled(isServerCertVerifyEnabled());
            return createSSLSocketFactory(manager);
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "createSSLSocketFactory: NoSuchAlgorithmException", e);
        } catch (IOException e) {
            Log.e(TAG, "createSSLSocketFactory: IOException", e);
        } catch (Exception e) {
            Log.e(TAG, "createSSLSocketFactory: ", e);
        }
        Log.w(TAG, "getSSLSocketFactory: create None SSLSocketFactory");
        return createNoneSSLSocketFactory();
    }

    /**
     * 获取本地自定义证书
     * @param context
     * @return
     */
    public static X509TrustManager getLocalCustomManager(Context context) {
        if (context == null) {
            return null;
        }
        try {
            X509Certificate rootCert = generateCertFromAssets(context,"root_ca.crt");
            // 设置算法
            TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            // 设置类型
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            // 不加载外部证书
            keyStore.load(null);
            // 加载内置的根证书到keyStore中
            if (rootCert != null) {
                Log.i(TAG, "getLocalCustomManager: add root_ca.crt");
                keyStore.setCertificateEntry("root_ca", rootCert);
            }
            // 还可以添加其他证书
            X509Certificate rootCert2 = generateCertFromAssets(context,"root_ca2.crt");
            if (rootCert2 != null) {
                Log.i(TAG, "getLocalCustomManager: add root_ca2.crt");
                keyStore.setCertificateEntry("root_ca2", rootCert2);
            }
            factory.init(keyStore);
            return getFirstX509TrustManager(factory);
        } catch (CertificateException e) {
            Log.w(TAG, "getLocalCustomManager CertificateException: " + e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            Log.w(TAG, "getLocalCustomManager NoSuchAlgorithmException: " + e);
            e.printStackTrace();
        } catch (KeyStoreException e) {
            Log.w(TAG, "getLocalCustomManager KeyStoreException: " + e);
            e.printStackTrace();
        } catch (IOException e) {
            Log.w(TAG, "getLocalCustomManager IOException: " + e);
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 获得系统默认的 X509TrustManager
     * @return
     * @throws KeyStoreException
     * @throws NoSuchAlgorithmException
     */
    public static X509TrustManager getDefaultManager() throws KeyStoreException, NoSuchAlgorithmException {
        TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        factory.init((KeyStore) null);
        return getFirstX509TrustManager(factory);
    }


    /**
     * 获取Assets中的内置证书
     * @param context
     * @param filename
     * @return
     */
    private static X509Certificate generateCertFromAssets(Context context, String filename) {
        try {
            InputStream inputStream = context.getAssets().open(filename);
            // 生成X509证书
            X509Certificate cert = generateX509Cert(inputStream);
            return cert;
        } catch (CertificateException e) {
            Log.w(TAG, "generateCertFromAssets Failed: " + e + ", filename: " + filename);
            e.printStackTrace();
        } catch (IOException e) {
            Log.w(TAG, "generateCertFromAssets Failed: " + e + ", filename: " + filename);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取输入流中的内置X509证书
     * @param inputStream  可以使assets里面的输入流,或者FileInputStream
     * @return
     */
    private static X509Certificate generateX509Cert(InputStream inputStream) throws CertificateException {
        // X509方案
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        // 生成X509证书
        X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(inputStream);
        return cert;
    }

    public static X509TrustManager getFirstX509TrustManager(TrustManagerFactory trustManagerFactory) {
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        for (TrustManager trustManager : trustManagers) {
            if (trustManager instanceof X509TrustManager) {
                return (X509TrustManager) trustManager;
            }
        }
        return null;
    }

    public static X509TrustManager createTrustNoneManager() {
        X509TrustManager tm = null;
        try {
            tm =   new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    //do nothing,接受任意客户端证书
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {

                    throw new CertificateException("Local X509TrustManager Init Failed");

                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
        } catch (Exception e) {
            Log.e(TAG, "createTrustNoneManager: ", e);
        }
        return tm;
    }

    /**
     * 获得KeyStore.
     * @param type 证书类型
     * @param keyStorePath
     *            密钥库路径
     * @param password
     *            密码
     * @return 密钥库
     * @throws Exception
     */
    public static KeyStore getKeyStore(String type, String password, String keyStorePath) throws Exception {
        // 实例化密钥库
        KeyStore ks = KeyStore.getInstance(type);
        // 获得密钥库文件流
        FileInputStream is = new FileInputStream(keyStorePath);
        // 加载密钥库
        ks.load(is, password.toCharArray());
        // 关闭密钥库文件流
        is.close();
        return ks;
    }

    /**
     * 获得 JKS KeyStore.
     * @param password
     * @param keyStorePath
     * @return
     */
    public static KeyStore getJksKeyStore(String password, String keyStorePath) throws Exception {
        return getKeyStore("JKS", password, keyStorePath);
    }

    /**
     * 获得SSLSocketFactory剩下文
     * @param password 密钥库密码
     * @param keyStore key 密钥库
     * @param trustStore 信任库
     *            密钥库路径
     * @return SSLContext
     * @throws Exception
     */
    public static SSLContext getSSLContext(String password, KeyStore keyStore, KeyStore trustStore) throws Exception {
        // 实例化密钥库
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        // 初始化密钥工厂
        keyManagerFactory.init(keyStore, password.toCharArray());

        // 实例化信任库
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());

        // 初始化信任库
        trustManagerFactory.init(trustStore);
        // 实例化SSL上下文
        SSLContext ctx = SSLContext.getInstance("TLS");
        // 初始化SSL上下文
        ctx.init(keyManagerFactory.getKeyManagers(),
                trustManagerFactory.getTrustManagers(), null);
        // SSLContext, 可获得SSLSocketFactory
        return ctx;
    }

    /**
     * 获得SSLSocketFactory.
     * @param password
     *            密码
     * @param keyStorePath
     *            密钥库路径
     * @param trustStorePath
     *            信任库路径
     * @return SSLSocketFactory
     * @throws Exception
     */
    public static SSLContext getSSLContext(String password, String keyStorePath, String trustStorePath) throws Exception {
        // 获得密钥库
        KeyStore keyStore = getJksKeyStore(password, keyStorePath);
        // 获得信任库
        KeyStore trustStore = getJksKeyStore(password, trustStorePath);
        return getSSLContext(password, keyStore, trustStore);
    }

    public static SSLSocketFactory getSSLSocketFactory(String password, String keyStorePath, String trustStorePath) throws Exception {
        // 声明SSL上下文
        SSLContext sslContext = null;
        try {
            sslContext = getSSLContext(password, keyStorePath, trustStorePath);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
            Log.e(TAG, "getSSLSocketFactory: ", e);
        }
        if (sslContext != null) {
            return sslContext.getSocketFactory();
        }
        return null;
    }

    /**
     * 初始化HttpsURLConnection.
     * @param password
     *            密码
     * @param keyStorePath
     *            密钥库路径
     * @param trustStorePath
     *            信任库路径
     * @throws Exception
     */
    public static void initHttpsURLConnection(String password, String keyStorePath, String trustStorePath) throws Exception {
        // 声明SSL上下文
        SSLContext sslContext = null;
        // 实例化主机名验证接口
        SSLSocketFactory factory = getSSLSocketFactory(password, keyStorePath, trustStorePath);
        if (factory != null) {
            HttpsURLConnection.setDefaultSSLSocketFactory(factory);
        }
        // 实例化主机名验证接口
        HostnameVerifier hnv = new NoneHostnameVerifier();
        HttpsURLConnection.setDefaultHostnameVerifier(hnv);
    }
}

补充

Http请求结果 (对应服务器返回的json数据)

package com.hulk.android.http.conn;

/**
 * Http请求结果 (对应服务器返回的json数据)
 * <p> json:
 *  {"code":0,"msg":"Success","data":"This is response data"}
 *  此处是举例返回数据, 具体的返回数据还更具真实数据确定
 * @author: zhanghao
 * @Time: 2021-03-04 17:35
 */
public class HttpResult<T> {
    public int code = -1;
    public String msg;
    public String detail;
    public Throwable error;
    public T data = null;

    //TODO 此处是举例返回数据, 具体的返回数据还更具真实数据确定

    public HttpResult() {
    }

    public HttpResult(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public HttpResult(int code, String msg, String detail, Throwable error) {
        this.code = code;
        this.msg = msg;
        this.detail = detail;
        this.error = error;
    }

    public void setData(T data) {
        this.data = data;
    }
}

以上主要代码展示, 具体的代目可在参考源代码 https://download.csdn.net/download/zhanghao_Hulk/16020524 , 或者留言单独提供

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值