Android八门神器(一): OkHttp框架源码解析
http是我们交换数据和媒体流的现代应用网络,有效利用http可以使我们节省带宽和更快地加载数据,square公司开源的okhttp网络请求是有效率的http客户端。之前的知识面仅限于框架api的调用,接触到实际的工作之后深知自己知识的不足,故而深挖框架源码尽力吸取前辈的设计经验。关于此框架的源码解析网上的教程多不胜数,此文名为源码解析,实则是炒冷饭之作,如有错误之处还望各位看官指出。
拦截器
拦截器是okhttp框架设计的精髓所在,拦截器所定义的是request的所通过的责任链而不管request的具体执行过程,并且可以让开发人员自定义自己的拦截器功能并且插入到责任链中
用户自定义的拦截器位于 okhttpclient.addinterceptor() 添加到interceptors责任链中
-
realcall.execute()执行的时候调用realcall.getresponsewithinterceptorchain()将 来自 okhttpclient的interceptors以及默认的拦截器一并加入到realinterceptorchain责任链中并调用, 代码并没有对originalrequest进行封装, interceptorchain和originalrequest一并流转到 realinterceptorchain类中处理
custominterceptor retryandfollowupinterceptor bridgeinterceptor cacheinterceptor connectinterceptor networkinterceptors callserverinterceptor
realinterceptorchain.proceed()
eventlistener.callstart()也是在realcall.execute()嵌入到request调用过程, eventlistener.callend()位于streamallocation中调用
-
request.builder
- url (string/url/httpurl)
- header
- cachecontrol
- tag (use this api to attach timing, debugging, or other application data to a request so that you may read it in interceptors, event listeners, or callbacks.)
bridgeinterceptor
bridges from application code to network code. first it builds a network request from a user request. then it proceeds to call the network. finally it builds a user response from the network response.
此拦截器是应用码到网络码的桥接。它会将用户请求封装成一个网络请求并且执行请求,同时它还完成从网络响应到用户响应的转化. 最后chain.proceed() 方法启动拦截器责任链, realinterceptorchain中通过递归调用将网络请求以及响应的任务分别分配到各个拦截器中, 然后通过responsebuilder.build()方法将网络响应封装, 然后递归调用责任链模式使得调用以及response处理的过程可以一并写入bridgeinterceptor中
public final class realinterceptorchain implements interceptor.chain { public response proceed(request request, streamallocation streamallocation, httpcodec httpcodec, realconnection connection) throws ioexception { if (index >= interceptors.size()) throw new assertionerror(); calls++; ... // call the next interceptor in the chain. realinterceptorchain next = new realinterceptorchain(interceptors, streamallocation, httpcodec,connection, index + 1, request, call, eventlistener, connecttimeout, readtimeout,writetimeout); interceptor interceptor = interceptors.get(index); response response = interceptor.intercept(next); ... return response; } }
callserverinterceptor
interceptor的逻辑均在intercept()方法中实现, 在通过chain实体类获取到请求主题之后,通过bufferedsink接口将请求转发到okio接口,在拦截过程中通过eventlistener接口将拦截器处理状态(主要是requestbodystart和requestbodyend两个状态)发送出去
public final class callserviceinterceptor implements interceptor { @override public response intercept(chain chain) throws ioexception { response.builder responsebuilder = null; if (httpmethod.permitsrequestbody(request.method()) && request.body() != null) { // if there's a "expect: 100-continue" header on the request, wait for a "http/1.1 100 // continue" response before transmitting the request body. if we don't get that, return // what we did get (such as a 4xx response) without ever transmitting the request body. if ("100-continue".equalsignorecase(request.header("expect"))) { httpcodec.flushrequest(); realchain.eventlistener().responseheadersstart(realchain.call()); responsebuilder = httpcodec.readresponseheaders(true); } if (responsebuilder == null) { // write the request body if the "expect: 100-continue" expectation was met. realchain.eventlistener().requestbodystart(realchain.call()); long contentlength = request.body().contentlength(); countingsink requestbodyout = new countingsink(httpcodec.createrequestbody(request, contentlength)); bufferedsink bufferedrequestbody = okio.buffer(requestbodyout); request.body().writeto(bufferedrequestbody); bufferedrequestbody.close(); realchain.eventlistener() .requestbodyend(realchain.call(), requestbodyout.successfulcount); } else if (!connection.ismultiplexed()) { // if the "expect: 100-continue" expectation wasn't met, prevent the http/1 connection // from being reused. otherwise we're still obligated to transmit the request body to // leave the connection in a consistent state. streamallocation.nonewstreams(); } } } }
cacheinterceptor
dispatcher
dispatcher(分离器或者复用器)是异步网络请求调用时执行的策略方法, 复用器的概念十分常见,它主要的作用是输入的各路信号进行卷积运算,最大可能压榨通信的带宽,提高信息传输的效率。 okhttp在每个分离器使用一个executorservice内部调用请求, dispatcher内部主要并不涉及执行的具体。
synchronized void enqueue(asynccall call) { if (runningasynccalls.size() < maxrequests && runningcallsforhost(call) < maxrequestsperhost) { runningasynccalls.add(call); executorservice().execute(call); } else { readyasynccalls.add(call); } } ... /** used by {@code call#execute} to signal it is in-flight. */ synchronized void executed(realcall call) { runningsynccalls.add(call); }
executorsevice.execute(asynccall)执行代码位于asynccall内部复写的execute()方法, 方法内定义一些callback回调节点运行逻辑,包括用户主动取消执行(使用retryandfollowupinterceptor)以及执行请求成功或者失败时的回调方法
final class asynccall extends namedrunnable { ... @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 { eventlistener.callfailed(realcall.this, e); responsecallback.onfailure(realcall.this, e); } } finally { client.dispatcher().finished(this); } } }
-
created lazily 成员
- executorservice
- cachecontrol
websocket
-
websocket 异步非堵塞的web socket接口 (通过enqueue方法来实现)
okhttpclient 通过实现 websocket.factory.newwebsocket 接口实现工厂构造, 通常是由 okhttpclient来构造
-
websocket生命周期:
- connecting状态: 每个websocket的初始状态, 此时message可能位于入队状态但是还没有被dispatcher处理
- open状态: websocket已经被服务器端接受并且socket位于完全开放状态, 所有message入队之后会即刻被处理
- closing状态: websocket进入优雅的关闭状态,websocket继续处理已入队的message但拒绝新的message入队
- closed状态: websocket已完成收发message的过程, 进入完全关闭状态
websocket受到网络等各种因素影响, 可能会断路而提前进入关闭流程 - canceled状态: 被动websocket失败连接为非优雅的过程, 而主动则是优雅短路过程
-
realwebsocket
realwebsocket管理着request队列内容所占的空间大小以及关闭socket之后留给优雅关闭的时间,默认为16m和60秒,在realwebsocket.connect()方法中realwebsocket对okhttpclient以及request封装成call的形式,然后通过call.enqueue()方法定义调用成功和失败时的callback代码
public void connect(okhttpclient client) { client = client.newbuilder() .eventlistener(eventlistener.none) .protocols(only_http1) .build(); final request request = originalrequest.newbuilder() .header("upgrade", "websocket") .header("connection", "upgrade") .header("sec-websocket-key", key) .header("sec-websocket-version", "13") .build(); call = internal.instance.newwebsocketcall(client, request); call.enqueue(new callback() { @override public void onresponse(call call, response response) { try { checkresponse(response); } catch (protocolexception e) { failwebsocket(e, response); closequietly(response); return; } // promote the http streams into web socket streams. streamallocation streamallocation = internal.instance.streamallocation(call); streamallocation.nonewstreams(); // prevent connection pooling! streams streams = streamallocation.connection().newwebsocketstreams(streamallocation); // process all web socket messages. try { listener.onopen(realwebsocket.this, response); string name = "okhttp websocket " + request.url().redact(); initreaderandwriter(name, streams); streamallocation.connection().socket().setsotimeout(0); loopreader(); } catch (exception e) { failwebsocket(e, null); } } @override public void onfailure(call call, ioexception e) { failwebsocket(e, null); } }); }
当call请求被服务端响应的时候就将http流导入到web socket流中,并且调用websocketlistener相对应的状态方法, websocketlistener状态如下:
onopen()
onmessage()
onclosing()
onclosed()
onfailure()
- websocket -> realwebsocket
- connection -> realconnection
- interceptor -> realinterceptorchain
- call -> realcall
- responsebody -> realresponsebody
gzip压缩机制
处理gzip压缩的代码在bridgeinterceptor中,默认情况下为gzip压缩状态,可以从下面的源码片段中获知。如果header中没有accept-encoding
,默认自动添加 ,且标记变量transparentgzip
为true
// if we add an "accept-encoding: gzip" header field we're responsible for also decompressing // the transfer stream. boolean transparentgzip = false; if (userrequest.header("accept-encoding") == null && userrequest.header("range") == null) { transparentgzip = true; requestbuilder.header("accept-encoding", "gzip"); }
bridgeinterceptor解压缩的过程调用了okio.gzipsource()方法并调用okio.buffer()缓存解压过程,源码如下
if (transparentgzip && "gzip".equalsignorecase(networkresponse.header("content-encoding")) && httpheaders.hasbody(networkresponse)) { gzipsource responsebody = new gzipsource(networkresponse.body().source()); headers strippedheaders = networkresponse.headers().newbuilder() .removeall("content-encoding") .removeall("content-length") .build(); responsebuilder.headers(strippedheaders); string contenttype = networkresponse.header("content-type"); responsebuilder.body(new realresponsebody(contenttype, -1l, okio.buffer(responsebody))); }
realcall构造方法
在realcall构造方法上面,早期版本的realcall构造方法中将eventlistener.factory以及eventlistenerfactory.create()分开处理导致realcall构造方法非线程安全. 现在版本的realcall的构造函数使用okhttpclient.eventlistenerfactory().create()
早期版本如下:
realcall(okhttpclient client, request originalrequest, boolean forwebsocket) { final eventlistener.factory eventlistenerfactory = client.eventlistenerfactory(); this.client = client; 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); }
现在 okhttp 3.11.0 的realcall源代码如下
final class realcall implements call { private eventlistener eventlistener; ... private realcall(okhttpclient client, request originalrequest, boolean forwebsocket) { this.client = client; this.originalrequest = originalrequest; this.forwebsocket = forwebsocket; this.retryandfollowupinterceptor = new retryandfollowupinterceptor(client, forwebsocket); } static realcall newrealcall(okhttpclient client, request originalrequest, boolean forwebsocket) { // safely publish the call instance to the eventlistener. realcall call = new realcall(client, originalrequest, forwebsocket); call.eventlistener = client.eventlistenerfactory().create(call); return call; }
cachestrategy
上一篇: 美女抱一下