基本使用:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.html
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); } }); } }
兩個需要注意的點:
- okhttp3不能在ui線程中運行
- onresponse在子線程中,需要用handler回調才能操作ui
請求網絡原理解析:
http://liuwangshu.cn/application/network/7-okhttp3-sourcecode.html
HTTP請求執行流程分析
http://www.jianshu.com/p/230e2e2988e0
OkHttp3源碼詳解(二整體流程)
整個實現流程如下:
- 創建OkHttpClient對象。OkHttpClient為網絡請求執行的一個中心,它會管理連接池,緩存,SocketFactory,代理,各種超時時間,DNS,請求執行結果的分發等許多內容。
- 創建Request對象。Request用於描述一個HTTP請求,比如請求的方法是"GET"還是"POST",請求的URL,請求的header,請求的body,請求的緩存策略等。
- 利用前面創建的OkHttpClient對象和Request對象創建Call對象。Call是一次HTTP請求的Task,它會執行網絡請求以獲得響應。OkHttp中的網絡請求執行Call既可以同步進行,也可以異步進行。調用call.execute()將直接執行網絡請求,阻塞直到獲得響應。而調用call.enqueue()傳入回調,則會將Call放入一個異步執行隊列,由ExecutorService在后台執行。
如果正在運行的異步任務隊列數量小於最大請求數,線程池調用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
做的事情:
- 創建StreamAllocation對象,為后面流程的執行准備條件。
- 處理重定向的HTTP響應。
- 錯誤恢復。
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
還會關閉連接。
RetryAndFollowUpInterceptor --->創建StreamAllocation對象,處理http的redirect,出錯重試。對后續Interceptor的執行的影響:修改request及StreamAllocation。
BridgeInterceptor-------------->補全缺失的一些http header。對后續Interceptor的執行的影響:修改request。
CacheInterceptor-------------->處理http緩存。對后續Interceptor的執行的影響:若緩存中有所需請求的響應,則后續Interceptor不再執行。
ConnectInterceptor------------>借助於前面分配的StreamAllocation對象建立與服務器之間的連接,並選定交互所用的協議是HTTP 1.1還是HTTP 2。對后續Interceptor的執行的影響:創建了httpStream和connection。
CallServerInterceptor----------->處理IO,與服務器進行數據交換。對后續Interceptor的執行的影響:為Interceptor鏈中的最后一個Interceptor,沒有后續Interceptor。
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