首先为何要写这篇博客,主要是由于如今网络用的都是okhttp3,因此在面试的时候,都会问一下okhttp的原理,而网上的原理,也看了下,要么太简短,核心的一笔带过,要么长篇大伦,看着蒙圈。因此想看看能不能用最简短明白的方式来解释下okhttp3的原理。 固然,若是还不是很熟悉这个框架的小朋友,能够点这里一文了解OKHttp3全(使用篇)
java
来,这就是简单的一个get请求,我们按照顺序从1,2,3,4这几点开始分析。首先先分析第1点。web
首先要想使用网络请求的话,得先初始化它,先看看有哪些属性。面试
文件位置:OkHttpClient.java设计模式
final Dispatcher dispatcher;//调度器
final Proxy proxy;//代理
final List<Protocol> protocols;//协议
final List<ConnectionSpec> connectionSpecs;//传输层版本和链接协议
final List<Interceptor> interceptors;//拦截器
final List<Interceptor> networkInterceptors;//网络拦截器
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;//代理选择器
final CookieJar cookieJar;//cookie
final Cache cache;//cache 缓存
final InternalCache internalCache;//内部缓存
final SocketFactory socketFactory;//socket 工厂
final SSLSocketFactory sslSocketFactory;//安全套层socket工厂 用于https
final CertificateChainCleaner certificateChainCleaner;//验证确认响应书,适用HTTPS 请求链接的主机名
final HostnameVerifier hostnameVerifier;//主机名字确认
final CertificatePinner certificatePinner;//证书链
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;//写入超时
复制代码
很好,属性看完了,就这些,基本也够了,可是尼,怎么赋值是个问题,总不能都写在构造函数里面吧?有什么好的设计模式尼?就是那种将使用和复杂的构建相分离的那种?恭喜你答对了,就是构建者模式。缓存
果真,okhttp3用的也是它。看下源码吧安全
public static final class Builder {
...
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
...
}
复制代码
这就简单了,都设置了默认值,那么咱们在外边调用的时候就很简单了,只须要这么写就能够了。服务器
OkHttpClient mClient = new OkHttpClient.Builder() // 构建者模式,建立实例
.build();
复制代码
OK,第1步完事了cookie
第 1 步就是用构建者模式建立okhttp3的实例,里面封装了一些使用中必要的属性,如超时时间,拦截器等网络
这是第2步,说白了,第1步是建立okhttp3的实例,那么第2步,就是建立请求信息。 废话少说,先看下它有哪些属性。并发
文件位置:Request
final HttpUrl url; // 接口地址
final String method; // post仍是get
final Headers headers; // Http消息的头字段
final RequestBody body; // 它是抽象类, 有些请求须要咱们传入body实例,若是是GET请求,body对象传的是null,若是是POST请求,就须要咱们去设定了。
final Object tag;
复制代码
好了,若是你想简单省事的去用,自动设置默认值,那就继续咱们的构建者模式吧
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
复制代码
其实也没省啥事,无非就是你直接用的时候,给你默认了个Get而已。 不过既然封装了,咱们就能够这么调用了
Request mRequest = new Request.Builder() // 构建者模式,建立请求信息
.url("https://www.baidu.com")
.build();
复制代码
OK,第2步完事了
第 2 步就是用构建者模式建立请求信息的实例,里面封装了一些使用中必要的属性,如请求方式,请求头信息,接口地址等
好了,继续看咱们的第3步 首先尼,根据okHttpClient和Request对象,咱们就能构建出实际进行请求的call对象。看源码 咱们得知,call是个接口,实际进行请求的是RealCall,来咱们建立它
文件位置:OkHttpClient.java
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
复制代码
看看它作了什么
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client; // 第1步建立的okHttpClient实例
this.originalRequest = originalRequest; // 第2步建立的request实例
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // 重定向拦截器,后面会说
// TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}
复制代码
好了,第3步就完事了
第 3 步就是建立realcall对象,真正的请求是交给了 RealCall 类,它实现了Call方法,它是真正的核心代码。
开始分析最后1步 这块是关键,咱们一步一步的来。首先看咱们的调用
call.enqueue(new Callback(){ ... });
复制代码
而后看,realcall.enqueue()方法
@Override
public void enqueue(Callback responseCallback) {
1.synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
2.client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
复制代码
先看下new AsyncCall(responseCallback)
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
}
复制代码
对咱们的回调进行封装,继承Runnable接口 好,再继续看client.dispatcher().enqueue()
文件位置:Dispatcher.java
synchronized void enqueue(AsyncCall call) {
1.if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
2.runningAsyncCalls.add(call);
3.executorService().execute(call);
} else {
4.readyAsyncCalls.add(call);
}
}
复制代码
在call.enqueue(new Callback(){ ... }) 执行以后,首先作的是
1. 调用RealCall.call.enqueue()方法,判断当前call是否已经被执行过了,被执行过了,就抛出异常,若是没有执行过,就先将callback封装成AsyncCall,而后调用dispatcher.enqueue()方法(dispatcher调度器,在okHttpClient里面建立的)
2. 在dispatcher.enqueue()方法中,判断当前正在执行的请求数及当前网络请求的主机数是否超过了最大值。要是超过了最大值,就将请求放到等待队列中,要是没超过,就放当正在执行的队列中,而后调用线程池执行它。
吃过了午餐,继续分析源码,承接上文,咱们分析到了,若是符合条件,就用线程池去执行它,也就是这句
executorService().execute(call);
复制代码
看一下咱们的线程池
public synchronized ExecutorService executorService() {
if (executorService == null) {
1.executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
复制代码
核心线程 最大线程 非核心线程闲置60秒回收,固然会有人提出疑问,最大值设置为Integer.MAX_VALUE,会不会对性能形成影响呀?答案是不会的,由于尼,在前面的dispatcher.enqueue()方法中,已经对请求数作了限制,超过设置的最大请求数,会被放到等待队列里面。
好,继续再看下线程池executorService.execute(call)方法 它会执行里面call方法的run()方法,也就是AsyncCall的run方法,这个run方法其实是在它的父类NamedRunnable里面。在NamedRunnable.run()方法里面,其实是调用了execute(),该方法由子类实现,也就是调用了AsyncCall.execute()
简单来讲,就是executorService.execute(call) -> NamedRunnable.run() -> AsyncCall.execute() 看到这个写法,心里中就想吐槽一句话,真鸡儿秀! 来继续看下,AsyncCall.execute()
看源码 文件位置:realcall.java
@Override protected void execute() {
boolean signalledCallback = false;
try {
1.Response response = getResponseWithInterceptorChain();
2.if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
3.responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
4.client.dispatcher().finished(this);
}
}
复制代码
看源码
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
1.if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
2.if (promoteCalls) promoteCalls();
3.runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
复制代码
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
复制代码
很简单,这里无非就是遍历等待队列中的请求,而后加入到执行请求队列中,直到并发数和当前网络请求的主机数达到上限。
至此,okhttp的异步已经分析完毕了
面试题 1.什么是dispatcher? dispatcher做用是为维护请求的状态,并维护一个线程池。用于执行请求。
小伙子,是否是很简单呀?是否是已经完了?你想多了,来分析最核心的一部分
先看源码
Response getResponseWithInterceptorChain() throws IOException {
// 添加拦截器,责任链模式
List<Interceptor> interceptors = new ArrayList<>();
// 在配置okhttpClient 时设置的intercept 由用户本身设置
interceptors.addAll(client.interceptors());
// 负责处理失败后的重试与重定向
interceptors.add(retryAndFollowUpInterceptor);
/** 负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应 处理 配置请求头等信息. 从应用程序代码到网络代码的桥梁。首先,它根据用户请求构建网络请求。而后它继续呼叫网络。最后,它根据网络响应构建用户响应。 */
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应
// 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改)
// 可配置用户本身设置的缓存拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
// 链接服务器 负责和服务器创建链接 这里才是真正的请求网络
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
// 执行流操做(写出请求体、得到响应数据) 负责向服务器发送请求数据、从服务器读取响应数据
// 进行http请求报文的封装与请求报文的解析
interceptors.add(new CallServerInterceptor(forWebSocket));
// 责任链,将上述的拦截器添加到责任链里面
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
复制代码
这里用的是责任链模式,不了解的朋友能够先了解下 这段代码其实很简单,就是将一些拦截器,都装到一个集合里面,而后将拦截器的集合当作构造参数,建立了个对象(RealInterceptorChain),最后调用它的proceed方法。而后一直往下调用。
原本想再写写拦截器这边的逻辑,可是尼,老夫掐指一算,就知道大家这块容易看懵逼,那么我们本身写一个吧
1.先定义接口
public interface Interceptor {
interface Chain{
String request();
String proceed(String request);
}
}
复制代码
2.来定义一个RetryAndFollowInterceptor(重定向拦截器)
public class RetryAndFollowInterceptor implements Interceptor{
@Override
public String interceptor(Chain chain) {
System.out.println("RetryAndFollowInterceptor_start");
String response = chain.proceed(chain.request());
System.out.println("RetryAndFollowInterceptor_start");
return response;
}
}
复制代码
2.再定义一个BridgeInterceptor(桥接拦截器)
public class BridgeInterceptor implements Interceptor{
@Override
public String interceptor(Chain chain) {
System.out.println("BridgeInterceptor_start");
String response = chain.proceed(chain.request());
System.out.println("BridgeInterceptor_end");
return response;
}
}
复制代码
3.最后一个拦截器CallServerInterceptor
public class CallServerInterceptor implements Interceptor {
@Override
public String interceptor(Chain chain) {
System.out.println("CallServerInterceptor_start");
System.out.println("----------将数据传到服务器端:数据为"+ chain.request());
System.out.println("CallServerInterceptor_end");
return "登录成功";
}
}
复制代码
4.来,定义个责任链RealInterceptorChain对象
public class RealInterceptorChain implements Interceptor.Chain{
private List<Interceptor> interceptors;
private int index;
private String request;
public RealInterceptorChain(List<Interceptor> interceptors, int index, String request) {
this.interceptors = interceptors;
this.index = index;
this.request = request;
}
@Override
public String request() {
return request;
}
@Override
public String proceed(String request) {
if(index >= interceptors.size()) {
return null;
}
// 这里就是责任链模式,它会把它的index+1 而后再建立一个RealInterceptorChain对象
RealInterceptorChain next = new RealInterceptorChain(interceptors, index + 1, request);
Interceptor Interceptor = interceptors.get(index);
return Interceptor.interceptor(next);
}
}
复制代码
5.调用一下
public static void main(String[] args) {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.add(new RetryAndFollowInterceptor());
interceptors.add(new BridgeInterceptor());
interceptors.add(new CallServerInterceptor());
RealInterceptorChain chain = new RealInterceptorChain(interceptors, 0, "");
String result = chain.proceed("xiaoming, 123");
System.out.println("----------服务器返回的结果是:"+result);
}
复制代码
6.结果
看明白了吗?若是不明白,这个图应该明白了吧。
是否是感受okhttp很简单?拦截器还没介绍尼,来最后介绍下拦截器。
一、用户自定义的拦截器 用在与服务器创建连接以前进行拦截
interceptors.addAll(client.interceptors());
复制代码
二、RetryAndFollowUpInterceptor重试或失败重定向拦截器
interceptors.add(retryAndFollowUpInterceptor);
复制代码
三、BridgeInterceptor 校接和适配拦截器,主要补充用户建立请求当中的一些请求头Content-Type
interceptors.add(new BridgeInterceptor(client.cookieJar()));
复制代码
四、CacheInterceptor主要处理缓存
interceptors.add(new CacheInterceptor(client.internalCache()));
复制代码
五、ConnectInterceptor 与服务器建立连接,建立能够用的RealConnection(对java.io和java.nio进行了封装)
interceptors.add(new ConnectInterceptor(client));
复制代码
六、用户自定义的拦截器 用在与服务器创建连接以后进行拦截。只有非socket进行设置
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
复制代码
七、CallServerInterceptor 向服务器发送请求和接收数据。将请求写入IO流,再从IO流中读取响应数据
interceptors.add(new CallServerInterceptor(forWebSocket));
复制代码
这里只是面试的时候简写,如需详细,请底下留言。