Okhttp3 使用和原理(DEMO)

 

基本使用: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);
            }
        });

    }
}

 

兩個須要注意的點:服務器

  1. okhttp3不能在ui線程中運行
  2. onresponse在子線程中,須要用handler回調才能操做ui

 

 

請求網絡原理解析:cookie

http://liuwangshu.cn/application/network/7-okhttp3-sourcecode.html網絡

HTTP請求執行流程分析

http://www.jianshu.com/p/230e2e2988e0app

 

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在後臺執行。
Call對象
若是使用enqueue方法,則調用dispatch.enqueue(),發送到線程池
若是使用execute,則不須要dispatch發送到線程池處理,直接同步處理。

線程池 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  
  }  
因而可知OkHttp中,Http請求的實際處理流程將大體以下圖這樣:

 

RetryAndFollowUpInterceptor

     重試與重定向攔截器,用來實現重試和重定向功能,內部經過while(true)死循環來進行重試獲取Response(有重試上限,超過會拋出異常)。followUpRequest主要用來根據響應碼來判斷屬於哪一種行爲觸發的重試和重定向(好比未受權,超時,重定向等),而後構建響應的Request進行下一次請求。固然,若是沒有觸發從新請求就會直接返回Response。

RetryAndFollowUpInterceptorintercept()中首先從client取得connection pool,用所請求的URL建立Address對象,並以此建立StreamAllocation對象。

RetryAndFollowUpInterceptor對重定向的響應也不會無休止的處理下去,它處理的最多的重定向級數爲20次,超過20次時,它會拋異常出來。

總結一下RetryAndFollowUpInterceptor作的事情:

  1. 建立StreamAllocation對象,爲後面流程的執行準備條件。
  2. 處理重定向的HTTP響應。
  3. 錯誤恢復。

 

BridgeInterceptor

橋接攔截器,用於完善請求頭

這個Interceptor作的事情比較簡單。能夠分爲發送請求和收到響應兩個階段來看。在發送請求階段,BridgeInterceptor補全一些http header,這主要包括Content-TypeContent-LengthTransfer-EncodingHostConnectionAccept-EncodingUser-Agent,還加載Cookie,隨後建立新的Request,並交給後續的Interceptor處理,以獲取響應。

而在從後續的Interceptor獲取響應以後,會首先保存Cookie。若是服務器返回的響應的content是以gzip壓縮過的,則會先進行解壓縮,移除響應中的header Content-EncodingContent-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值爲closeCallServerInterceptor還會關閉鏈接。

總結一下這幾個Interceptor的職責:
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。
 
 
在RealInterceptorChain.proceed()中,除了對狀態及獲取的reponse作檢查以外,最主要的事情便是構造新的RealInterceptorChain對象,獲取對應Interceptor,並調用Interceptor的intercept(next)了。在這裏,index充當迭代器或指示器的角色,用於指出當前正在處理的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 對象的過程涉及到 StreamAllocationRealConnection,代碼較長,這裏就不展開,這個過程歸納來講,就是找到一個可用的RealConnection,再利用RealConnection 的輸入輸出(BufferedSourceBufferedSink)建立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

相關文章
相關標籤/搜索