前言
okhttp是一个网络请求框架,也是目前市面上使用最多的网络框架之一。
之前参与的项目一直没使用,这次刚好有个机会使用OKHttp,记录下。
使用笔记
okhttp git地址: https://github.com/square/okhttp
okhttp 相关文档介绍:https://square.github.io/okhttp/https/
okhttp 使用如果没有特殊需求,使用相对简单,如下:
1. Get a URL
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
2. Post to a Server
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
okhttp 使用大体步骤可以分为以下3步:
- 创建一个 OkHttp 的实例例
- 创建 Request
- 创建 Call 并发起⽹网络请求
tips: execute() 为同步方法, Android中常用的enqueue()在异步线程发起网络请求
源码分析
OkHttpClient
包含整个网络请求的配置,配置信息如下:
static final List<Protocol> DEFAULT_PROTOCOLS;
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS;
final Dispatcher dispatcher; // 任务分发类,用于调度后台发起的⽹网络请求,控制线程的分发和性能平衡(有后台总请求数和 单主机总请求数的控制)
@Nullable
final Proxy proxy; // 代理设置
final List<Protocol> protocols; // ⽀持的应⽤层协议,例如: HTTP/1.1、HTTP/2 等
final List<ConnectionSpec> connectionSpecs; //应用层支持的 Socket 设置,使⽤明文传输(⽤于 HTTP)还是某个版本的 TLS(用于 HTTPS)
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final okhttp3.EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
@Nullable
final Cache cache;
@Nullable
final InternalCache internalCache;
final SocketFactory socketFactory;
@Nullable
final SSLSocketFactory sslSocketFactory;
@Nullable
final CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier; // 用于验证 HTTPS 握手过程中下载到的证书所属者是否和⾃己要访问的主机名⼀致
final CertificatePinner certificatePinner; // HTTPS校验时,通过验证证书公钥来判断连接是否可用
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int connectTimeout;
final int readTimeout; // 发送请求到读响应数据的超时时间
final int writeTimeout; // 发送请求到目标服务器接受的超时时间
final int pingInterval;
newCall(Request)
: 是发起网络请求的入口函数。返回一个RealCall
对象,它是 Call
接⼝的实现. 当调用enqueue()
方法时, 会调用Dispatcher
分配到线程中,把请求放到后台
enqueue(Callback)
: 是RealCall
中的函数。 Android 常用enqueue(Callback)
来发起异步的网络请求。 异步请求最后会调用AsyncCall
中的execute()
protected void execute() {
boolean var1 = false;
try {
Response var2 = RealCall.this.getResponseWithInterceptorChain();
if (RealCall.this.retryAndFollowUpInterceptor.isCanceled()) {
var1 = true;
this.responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
var1 = true;
this.responseCallback.onResponse(RealCall.this, var2);
}
} catch (IOException var6) {
if (var1) {
Platform.get().log(4, "Callback failure for " + RealCall.this.toLoggableString(), var6);
} else {
RealCall.this.eventListener.callFailed(RealCall.this, var6);
this.responseCallback.onFailure(RealCall.this, var6);
}
} finally {
RealCall.this.client.dispatcher().finished(this);
}
}
//getResponseWithInterceptorChain
// 把所有配置好的 Interceptor 放在 ⼀个 List 中,然后作为参数,创建一个 RealInterceptorChain 对象,并调⽤用 chain.proceed(request) 来发起请求和获取响应
okhttp实现https请求
参考连接:https://square.github.io/okhttp/https/
HTTPS是包含了HTTP协议及SSL /TLS协议这两部分内容,简单的理解就是基于SSL/TLS进行HTTP的加密传输。
所以https请求会有证书相关的验证。
1. 使用CA颁发的证书
okhttp默认情况下是支持https协议的网站的,例如https://www.baidu.com
2. 使用自签名证书
需要自己处理证书校验。( 可以选择信任所有证书 或者 自定义证书校验 )
a. 信任所有证书(处理比较粗暴,忽略安全问题,不建议使用)
处理方式可以google查询。有很多介绍
b. 证书校验
固定证书校验:
private final OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(
new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.build())
.build();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/robots.txt")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
for (Certificate certificate : response.handshake().peerCertificates()) {
System.out.println(CertificatePinner.pin(certificate));
}
}
}
自定义证书校验
private final OkHttpClient client;
public CustomTrust() {
X509TrustManager trustManager;
SSLSocketFactory sslSocketFactory;
try {
trustManager = trustManagerForCertificates(trustedCertificatesInputStream());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());
}
private InputStream trustedCertificatesInputStream() {
... // Full source omitted. See sample.
}
public SSLContext sslContextForTrustedCertificates(InputStream in) {
... // Full source omitted. See sample.
}
域名校验
/**
* 实现HostnameVerifier接口
*/
public class TrustAllHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
// demo是信任素有证书,可根据具校验修改
return true;
}
}
网友评论