咨詢(xún)電話(huà)
微信掃一掃
新聞動(dòng)態(tài)
OKHttp 可能你從來(lái)沒(méi)用過(guò)這樣的攔截器
網(wǎng)站建設 發(fā)布者:cya 2019-12-20 08:37 訪(fǎng)問(wèn)量:158
作者:北斗星_And
博客:https://juejin.im/post/5ddddd2a6fb9a07161483fb2
前言
在平時(shí)開(kāi)發(fā)中,你有沒(méi)有下面這樣的困擾呢?
明明是服務(wù)端的接口數據錯誤,而QA(測試)第一個(gè)找到的可能是客戶(hù)端開(kāi)發(fā)的你,為什么這個(gè)頁(yè)面出現錯誤了?
而作為客戶(hù)端開(kāi)發(fā)的你,可能要拿出測試機連上電腦,打一下Log,看一下到底返回了什么數據,導致頁(yè)面錯誤。
或者高級一點(diǎn)的QA,會(huì )自己打Log或者連接抓包工具看一下服務(wù)端返回的具體數據,然后把Bug提給對應的人,而大多數公司的業(yè)務(wù)測試,都僅僅是測試業(yè)務(wù),不管技術(shù)層的。我司的大部分QA,屬于外派來(lái)的,一般也只測試業(yè)務(wù),每次有問(wèn)題,都先找客戶(hù)端。
你現在正在外面做地鐵,產(chǎn)品或者你領(lǐng)導突然給你反饋,你之前做的那塊業(yè)務(wù),突然線(xiàn)上跑不起來(lái)了,不行了。你一想,這肯定是服務(wù)端的問(wèn)題啊,但是怎么證明呢?
服務(wù)端上個(gè)線(xiàn),每次都需要客戶(hù)端加班配合,說(shuō)有問(wèn)題,可以及時(shí)幫助排查問(wèn)題。
說(shuō)了這么多,就是缺少一個(gè)端上的抓包小工具,來(lái)查看服務(wù)端的數據是否有問(wèn)題,今天推薦的是一個(gè)基于OKHttp的抓包工具。 部分截圖如下
自帶分類(lèi)接口
抓包數據以時(shí)間為緯度,默認存儲到手機緩存下 /Android/Data/包名/Cache/capture/ 下
支持Http/Https協(xié)議的抓包,分類(lèi)請求方式/請求URL/請求Header/請求體/響應狀態(tài)/響應Header/響應體
支持一鍵復制對應的狀態(tài)
響應體如果是JSON,支持自動(dòng)格式化
抓包數據,默認緩存一天
代碼已經(jīng)托管到Github 地址:https://github.com/DingProg/NetworkCaptureSelf
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
dependencies {
debugImplementation 'com.github.DingProg.NetworkCaptureSelf:library:v1.0.1'
releaseImplementation 'com.github.DingProg.NetworkCaptureSelf:library_no_op:v1.0.1'
}
在你的全局 OkHttp 中添加 Interceptor
new OkHttpClient.Builder() .addInterceptor(new CaptureInfoInterceptor()) .build();
原理及涉及知識詳解
作為Android開(kāi)發(fā),說(shuō)到OKHttp的Interceptor,肯定熟悉不過(guò)了。那么你對 Interceptor 又了解多少呢?你都使用過(guò)那些OKHttp的 Interceptor呢?
我們先來(lái)看一下最近滴滴很火的哆啦A夢(mèng)
長(cháng)下面這個(gè)樣子
其中關(guān)于網(wǎng)絡(luò )模塊OK Http的監聽(tīng)如下
OkHttpClient client = new OkHttpClient().newBuilder() //用于模擬弱網(wǎng)的攔截器 .addNetworkInterceptor(new DoraemonWeakNetworkInterceptor()) //網(wǎng)絡(luò )請求監控的攔截器 ,用于網(wǎng)絡(luò )流量監聽(tīng)等 .addInterceptor(new DoraemonInterceptor()).build();
這里舉例說(shuō)一下弱網(wǎng)模擬
看一下他的實(shí)現代碼
public class DoraemonWeakNetworkInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
if (!WeakNetworkManager.get().isActive()) {
Request request = chain.request();
return chain.proceed(request);
}
final int type = WeakNetworkManager.get().getType();
switch (type) {
case WeakNetworkManager.TYPE_TIMEOUT:
//超時(shí)
final HttpUrl url = chain.request().url();
throw WeakNetworkManager.get().simulateTimeOut(url.host(), url.port());
case WeakNetworkManager.TYPE_SPEED_LIMIT:
//限速
return WeakNetworkManager.get().simulateSpeedLimit(chain);
default:
//斷網(wǎng)
throw WeakNetworkManager.get().simulateOffNetwork(chain.request().url().host());
}
}
}
實(shí)現一個(gè)OkHttp的Intercepter,根據不同的狀態(tài)來(lái)進(jìn)行延遲,例如如下的模擬超時(shí)
/** * 模擬超時(shí) * * @param host * @param port */ public SocketTimeoutException simulateTimeOut(String host, int port) { SystemClock.sleep(mTimeOutMillis); return new SocketTimeoutException(String.format("failed to connect to %s (port %d) after %dms", host, port, mTimeOutMillis)); }
根據Interceptor 可以干很多事情,那么Interceptor到底是什么樣的原理呢?
先看一下Interceptor的原型
public interface Interceptor { Response intercept(Chain chain) throws IOException;}
再看一下OkHttp源碼,可以知道,我們的請求最終都會(huì )被調用到RealCall中,并執行到如下代碼
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
}
...
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> 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 (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
在getResponseWithInterceptorChain 添加了很多OkHttp自定義的攔截器,其中有重定向,Cache,連接請求,發(fā)起請求到服務(wù)端等。我們來(lái)看一下最后幾行 代碼,RealInterceptorChain 是一個(gè)Interceptor.Chain類(lèi)型,并執行chain.proceed,接著(zhù)看一下 proceed 方法
//RealInterceptorChain public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { ... if (index >= interceptors.size()) throw new AssertionError(); calls++; // Call the next interceptor in the chain. RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpCodec, connection, index + 1, request); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); .... return response; }
重點(diǎn)看一下Call the next interceptor in the chain 下面幾行代碼,他把當前的interceptor.intercept()時(shí),傳入的是下一個(gè)interceptor的包裝類(lèi),RealInterceptorChain 這樣就實(shí)現了,鏈式遞歸調用了,直到最后一個(gè)response返回,才會(huì )依次返回到第一個(gè)interceptor。
可以用如下圖大致描述:
講了那么多相關(guān)的知識點(diǎn),我們來(lái)回到正題,上述推薦小工具的實(shí)現步驟介紹
抓包工具實(shí)現主要步驟介紹
在Manifest中注冊即可,如下
<activity
android:name="com.ding.library.internal.ui.CaptureInfoActivity"
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|locale"
android:launchMode="singleInstance"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />
<activity-alias
android:label="抓包入口"
android:name="CaptureInfoActivity"
android:targetActivity="com.ding.library.internal.ui.CaptureInfoActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
public final class CaptureInfoInterceptor implements Interceptor{ @Override public Response intercept(Chain chain) throws IOException { //獲取request 所有信息 ... //獲取response 所有信息 ... //存儲抓包數據 CacheUtils.getInstance().saveCapture(request.url().toString(),captureEntity); }}
這里其中有兩種方式,添加到OkHttp的Interceptor,一種硬編碼,如下
new OkHttpClient.Builder() .addInterceptor(new CaptureInfoInterceptor()) .build();
另一種方式 采用字節碼注入的形式,關(guān)于字節碼注入,可以簡(jiǎn)單參考我的另一篇Gradle學(xué)習筆記,自定義 Transform部分。
存儲時(shí),為了不影響到主APP的網(wǎng)絡(luò )請求效率,需要在單獨的線(xiàn)程中執行IO操作,這里使用了單線(xiàn)程池
public class DiskIOThreadExecutor implements Executor { private final Executor mDiskIO; public DiskIOThreadExecutor() { mDiskIO = Executors.newSingleThreadExecutor(); } @Override public void execute(@NonNull Runnable command) { mDiskIO.execute(command); }}
public void saveCapture(final String url, final CaptureEntity value) { Runnable runnable = new Runnable() { @Override public void run() { String saveUrl = url; if (url.contains("?")) { saveUrl = saveUrl.substring(0, saveUrl.indexOf("?")); } String key = urlMd5(saveUrl); sp.edit().putString(key, saveUrl).apply(); checkOrCreateFilePath(key); File file = new File(captureFilePath + "/" + key + "/" + getCurrentTime() + ".txt"); BufferedSink bufferedSink = null; try { file.createNewFile(); bufferedSink = Okio.buffer(Okio.sink(file)); bufferedSink.writeString(JSON.toJSONString(value), StandardCharsets.UTF_8); bufferedSink.flush(); } catch (Exception e) { e.printStackTrace(); } finally { if (bufferedSink != null) { try { bufferedSink.close(); } catch (IOException e) { e.printStackTrace(); } } } } }; diskIOThreadExecutor.execute(runnable); }
讀取抓包數據時(shí),不直接讀取全部的數據,只讀取當前抓包的目錄,數據,點(diǎn)擊時(shí),在去加載對應的數據
public List<String> getCapture() {
File file = new File(captureFilePath);
return getFileList(file);
}
public List<String> getCapture(String key) {
File file = new File(captureFilePath + "/" + key);
return getFileList(file);
}
好了,關(guān)于這個(gè)小工具,就介紹那么多了,具體細節代碼,可以直接查看Github代碼倉庫,github.com/DingProg/Ne…
總結
其實(shí)關(guān)于抓包工具,有一些成熟的方案。
電腦端的有Fiddler、Charels,Wireshark等,但是不是特別方便。
APP可以抓其他包的工具,如NetWorkPacketCapture/抓包精靈/AndroidHttpCapture,但是都一些限制條件,要么代碼沒(méi)開(kāi)源,廣告多。要么就是只能在WIFI下,或者要么就是需要Root等,不太好定制。
本文,主要是介紹OkHttp的攔截器,并從中發(fā)現可以干很多事情。如文中有錯誤,還忘指正,感謝。
關(guān)鍵字: OKHttp 攔截器 開(kāi)封網(wǎng)站建設
文章連接: http://www.gostscript.com/wzjss/649.html
版權聲明:文章由 晨展科技 整理收集,來(lái)源于互聯(lián)網(wǎng)或者用戶(hù)投稿,如有侵權,請聯(lián)系我們,我們會(huì )立即刪除。如轉載請保留
晨展解決方案
晨展新聞