转载请注明本文出自maplejaw的博客(http://blog.csdn.net/maplejaw_)
开源库地址:https://github.com/square/okhttp
解读版本:3.4.1
OkHttp是目前非常流行的网络请求库,出自Square公司。对于该库的使用,相信大家已经比较熟悉了。今天,我将从源码角度对OkHttp3进行剖析。
基本使用
Okhttp的使用可以分为四步:
初始化客户端(实际应用中应当保持单例)
//获取一个客户端 OkHttpClient client = new OkHttpClient.Builder() ... .build();
构建Request
//构建一个Request Request request = new Request.Builder() .url(url) .build();
获取Call对象
//获取Call对象 Call call=client.newCall(request);
发送请求(execute同步/enqueue异步)
//同步调用 Response response = call.execute(); //异步调用 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
当然,在使用类似POST等可以设置请求体的请求方法时,我们还可以如下构建请求体。
构建字符串,字节,文件请求体:
public static final MediaType TEXT = MediaType.parse("text/plain; charset=utf-8");
public static final MediaType STREAM = MediaType.parse("application/octet-stream");
//构建字符串请求体
RequestBody body1 = RequestBody.create(TEXT, string);
//构建字节请求体
RequestBody body2 = RequestBody.create(STREAM, byte);
//构建文件请求体
RequestBody body3 = RequestBody.create(STREAM, file);
//将请求体设置给请求方法内
Request request = new Request.Builder()
.url(url)
.post(xx)// xx表示body1,body2,body3中的某一个
.build();
构建表单请求体:
//构建表单RequestBody
RequestBody formBody=new FormBody.Builder()
.add("name","maplejaw")
.add("age","18")
...
.build();
构建分块表单请求体:
public static final MediaType STREAM = MediaType.parse("application/octet-stream");
//构建表单RequestBody
RequestBody multipartBody=new MultipartBody.Builder()
.setType(MultipartBody.FORM)//指明为 multipart/form-data 类型
.addFormDataPart("name","maplejaw") //添加表单数据
.addFormDataPart("age","20") //添加表单数据
.addFormDataPart("avatar","111.jpg",RequestBody.create(STREAM,file)) //添加文件,其中avatar为表单名,111.jpg为文件名。
.addPart(..)//该方法用于添加自定义Part,一般来说以上已经够用
.build();
关于Okhttp的基本使用已经介绍完毕,在大多数情况下,只要掌握以上使用方法,就足以应付关于网络请求的日常使用。
接下来,将从源码角度剖析OkHttp这个网络框架,如果你到目前为止还弄不清请求行、状态行、请求头、响应头、请求体和响应体这些基本概念的话,建议先阅读你应该知道的HTTP基础知识这篇文章。
源码解读
初始化OkHttpClient
所谓初始化OkHttpClient,无非就是对其进行相关配置,在了解OkHttpClient
相关配置前,先认识一下以下一些基本的类。
Proxy
代理类,默认有三种代理模式DIRECT(直连),HTTP(http代理),SOCKS(socks代理),这三种模式,折腾过科学上网的或多或少都了解一点吧。
ProxySelector
代理选择类,默认不使用代理,即使用直连方式,当然,我们可以自定义配置,以指定URI使用某种代理,类似代理软件的PAC功能。
Protocol
协议类,用来表示使用的协议版本,比如http/1.0
,http/1.1
,spdy/3.1
,h2
等
Dns
DNS这里就不用介绍了,用于根据主机名来查询对应的IP。
Cache
缓存类,内部使用了DiskLruCache来进行管理缓存,匹配缓存的机制不仅仅是根据url,而且会根据请求方法和请求头来验证是否可以响应缓存。此外,仅支持GET请求的缓存。
ConnectionSpec
连接规范,用于配置Socket连接层。对于HTTPS,还能配置安全传输层协议(TLS)版本和密码套件(CipherSuite)
Interceptor
拦截器,该类的功能还是比较强大的,通过拦截器可以监视、重写和重试请求。拦截器的源码如下:
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
拦截器的使用也非常简单,如果你只是想修改Request,那么就通过chain.request()
获取原始的Request然后进行修改,比如添加cookie,代理等请求头,甚至还能修改请求方法和请求体。同理如果需要修改Response,则可以通过chain.proceed
来获取Response后进行修改。此外我们还可以在其中进行打印日志等其他监视行为。
关于拦截器的使用例子如下:
//通过addInterceptor添加拦截器
OkHttpClient client = new OkHttpClient.Builder()
...
.addInterceptor(new MyInterceptor())
.build();
//自定义拦截器
class MyInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
//获取原始Request
Request request = chain.request();
//构建新的Request
Request newRequest=request.newBuilder()//使用newBuilder,在原来request基础上修改,当然如果暴力点,可以完全重写Request。
.header("User-Agent", "OkHttp Example")
...
.build();
//获取Response
Response response = chain.proceed(newRequest);
//构建新的Response
Response newResponse=response.newBuilder()
.header("Cache-Control", "max-age=60")
....
.build();
return newResponse;
}
}
CookieJar
用来管理cookie,可以根据url保存cookie,也可以通过url取出相应cookie。默认的不做cookie管理。该接口中有两个抽象方法,用户可以自己实现该接口以对cookie进行管理。
//保存cookie
void saveFromResponse(HttpUrl url, List<Cookie> cookies);
//根据Url导入保存的Cookie
List<Cookie> loadForRequest(HttpUrl url);
SocketFactory
Socket工厂,通过createSocket
来创建Socket。
SSLSocketFactory
安全套接层工厂,HTTPS相关,用于创建SSLSocket。一般配置HTTPS证书信任问题都需要从这里着手。对于不受信任的证书一般会提示javax.net.ssl.SSLHandshakeException
异常。配置信任所有证书的源码如下:
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(getTrustAllSSLSocketFactory())//配置SSL工厂
.build();
//获取信任所有证书的SSLSocketFactory
public static SSLSocketFactory getTrustAllSSLSocketFactory() {
// 信任所有证书
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, null);
return sslContext.getSocketFactory();
} catch (Throwable ex) {
}
return null;
}
对于信任自证书的配置问题,可以参考Android Https相关完全解析 当OkHttp遇到Https。这篇文章。
CertificateChainCleaner
证书链清洁器,HTTPS相关,用于从Java的TLS API构建的原始数组中统计有效的证书链,然后清除跟TLS握手不相关的证书,提取可信任的证书以便可以受益于证书锁机制。
HostnameVerifier
主机名验证器,与HTTPS中的SSL相关,当握手时如果URL的主机名不是可识别的主机,就会要求进行主机名验证。
public interface HostnameVerifier {
//通过session验证指定的主机名是否被允许
boolean verify(String hostname, SSLSession session);
}
CertificatePinner
证书锁