OKhttp攔截器實(shí)現(xiàn)實(shí)踐環(huán)節(jié)源碼解析
正文
本節(jié)我們開始自我實(shí)現(xiàn)我們自己okhttp框架中的每個(gè)攔截器。
先簡單回顧一下各個(gè)攔截器的作用:
- RetryAndFollowUpInterceptor:重試攔截器
處理重試的一個(gè)攔截器,會(huì)去處理一些異常,根據(jù)底層返回的響應(yīng)數(shù)據(jù),先進(jìn)行一些特殊狀態(tài)碼的判斷,例如:如果底層返回307,則根據(jù)服務(wù)端返回的最新location,重新構(gòu)建新的請求,交由底層攔截器,重新發(fā)起請求。如果底層返回路由異常、某些IO異常,則會(huì)continue,重新發(fā)起請求。
- BridgeInterceptor:基礎(chǔ)的攔截器
給我們平常發(fā)起的請求,添加通用和請求首部信息,做一個(gè)簡單的處理,設(shè)置一些通用的請求頭,Cookie、Connection、Content-Type、Content-Length,做一些返回的處理,如果返回的數(shù)據(jù)被壓縮了,采用 ZipSource,保存Cookie。
- CacheInterceptor:緩存攔截器
緩存存儲(chǔ)策略、緩存過期策略、緩存對比策略的具體實(shí)現(xiàn)。
- ConnectInterceptor:連接的攔截器
ConnectInterceptor負(fù)責(zé)連接復(fù)用、建立socket連接,okio與socket輸入輸出流綁定。
- CallServerInterceptor: 具體與服務(wù)器通信,給服務(wù)器寫數(shù)據(jù)和讀取數(shù)據(jù);
攔截器的自我實(shí)現(xiàn)
好了,接下來,把我們之前寫的框架的代碼,重新梳理一下,新增一下幾個(gè)攔截器。 RealCall.java
package com.itbird.okhttpstudy.okhttp;
import android.util.Log;
import com.itbird.okhttpstudy.interceptor.BridgeInterceptor;
import com.itbird.okhttpstudy.interceptor.CacheInterceptor;
import com.itbird.okhttpstudy.interceptor.CallServerInterceptor;
import com.itbird.okhttpstudy.interceptor.ConnectInterceptor;
import com.itbird.okhttpstudy.interceptor.Interceptor;
import com.itbird.okhttpstudy.interceptor.RetryAndFollowUpInterceptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by itbird on 2022/11/21
*/
public class RealCall implements Call {
private Request request;
private OkhttpClient okhttpClient;
public RealCall(Request request, OkhttpClient okhttpClient) {
this.request = request;
this.okhttpClient = okhttpClient;
}
@Override
public void enqueue(Callback callback) {
okhttpClient.dispatcher().enqueue(new AsyncCall(callback));
}
@Override
public Response execute() {
return getResponseWithInterceptorChain();
}
@Override
public Request request() {
return request;
}
private Response getResponseWithInterceptorChain() {
List<Interceptor> interceptors = new ArrayList<Interceptor>();
interceptors.add(new BridgeInterceptor());// 基礎(chǔ)
interceptors.add(new CacheInterceptor());// 緩存
interceptors.add(new ConnectInterceptor());// 建立連接
interceptors.add(new CallServerInterceptor());// 寫數(shù)據(jù)
interceptors.add(new RetryAndFollowUpInterceptor());// 重試
Interceptor.Chain chain = new RealInterceptorChain(this, interceptors, request);
try {
return chain.proceed(request);
} catch (IOException e) {
//處理過程被中斷時(shí),通過錯(cuò)誤碼返回
return null;
}
}
class AsyncCall extends NamedRunnable {
private Callback callback;
public AsyncCall(Callback callback) {
this.callback = callback;
}
@Override
public void execute() {
Log.d(Constants.TAG, "AsyncCall execute");
//這里有問題的
Response response = getResponseWithInterceptorChain();
if (callback != null) {
try {
callback.onResponse(RealCall.this, response);
} catch (IOException e) {
}
}
}
}
}
接下來還是老辦法,按照AS提示,新建這些類。
RetryAndFollowUpInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class RetryAndFollowUpInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "RetryAndFollowUpInterceptor");
Request request = chain.request();
//okhttp表現(xiàn)為,此處,去根據(jù)底層拋出的異常,決定是否為關(guān)鍵錯(cuò)誤異常,如果不是,則while true循環(huán),去執(zhí)行重試請求
return chain.proceed(request);
}
}
BridgeInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.RequsetBody;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class BridgeInterceptor implements Interceptor {
public BridgeInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "BridgeInterceptor");
Request request = chain.request();
// 添加一些請求頭
// request.addParam("Connection", "keep-alive");
// 做一些其他處理
if (request.requsetBody() != null) {
RequsetBody requestBody = request.requsetBody();
request.addParam("Content-Type", requestBody.getContentType());
request.addParam("Content-Length", Long.toString(requestBody.getContentLength()));
}
//GZIP數(shù)據(jù)流轉(zhuǎn)換
return chain.proceed(request);
}
}
CacheInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.CacheControl;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class CacheInterceptor implements Interceptor {
public CacheInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "CacheInterceptor");
Request request = chain.request();
if (request.cache() == CacheControl.FORCE_CACHE) {
//本地緩存有沒有,緩存過期了沒有,緩存對比服務(wù)器返回307
}
return chain.proceed(request);
}
}
ConnectInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class ConnectInterceptor implements Interceptor {
public ConnectInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "ConnectInterceptor");
Request request = chain.request();
//表現(xiàn)為okhttp的話,這里就是socket簡歷連接,并且將socket輸入輸出流與okio綁定在一起
return chain.proceed(request);
}
}
CallServerInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by itbird on 2022/11/24
*/
public class CallServerInterceptor implements Interceptor {
public CallServerInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "CallServerInterceptor");
Request request = chain.request();
try {
//獲取連接請求
URL url = new URL(request.url());
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//設(shè)置連接超時(shí)
httpURLConnection.setConnectTimeout(3000);
//設(shè)置方法
httpURLConnection.setRequestMethod(request.method());
if (request.requsetBody() != null) {
httpURLConnection.setRequestProperty("Content-Type", request.requsetBody().getContentType());
httpURLConnection.setRequestProperty("Content-Length", String.valueOf(request.requsetBody().getContentLength()));
Log.d(Constants.TAG, httpURLConnection.getRequestProperty("Content-Length"));
Log.d(Constants.TAG, httpURLConnection.getRequestProperty("Content-Type"));
}
//開始連接
httpURLConnection.connect();
//插入,如果requsetbody不為空,則繼續(xù)寫入內(nèi)容
if (request.requsetBody() != null) {
request.requsetBody().writeBodyData(httpURLConnection.getOutputStream());
}
//判斷返回的狀態(tài)碼
if (httpURLConnection.getResponseCode() == 200) {
//獲取返回的數(shù)據(jù)
InputStream inputStream = httpURLConnection.getInputStream();
//將返回的數(shù)據(jù),封裝為response
Response response = new Response(inputStream);
return response;
}
} catch (MalformedURLException e) {
throw e;
} catch (IOException e) {
throw e;
}
return null;
}
}
運(yùn)行一下

題外話
說到責(zé)任鏈模式,這里有一個(gè)題外話,我們之前分析view事件源碼的時(shí)候,也看到過,view 事件源碼,也是責(zé)任鏈機(jī)制,它是通過每層返回true、false來決定是否攔截。

大家想一下,和okhttp這里的責(zé)任鏈有啥不同的?我們上面查看okhttp源碼的時(shí)候知道,它并不是通過每層返回true or false來決定是否攔截的,而是根據(jù)每層返回的response 以及 是否拋出異常來決定是否攔截。
以上就是OKhttp攔截器實(shí)現(xiàn)實(shí)踐環(huán)節(jié)源碼解析的詳細(xì)內(nèi)容,更多關(guān)于OKhttp 攔截器實(shí)現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android?studio項(xiàng)目:綁定服務(wù)和線程實(shí)現(xiàn)計(jì)時(shí)器
這篇文章主要介紹了android?studio項(xiàng)目:綁定服務(wù)和線程實(shí)現(xiàn)計(jì)時(shí)器,完成一個(gè)秒表,具備啟停功能,通過綁定服務(wù)實(shí)現(xiàn)功能,通過Thread+handler更新界面,需要的朋友可以參考一下2021-12-12
android 仿微信demo——微信消息界面實(shí)現(xiàn)(移動(dòng)端)
本系列文章主要介紹了微信小程序-閱讀小程序?qū)嵗╠emo),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望能給你們提供幫助2021-06-06
Android實(shí)現(xiàn)圓圈倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)圓圈倒計(jì)時(shí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
Android中TextView自動(dòng)識(shí)別url且實(shí)現(xiàn)點(diǎn)擊跳轉(zhuǎn)
這篇文章主要介紹了關(guān)于Android中TextView自動(dòng)識(shí)別url且實(shí)現(xiàn)點(diǎn)擊跳轉(zhuǎn)的相關(guān)資料,文中給出了詳細(xì)的示例代碼,對大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。2017-03-03
Android 中FloatingActionButton(懸浮按鈕)實(shí)例詳解
這篇文章主要介紹了Android 中FloatingActionButton(懸浮按鈕)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05
Android 實(shí)現(xiàn)電話攔截及攔截提示音功能的開發(fā)
本文主要介紹Android 實(shí)現(xiàn)電話攔截和攔截提示音功能的開發(fā),這里提供實(shí)現(xiàn)代碼和詳細(xì)講解,有需要的小伙伴可以參考下2016-08-08
android中Fragment+RadioButton實(shí)現(xiàn)底部導(dǎo)航欄
本篇文章主要介紹了android中Fragment+RadioButton實(shí)現(xiàn)底部導(dǎo)航欄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
Android 數(shù)據(jù)庫SQLite 寫入SD卡的方法
如果手機(jī)沒有root,數(shù)據(jù)庫文件是無法查看到的,不方便調(diào)試。最好的辦法是把數(shù)據(jù)庫寫進(jìn)SD卡。通過本文給大家介紹Android 數(shù)據(jù)庫SQLite 寫入SD卡的方法,需要的朋友參考下吧2016-04-04
淺析Kotlin使用infix函數(shù)構(gòu)建可讀語法流程講解
這篇文章主要介紹了淺析Kotlin使用infix函數(shù)構(gòu)建可讀語法,我們在Kotlin中就多次使用A to B這樣的語法結(jié)構(gòu)構(gòu)建鍵值對,包括Kotlin自帶的mapOf()函數(shù),這種語法結(jié)構(gòu)的優(yōu)點(diǎn)是可讀性強(qiáng)2023-01-01

