Android小知識-介紹OkHttp中的攔截器
本平臺的文章更新會有延遲,大家可以關注微信公眾號-顧林海,包括年底前會更新kotlin由淺入深系列教程,目前計劃在微信公眾號進行首發,如果大家想獲取最新教程,請關注微信公眾號,謝謝
在OkHttp中執行同步請求會阻塞當前執行緒,直到HTTP響應返回,同步請求使用的是execute()方法;而非同步請求類似於非阻塞式的請求,它的執行結果一般通過介面回撥的方式告知呼叫者,非同步請求使用的是enqueue(Callback)方法;
OkHttp中不管是同步還是非同步,都是通過攔截器完成網路的獲取。
官網對攔截器的解釋是:攔截器是OkHttp中提供的一種強大機制,它可以實現網路監聽、請求以及響應重寫、請求失敗重試等功能。
看下面這張圖:

image
在這張圖中可以看到有兩種攔截器,一種是APPLICATION INTERCEPTORS,也就是應用攔截器;第二種是NETWORK INTERCEPTORS,表示網路攔截器。除了這兩種攔截器,重要的是中間OkHttp core這塊,這是OkHttp提供的內部攔截器。
看下圖:

image
這是OkHttp提供給我們的攔截器,內部是以攔截器的鏈的形式執行HTTP的請求,其中RetryAndFollowUpInterceptor是重試和失敗重定向攔截器,BridgeInterceptor是橋接和適配攔截器,CacheInterceptor是快取攔截器,ConnectInterceptor是連線攔截器,負責建立可用的連線,CallServerInterceptor負責將HTTP的請求寫入網路的IO流中,並且從網路IO流中讀取服務端返回給客戶端的資料。
看過前面幾節的同學應該知道,無論是同步請求還是非同步請求,最終執行網路請求並獲取的Response都是通過getResponseWithInterceptorChain()方法獲取的,程式碼如下。
//非同步請求 @Override protected void execute() { boolean signalledCallback = false; try { //重點1 使用攔截器鏈 Response response = getResponseWithInterceptorChain(); ... } catch (IOException e) { ... } finally { 回收請求 client.dispatcher().finished(this); } } //同步請求 @Override public Response execute() throws IOException { //第一步:判斷同一Http是否請求過 ... //捕捉Http請求的異常堆疊資訊 ... //監聽請求開始 ... try { //第二步:同步請求新增到同步佇列中 ... //第三步:使用攔截器鏈 Response result = getResponseWithInterceptorChain(); ... } catch (IOException e) { ... } finally { //第四步:回收請求 client.dispatcher().finished(this); } }
getResponseWithInterceptorChain()方法返回的就是我們網路請求的響應結果Response物件。
進入getResponseWithInterceptorChain()方法:
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor> interceptors = new ArrayList<>(); //使用者自定義的攔截器 interceptors.addAll(client.interceptors()); //新增OkHttp提供的五個攔截器以及networkInterceptors interceptors.add(retryAndFollowUpInterceptor); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client)); if (!forSocket/">WebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket)); //標記1 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); //標記2 return chain.proceed(originalRequest); }
getResponseWithInterceptorChain方法一開始將我們需要的攔截器新增到一個集合中,其中就包括我們自定義的攔截器以及上面提到的幾種攔截器。
接著在標記1處建立了一個RealInterceptorChain物件,傳入的第一個引數就是上面的新增的一系列攔截器,建立完畢後,在標記2處執行RealInterceptorChain物件的proceed方法。
進入RealInterceptorChain的proceed方法:
@Override public Response proceed(Request request) throws IOException { return proceed(request, streamAllocation, httpCodec, connection); }
繼續往下看:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { ... //標記1 RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); //標記2:取出index位置的攔截器 Interceptor interceptor = interceptors.get(index); //標記3 Response response = interceptor.intercept(next); ... return response; }
在標記1處又建立了一個RealInterceptorChain物件,在建立物件時,傳入的第五個引數是index+1,這樣的話在下次訪問時,只能從下一個攔截器開始進行訪問,而不能從當前攔截器。
在標記2處取出第index位置的攔截器。
在標記3處將代表下一個攔截器的鏈的RealInterceptorChain物件傳入當前位置的攔截器中,在當前攔截器鏈中執行請求,獲取Response後依次返回給它的上一個攔截器,如果當前攔截器沒有獲取Response就繼續呼叫RealInterceptorChain物件的prceed方法來建立下一個攔截器鏈,就這樣攔截器鏈一層一層的呼叫,這樣所有的攔截器鏈構成了一個完整的鏈條。
到目前為止,總結如下:
-
建立一系列攔截器,並將其放入一個攔截器list集合中。
-
建立一個攔截器鏈RealInterceptorChain,並執行攔截器鏈的proceed方法,這個proceed方法的核心是繼續建立下一個攔截器鏈。
我們看下RetryAndFollowUpInterceptor這個攔截器,它是重試和失敗重定向攔截器。
@Override public Response intercept(Interceptor.Chain chain) throws IOException { ... RealInterceptorChain realChain = (RealInterceptorChain) chain; ... response = realChain.proceed(request, streamAllocation, null, null); ... }
可以看到RetryAndFollowUpInterceptor攔截器的intercept方法,內部又執行了傳遞進來的RealInterceptorChain物件的proceed方法,而proceed方法在上面介紹過了,作用是建立下一個攔截器鏈,這樣就說明了整個攔截器鏈的執行過程就像鏈條一樣,一環扣一環。

838794-506ddad529df4cd4.webp.jpg
搜尋微信“顧林海”公眾號,定期推送優質文章。