最近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;}@Overridepublic 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包装成我们的ProgressResponseBodyreturn 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);}}@Overridepublic MediaType contentType() {return responseBody.contentType();}@Overridepublic long contentLength() {return responseBody.contentLength();}@Overridepublic 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;@Overridepublic 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() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType)throws CertificateException {//do nothing,接受任意客户端证书}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {throw new CertificateException("Local X509TrustManager Init Failed");}@Overridepublic 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, 可获得SSLSocketFactoryreturn 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 , 或者留言单独提供

Android/Java中okhttp用法介绍相关推荐

  1. Java中BigDecimal类介绍及用法

    Java中BigDecimal类介绍及用法 Java中提供了大数字(超过16位有效位)的操作类,即 java.math.BinInteger 类和 java.math.BigDecimal 类,用于高 ...

  2. java中extends用法_JAVA的extends用法

    理解继承是理解面向对象程序设计的关键.在Java中,通过关键字extends继承一个已有的类,被继承的类称为父类(超类,基类),新的类称为子类(派生类).在Java中不允许多继承. (1)继承 cla ...

  3. java中switch用法举例范围_Java中Switch用法代码示例

    一.java当中的switch与C#相比有以下区别 注:在java中switch后的表达式的类型只能为以下几种:byte.short.char.int(在Java1.6中是这样), 在java1.7后 ...

  4. java中import用法

    java中import用法 单类型导入(single-type-import),例如import java.io.File; 按需类型导入(type-import-on-demand),例如 impo ...

  5. Android+Java中使用Aes对称加密的工具类与使用

    场景 Android+Java中使用RSA加密实现接口调用时的校验功能: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/11146 ...

  6. java中ThreadPool的介绍和使用

    文章目录 Thread Pool简介 Executors, Executor 和 ExecutorService ThreadPoolExecutor ScheduledThreadPoolExecu ...

  7. javascript中in用法介绍

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. java中continue用法

    java中continue用法 有时强迫一个循环提早反复是有用的,也就是,你可能想要继续运行循环,但是要忽略这次重复剩余的循环体的语句,所以 java提供了 continue 语句.continue ...

  9. 数据库 SQLServer中GUID用法介绍

    数据库 SQLServer中GUID用法介绍 1.NEWID用法 NEWID()作用是生成无顺序的GUID字符串.用法如下: SELECT NEWID() --生成36位的GUID SELECT RE ...

最新文章

  1. 单文件浏览器_图文并茂深度解析浏览器渲染原理,包看懂超值得收藏
  2. 基于xgboost 的贷款风险预测
  3. RIPv1 与 RIPv2 基础配置
  4. 经典C语言程序100例之九九
  5. Elasticsearch 集群平衡配置
  6. GetCurrentProcessID、OpenProcessToken、LookupPrivilegeValue、AdjustTokenPrivileges
  7. go 使用sarama写入kafka数据时间戳问题
  8. 计算广告 读书笔记 计算广告的核心问题
  9. 五子棋java百度贴吧_五子棋单机版
  10. MySQL抽稀_Android GPS定位轨迹抽稀之道格拉斯-普克(Douglas-Peuker)算法详解
  11. c语言上机作业五套含答案,计算机二级C语言上机题库100套(含答案)
  12. java注解拦截_轻松实现java拦截器+自定义注解
  13. 怎么在电脑上录制qq音乐
  14. 找不到任何设备驱动程序.请确保安装介质_Windows 10 1909全新安装终极指南
  15. Java服务端支付宝对接(详细)
  16. 百度地图多点路线规划_期待已久的多地点路线规划功能
  17. 既是计算机高手,也是情书高手,原来王小波才是最会撩妹的程序员
  18. mac 查看本机的IP地址
  19. 达观数据中标国信证券文档智能审阅项目
  20. 只允许电脑连接指定WIFI

热门文章

  1. c语言辅音字符怎么表示,C 语言实例 - 判断元音/辅音
  2. GIC检测中断的流程
  3. 十大人文科技类图书(转)
  4. 简述计算机视觉在各领域中的成功应用,计算机视觉技术在茶叶领域中的应用现状及展望...
  5. 白帽子讲Web安全学习之浏览器
  6. 使用lux(annie)下载视频
  7. bootstrap-datetimepicker时间控件
  8. 毒品犯罪论文的英文文献去哪找?
  9. Python画正方体
  10. Quartus无法选择器件库