OKHttp3源码阅读

OKHttp牛逼之处

1.支持HTTP2/SPDY黑科技 --->okHttp中分包就分为Http1 和Http2

2.socket支持自动重连 ---> 支持自动重连是因为他有RetryAndFollowIntercepter

3.拥有自动维护的socket连接池,减少握手次数-->也就是ConnectionPool ,ConnectionPool 支持5个并发KeepAlive,默认链路生命为5分钟

//ConnectionPool 中
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES);
}

public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
  throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}

4.拥有队列线程池,轻松写并发 -->Dispatcher

5.拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)--->在AsyncCall中的execute()中会调用getResponseWithInterceptorChain()会处理这一系列的Interceptors

6.基于Headers的缓存策略-->因为有CacheInterceptor

OKHttp3中类的作用

0.Interceptor;(接口)拦截器,将请求进行拦截,并对其头文件进行修改或删除,内部有一个Chain,也是一个接口

1.CacheIntercepter:(实现了Interceptor)从缓存中查找请求或者将请求写入到缓存中.

2.CacheStrategy:(缓存策略)根据给定的条件选择 OKHttp是否使用网络请求,或者直接使用缓存或者都是用.

3.FaultHidingSink:对流操作进行了封装和try/cache,可以保证对外界不抛出IOException.

4.FileOperator:文件的读写类

5.ConnectionInterceptor:(实现了Interceptor)负责和服务器进行连接的拦截器

6.ConnectionSpecSelector:处理连接规范回退策略: 当由于握手/协议问题导致安全套接字连接失败时,可能会使用不同的协议重试连接。实例是有状态的,应该被创建并用于单个连接尝试。

7.RealConnection:真正使用Http协议去连接网络的类

8.RouteDatabase:记录创建失败的路由

9.RouteSelector:负责线路(路由)的选择和自动重连

10.StreamAllocation:主要负责Connections和Stream的资源分配和释放

11.BridgeInterceptor:(实现了Interceptor接口)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应

12.CallServerInterceptor:(实现了Interceptor接口)他是调用连中的最后一个拦截器,用于发送网络请求到服务器中

13.HttpCodec:(接口)用于编码Http请求和解码Http响应的父级接口

14.HttpDate:Http的日期解析器

15.HttpHeaders:OkHttp内部封装的请求头

16.RealInterceptorChain:实现了Interceptor中内部接口Chain的类.真正的拦截器链,调用完一个拦截器在调用下一个拦截器

17.RealResponseBody:(继承了ResponseBody) OkHTTP中对responseBody的封装

18.RetryAndFollowInterceptor:(实现了Interceptor接口)负责重试和重定向的拦截器,如果取消有可能抛出IOException异常

19.RequestLine:请求行

20.StatusLine:HTTP响应状态栏

21.HPACK:Http/2的头部压缩算法

22.Huffman:霍夫曼编码

23.Ping:本地发起的ping

24.Cache:将http和https的响应使用DiskLruCache缓存到文件夹系统中

25.CacheControl:一个缓存控制头,带有来自服务器或客户端的缓存指令。这些指令规定了哪些响应可以被存储,哪些请求可以被存储的响应满足

26.Call:(接口)请求任务的封装(一个Call只能执行一次)

27.RealCall:Call的实现类

28.CertificatePinner:约束那些证书是可信的

29.Connection:(接口)RealConnection实现了它,按照各种协议去请求网络的接口

30ConnectionPool:Socket连接池,对连接缓存进行回收和管理

31.Dispatcher:用于线程分发,它有两种方法,一个是普通的同步单线程;另一种是使用了队列进行并发任务的分发(Dispatch)与回调

32.OkHttpClient:是Call的工厂

32.Request:一个Http请求的封装,包含一个URL、一个方法(GET或POST或其他)、一些HTTP头。请求还可能包含一个特定内容类型的数据类的主体部分。

33.Response:Http响应的封装,包含状态码、HTTP头和主体部分。

35.HttpUrl:对Url的封装,包含http和https

最简单的get请求访问网络代码解析

    OkHttpClient okHttpClient = new OkHttpClient();
    Request.Builder builder = new Request.Builder();
    builder.url("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png");
    builder.method("GET", null);
    final Request request = builder.build();
    Call newCall = okHttpClient.newCall(request);
    newCall.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {

        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            boolean b = response.cacheResponse() != null;
            if (BuildConfig.DEBUG) Log.e("MainActivity", "b:" + b);

            InputStream inputStream = response.body().byteStream();
            final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mIv.setImageBitmap(bitmap);

                }
            });
        }
    });

1.首先在new OKHttpClient()的时候,会调用自己的OkHttpClient(Builder builder)这个构造方法,将一个builder传进去,Builder是OkHttpClient的静态内部类,创建Builder的时候,也就是进行了一大堆的初始化操作,然后将builder传到OKHttpClient的构造方法中,也就是将builder中初始化的参数赋值给OKHttpClient中

//Builder的构造方法
public Builder() {
  //初始化线程分发器
  dispatcher = new Dispatcher();
  //初始化协议包括,Http_1_1和Http_2
  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;
}

//OkHttpClient最终走的构造方法
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;

boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
  isTLS = isTLS || spec.isTls();
}

if (builder.sslSocketFactory != null || !isTLS) {
  this.sslSocketFactory = builder.sslSocketFactory;
  this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
  X509TrustManager trustManager = systemDefaultTrustManager();
  this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
  this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}

this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
    certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}

2.在创建Request.Builder的时候,首先在Builder的构造方法中给Builder设置了默认的请求方式为"GET"和一个空的Headers.Builder()对象

public Builder() {
  this.method = "GET";
  this.headers = new Headers.Builder();
}

3.然后我们来看看下面这两句代码

    builder.url("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png");
    builder.method("GET", null);

首先我们给builder设置了url,不过是post还是get请求url总是必不可少的,然后有设置了请求方式为"GET"我们在上面已经知道了,OkHttp默认的请求方法就是"GET"所以如果我们的请求方式是"GET"的话可以不再设置.

4.下来 Request request = builder.build(); 这句代码做的工作和OKHttpClient内部做的工作一样,就是将builder中携带的参数,赋值给Request,这也就是明显的建造者模式

public Request build() {
  if (url == null) throw new IllegalStateException("url == null");
  return new Request(this);
}

Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}

5.下来Call newCall = okHttpClient.newCall(request);这句代码是创建一个Call(一个请求的封装),在执行newCall的时候会创建一个RealCall的实例,在创建RealCall的时候,会将OKHttpClient,Request,EventListener(该次请求的监听),和请求失败以后的拦截器RetryAndFollowInterceptor()这些进行初始化,并记录在RealCall中

    @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();
    //OKHttpClient
    this.client = client;
    //一个Request
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    //请求失败后重试的拦截器
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

    // TODO(jwilson): this is unsafe publication and not threadsafe.
    //请求过程的监听器
    this.eventListener = eventListenerFactory.create(this);
    }

6.最后就是执行newCall.enqueue(new Callback() )这个方法,这个方法就是去执行请求了,然后通过CallBack将结果回调回来.在执行enqueue方法的时候首先会通过RealCall身上的executed判断该请求是否执行过了,如果执行过了就会抛异常,从这里就可以看出一个请求只能被执行一次.然后就执行了Dispatcher的enqueue()方法,传入了一个AsyncCall对象,AsyncCall 是继承了NamedRunnable类,NamedRunnable又继承了Runnable,所以说AsyncCall就是一个线程.

@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
  if (executed) throw new IllegalStateException("Already Executed");
  executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

在Despatcher的enqueue()方法中首先会判断目前正在执行的请求数如果不大于最大请求数(64个)并且将要执行的这个请求的主机名 和正在执行的请求的主机名相同的请求数不超过5,就将该请求执行并且添加都正在执行请求的队列中,否则就将该请求添加到等待执行的队列中.

synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
  } else {
    readyAsyncCalls.add(call);
  }
 }

在执行executorService()这个方法的时候会返回一个线程池,也就是OKHttp访问服务器的线程池,添加到线程池中以后就标志着这个请求将要被执行.我们可以很明显的看出,这个线程池是没有核心线程数的,而且最大线程是int的最大值(也就是灰常大),线程在空闲60秒后也就是一分钟后销毁.

public synchronized ExecutorService executorService() {
if (executorService == null) {
  executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
      new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}

该请求被执行的话肯定会走自己的run()方法,所以我们来看看AsyncCall的run方法,run方法中只执行了execute()方法,而且该方法是抽象的,所以我们就来看看AsyncCall的execute方法.

@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
  execute();
} finally {
  Thread.currentThread().setName(oldName);
}
}

在execute()中首先会执行getResponseWithInterceptorChain()在这个方法中会将之前各个地方的拦截器全部统一撞到一个集合中,然后创建一个RealInterceptorChain,并将这个拦截器的集合作为参数穿进去,最后会执行RealInterceptorChain的Proceed方法去执行各个Interceptor然后最后获得一个Response,这里是真正访问网络的地方.合适最核心的地方,下面 我们来具体看看到底是怎么弄得.

Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
  interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(
    interceptors, null, null, null, 0, originalRequest);
//开始递归执行每一个Interceptor
return chain.proceed(originalRequest);
 }

按照Interceptor添加的顺序首先会执行执行OKHttpClient配置的Interceptor,但也有可能没有配置,所以智力我们先不管.

下来是RetryAndFollowUpInterceptor,RetryAndFollowUpInterceptor类中我们主要看他的intercept方法,该方法中主要是对请求失败的请求进行重试

再下来是BridgeInterceptor,BridgeInterceptor中的intercept方法主要是使用Request.Builder将用户创建的Request组装成真正发给服务器的请求包,也是用Request.Builder将服务器返回的结果封装成,我们需要的Response

再下来就是CacheInterceptor,CacheInterceptor中的intercept方法主要是到缓存中找,是否有已经有缓存而且没有过期就直接使用缓存中的Response,如果没有的话就进行下一个Interceptor

下一个Interceptor是ConnectInterceptor,ConnectInterceptor的intercept方法中主要是和服务器进行链接

最后一个Interceptor就是CallServerInterceptor,它主要是将请求提交给服务器,然后将结果进行返回.

最后我们来总结一下这些Interceptors的执行顺序和各自的功能

image.png

7.在getResponseWithInterceptorChain执行完以后就会返回一个Response到RealCall的execute方法中,然后使用我们调用层传入的Callback将结果回调到调用层,因为这里没有做线程的操作,所以onFailure()和onResponse()都是运行在子线程中的

@Override protected void execute() {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain();
    if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    } else {
      signalledCallback = true;
      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 {
    client.dispatcher().finished(this);
  }
}

总体流程

(图1)

OKHttp访问网络流程.png

(图2)

image.png

细节解析

1.OKHttp访问网络的线程池是如何构建的?
OKHttp的线程池没有核心线程,而且最大线程是Int的最大值.在空闲60s后自动销毁,而且使用的是SynchronousQueue这个不储存元素的阻塞队列,也就是说来一个任务就会来一个线程.

//Dispatcher中
public synchronized ExecutorService executorService() {
if (executorService == null) {
  executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
  new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}

2,Dispatcher线程池总结

1)调度线程池Disptcher实现了高并发,低阻塞的实现

public synchronized ExecutorService executorService() {
if (executorService == null) {
  executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
  new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}

2)采用Deque作为缓存,先进先出的顺序执行

/** 准备执行的请求 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/** 正在执行的异步请求,包含已经取消但未执行完的请求 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/** 正在执行的同步请求,包含已经取消单未执行完的请求 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

3)任务在try/finally中调用了finished函数,控制任务队列的执行顺序,而不是采用锁,减少了编码复杂性提高性能

//RealCall的AsyncCall中
@Override protected void execute() {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain();
    if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    } else {
      signalledCallback = true;
      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 {
    client.dispatcher().finished(this);
  }
}

3.OKHttp中再执行Interceptor这块使用了责任链模式,有点像view中的事件传递分发

4.OKHttp是直接使用socket和服务器进行通信的

5.OKHttp需要打印日志的话 需要添加日志拦截器 如下:

compile 'com.squareup.okhttp3:logging-interceptor:3.1.2'

参考文章

http://blog.csdn.net/mwq384807683/article/details/71173442?locationNum=8&fps=1

http://www.jianshu.com/p/aad5aacd79bf

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容