基本使用:html
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.htmlandroid
http://www.jianshu.com/p/1873287eed87算法
http://blog.csdn.net/itachi85/article/details/51190687編程
一個最簡單的DEMO緩存
public class OkHttp3BasicActivity extends Activity { @BindView(R.id.sn_tv) TextView tv; private OkHttpClient client ; String str = ""; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: tv.setText(msg.obj.toString()); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_network_main); ButterKnife.bind(this); new Thread(new Runnable() { @Override public void run() { okrun(); } }).start(); } private void okrun() { client = new OkHttpClient(); httpUrl = httpUrl+"?"+httpArg; Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); str = response.body().string(); Message msg = Message.obtain(); msg.what =1; msg.obj = str; handler.sendMessage(msg); } }); } }
兩個須要注意的點:服務器
請求網絡原理解析:cookie
http://liuwangshu.cn/application/network/7-okhttp3-sourcecode.html網絡
http://www.jianshu.com/p/230e2e2988e0app
整個實現流程以下:異步
若是正在運行的異步任務隊列數量小於最大請求數,線程池調用execute()執行該任務,不然加入準備隊列
默認狀況下,這是一個不限容量的線程池。但Dispatcher會限制每一個host同時執行的最大請求數量,默認爲5,同時也會限制同時執行的總的最大請求數量
用戶能夠經過Dispatcher的構造函數來定製ExecutorService,這須要經過OkHttpClient.Builder在OkHttpClient的構建過程當中間接的作到。
線程池execute()
由getResponseWithInterceptorChain()來執行網絡請求,獲得response
Response response = getResponseWithInterceptorChain();
成功後回調CallBack的onResponse方法
responseCallback.onResponse(RealCall.this, response);
能夠看到這裏對回調接口是同步調用,也就是回調方法將在後臺線程中被調用。
getResponseWithInterceptorChain()加上了一系列的interceptor,而後執行chain.proceed(request)
private Response getResponseWithInterceptorChain() throws IOException { //構建全棧攔截器 List 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 (!retryAndFollowUpInterceptor.isForWebSocket()) { interceptors.addAll(client.networkInterceptors());//用戶預約義的網絡攔截器 } interceptors.add(new CallServerInterceptor( retryAndFollowUpInterceptor.isForWebSocket()));//調用服務攔截器 //內部經過責任鏈模式來使用攔截器 Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest);//獲取Response }
RetryAndFollowUpInterceptor
重試與重定向攔截器,用來實現重試和重定向功能,
內部經過
while(true)死循環來進行重試獲取Response(有重試上限,超過會拋出異常)。
followUpRequest主要用來根據響應碼來判斷屬於哪一種行爲觸發的重試和重定向(好比未受權,超時,重定向等),而後構建響應的Request進行下一次請求。固然,若是沒有觸發從新請求就會直接返回Response。
RetryAndFollowUpInterceptor
在intercept()
中首先從client取得connection pool,用所請求的URL建立Address對象,並以此建立StreamAllocation對象。
RetryAndFollowUpInterceptor
對重定向的響應也不會無休止的處理下去,它處理的最多的重定向級數爲20次,超過20次時,它會拋異常出來。
總結一下RetryAndFollowUpInterceptor
作的事情:
BridgeInterceptor
橋接攔截器,用於完善請求頭
這個Interceptor作的事情比較簡單。能夠分爲發送請求和收到響應兩個階段來看。在發送請求階段,BridgeInterceptor補全一些http header,這主要包括Content-Type
、Content-Length
、Transfer-Encoding
、Host
、Connection
、Accept-Encoding
、User-Agent
,還加載Cookie
,隨後建立新的Request,並交給後續的Interceptor處理,以獲取響應。
而在從後續的Interceptor獲取響應以後,會首先保存Cookie
。若是服務器返回的響應的content是以gzip壓縮過的,則會先進行解壓縮,移除響應中的header Content-Encoding
和Content-Length
,構造新的響應並返回;不然直接返回響應。
CacheInterceptor
緩存攔截器,首先根據Request中獲取緩存的Response,而後根據用於設置的緩存策略來進一步判斷緩存的Response是否可用以及是否發送網絡請求(
CacheControl.FORCE_CACHE由於不會發送網絡請求,因此networkRequest必定爲空)。若是從網絡中讀取,此時再次根據緩存策略來決定是否緩存響應。
對於CacheInterceptor.intercept(Chain chain)
的分析一樣能夠分爲兩個階段,即請求發送階段和響應獲取以後的階段。這兩個階段由chain.proceed(networkRequest)
來分割。
在請求發送階段,主要是嘗試從cache中獲取響應,獲取成功的話,且響應可用未過時,則響應會被直接返回;不然經過後續的Interceptor來從網絡獲取,獲取到響應以後,若須要緩存的,則緩存起來。
關於HTTP具體的緩存策略這裏暫時再也不詳述。
由RealCall.getResponseWithInterceptorChain()
可見CacheInterceptor的cache一樣來自於OkHttpClient。OkHttp已經有實現Cache的整套策略,在Cache類,但默認狀況下不會被用起來,須要本身在建立OkHttpClient時,手動建立並傳給OkHttpClient.Builder。
ConnectInterceptor
鏈接攔截器,用於打開一個鏈接到遠程服務器。說白了就是經過StreamAllocation獲取HttpStream和RealConnection對象,以便後續讀寫。
CallServerInterceptor
調用服務攔截器,攔截鏈中的最後一個攔截器,經過網絡與調用服務器。經過HttpStream依次次進行寫請求頭、請求頭(可選)、讀響應頭、讀響應體。
CallServerInterceptor
首先將http請求頭部發給服務器,若是http請求有body的話,會再將body發送給服務器,繼而經過httpStream.finishRequest()
結束http請求的發送。
隨後即是從鏈接中讀取服務器返回的http響應,並構造Response。
若是請求的header或服務器響應的header中,Connection
值爲close
,CallServerInterceptor
還會關閉鏈接。
public final class RealInterceptorChain implements Interceptor.Chain { private final List<Interceptor> interceptors; private final StreamAllocation streamAllocation; private final HttpStream httpStream; private final Connection connection; private final int index; private final Request request; private int calls; public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, HttpStream httpStream, Connection connection, int index, Request request) { this.interceptors = interceptors; this.connection = connection; this.streamAllocation = streamAllocation; this.httpStream = httpStream; this.index = index; this.request = request; }
RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpStream, connection, index + 1, request); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next);
RealInterceptorChain + Interceptor實現了裝飾器模式,實現了請求/響應的串式或流式處理。只不過內層裝飾器不是外層裝飾器的成員變量,而是接口方法中建立的臨時變量。
@Override public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (RealInterceptorChain) chain; Request request = realChain.request(); StreamAllocation streamAllocation = realChain.streamAllocation(); // We need the network to satisfy this request. Possibly for validating a conditional GET. boolean doExtensiveHealthChecks = !request.method().equals("GET"); HttpStream httpStream = streamAllocation.newStream(client, doExtensiveHealthChecks); RealConnection connection = streamAllocation.connection(); return realChain.proceed(request, streamAllocation, httpStream, connection); }
在Http1xStram中,它利用Okio對Socket的讀寫操做進行封裝,而建立HttpStream 對象的過程涉及到
StreamAllocation
、RealConnection
,代碼較長,這裏就不展開,這個過程歸納來講,就是找到一個可用的RealConnection
,再利用RealConnection
的輸入輸出(BufferedSource
和BufferedSink
)建立HttpStream 對象,供後續步驟使用。
裏咱們能夠看到,核心工做都由HttpStream對象完成,而HttpStream實際上利用的是 Okio,而 Okio 實際上仍是用的Socket
,因此沒什麼神祕的,只不過一層套一層,層數有點多。
其實 Interceptor
的設計也是一種分層的思想,每一個Interceptor
就是一層。爲何要套這麼多層呢?分層的思想在 TCP/IP 協議中就體現得淋漓盡致,分層簡化了每一層的邏輯,每層只須要關注本身的責任(單一原則思想也在此體現),而各層之間經過約定的接口/協議進行合做(面向接口編程思想),共同完成複雜的任務。
OkHttpClient.newCall(request)進行execute或者enqueue操做
Request保存了url,method,body,head等信息
1.調用dispatcher的enqueue方法
dispatcher維護了一個相似於CachedThreadPool的線程池,比較適合執行大量的耗時比較少的任務。線程池正在運行的請求主機數小於5時則把請求加載到runningAsyncCalls中並在線程池中執行。
2.進入攔截器鏈
重試和重定向:處理重定向事件
bridge:完善請求頭,加載cookie
Cache:讀取緩存(ETAG,LastModified),判斷緩存是否過時,若是未過時則直接返回,再也不進入後續攔截器。若是開啓網絡鏈接則判斷是否要緩存響應
Connection:創建鏈接
其中,Cache這裏的緩存都是基於Map,key是請求中url的md5,value是在文件中查詢到的緩存,頁面置換基於LRU算法
創建鏈接後,核心工做都由HttpStream對象完成,而HttpStream實際上利用的是 Okio,而 Okio 實際上仍是用的Socket