日韩国产精品99成人不卡在线无毒|狠狠躁夜夜爽一级二级精品|亚洲日日噜噜孕妇中文字幕|日韩久草中文三级片

新聞動(dòng)態(tài)

OKHttp 可能你從來沒用過這樣的攔截器

網(wǎng)站建設(shè) 發(fā)布者:cya 2019-12-20 08:37 訪問量:237

作者:北斗星_And

博客:https://juejin.im/post/5ddddd2a6fb9a07161483fb2


前言


在平時(shí)開發(fā)中,你有沒有下面這樣的困擾呢?


場(chǎng)景一


明明是服務(wù)端的接口數(shù)據(jù)錯(cuò)誤,而QA(測(cè)試)第一個(gè)找到的可能是客戶端開發(fā)的你,為什么這個(gè)頁(yè)面出現(xiàn)錯(cuò)誤了?

而作為客戶端開發(fā)的你,可能要拿出測(cè)試機(jī)連上電腦,打一下Log,看一下到底返回了什么數(shù)據(jù),導(dǎo)致頁(yè)面錯(cuò)誤。

或者高級(jí)一點(diǎn)的QA,會(huì)自己打Log或者連接抓包工具看一下服務(wù)端返回的具體數(shù)據(jù),然后把Bug提給對(duì)應(yīng)的人,而大多數(shù)公司的業(yè)務(wù)測(cè)試,都僅僅是測(cè)試業(yè)務(wù),不管技術(shù)層的。我司的大部分QA,屬于外派來的,一般也只測(cè)試業(yè)務(wù),每次有問題,都先找客戶端。


場(chǎng)景二


你現(xiàn)在正在外面做地鐵,產(chǎn)品或者你領(lǐng)導(dǎo)突然給你反饋,你之前做的那塊業(yè)務(wù),突然線上跑不起來了,不行了。你一想,這肯定是服務(wù)端的問題啊,但是怎么證明呢?

場(chǎng)景三

服務(wù)端上個(gè)線,每次都需要客戶端加班配合,說有問題,可以及時(shí)幫助排查問題。


推薦一個(gè)小工具

說了這么多,就是缺少一個(gè)端上的抓包小工具,來查看服務(wù)端的數(shù)據(jù)是否有問題,今天推薦的是一個(gè)基于OKHttp的抓包工具。 部分截圖如下




支持功能

  • 自帶分類接口

  • 抓包數(shù)據(jù)以時(shí)間為緯度,默認(rèn)存儲(chǔ)到手機(jī)緩存下 /Android/Data/包名/Cache/capture/ 下

  • 支持Http/Https協(xié)議的抓包,分類請(qǐng)求方式/請(qǐng)求URL/請(qǐng)求Header/請(qǐng)求體/響應(yīng)狀態(tài)/響應(yīng)Header/響應(yīng)體

  • 支持一鍵復(fù)制對(duì)應(yīng)的狀態(tài)

  • 響應(yīng)體如果是JSON,支持自動(dòng)格式化

  • 抓包數(shù)據(jù),默認(rèn)緩存一天


Github地址

代碼已經(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();


原理及涉及知識(shí)詳解


作為Android開發(fā),說到OKHttp的Interceptor,肯定熟悉不過了。那么你對(duì) Interceptor 又了解多少呢?你都使用過那些OKHttp的 Interceptor呢?

我們先來看一下最近滴滴很火的哆啦A夢(mèng)


DoraemonKit


長(zhǎng)下面這個(gè)樣子



其中關(guān)于網(wǎng)絡(luò)模塊OK Http的監(jiān)聽如下

OkHttpClient client = new OkHttpClient().newBuilder()                //用于模擬弱網(wǎng)的攔截器                .addNetworkInterceptor(new DoraemonWeakNetworkInterceptor())                //網(wǎng)絡(luò)請(qǐng)求監(jiān)控的攔截器 ,用于網(wǎng)絡(luò)流量監(jiān)聽等                .addInterceptor(new DoraemonInterceptor()).build();


這里舉例說一下弱網(wǎng)模擬


弱網(wǎng)模擬


看一下他的實(shí)現(xiàn)代碼

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í)現(xiàn)一個(gè)OkHttp的Intercepter,根據(jù)不同的狀態(tà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));    }


根據(jù)Interceptor 可以干很多事情,那么Interceptor到底是什么樣的原理呢?


Interceptor原理


先看一下Interceptor的原型

public interface Interceptor {  Response intercept(Chain chain) throws IOException;}

再看一下OkHttp源碼,可以知道,我們的請(qǐng)求最終都會(huì)被調(diào)用到RealCall中,并執(zhí)行到如下代碼

@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,連接請(qǐng)求,發(fā)起請(qǐng)求到服務(wù)端等。我們來看一下最后幾行 代碼,RealInterceptorChain 是一個(gè)Interceptor.Chain類型,并執(zhí)行chain.proceed,接著看一下 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 下面幾行代碼,他把當(dāng)前的interceptor.intercept()時(shí),傳入的是下一個(gè)interceptor的包裝類,RealInterceptorChain 這樣就實(shí)現(xiàn)了,鏈?zhǔn)竭f歸調(diào)用了,直到最后一個(gè)response返回,才會(huì)依次返回到第一個(gè)interceptor。

可以用如下圖大致描述:



講了那么多相關(guān)的知識(shí)點(diǎn),我們來回到正題,上述推薦小工具的實(shí)現(xiàn)步驟介紹


抓包工具實(shí)現(xiàn)主要步驟介紹


添加一個(gè)抓包入口

在Manifest中注冊(cè)即可,如下


<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>


暴露Interceptor

public final class CaptureInfoInterceptor implements Interceptor{    @Override public Response intercept(Chain chain) throws IOException {    //獲取request 所有信息    ...    //獲取response 所有信息    ...        //存儲(chǔ)抓包數(shù)據(jù)    CacheUtils.getInstance().saveCapture(request.url().toString(),captureEntity);    }}



這里其中有兩種方式,添加到OkHttp的Interceptor,一種硬編碼,如下

new OkHttpClient.Builder()   .addInterceptor(new CaptureInfoInterceptor())   .build();


另一種方式 采用字節(jié)碼注入的形式,關(guān)于字節(jié)碼注入,可以簡(jiǎn)單參考我的另一篇Gradle學(xué)習(xí)筆記,自定義 Transform部分。


存儲(chǔ)和讀取抓包數(shù)據(jù) 效率問題


存儲(chǔ)時(shí),為了不影響到主APP的網(wǎng)絡(luò)請(qǐng)求效率,需要在單獨(dú)的線程中執(zhí)行IO操作,這里使用了單線程池

public class DiskIOThreadExecutor implements Executor {   private final Executor mDiskIO;   public DiskIOThreadExecutor() {       mDiskIO = Executors.newSingleThreadExecutor();   }   @Override   public void execute(@NonNull Runnable command) {       mDiskIO.execute(command);   }}

存儲(chǔ)


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ù)據(jù)時(shí),不直接讀取全部的數(shù)據(jù),只讀取當(dāng)前抓包的目錄,數(shù)據(jù),點(diǎn)擊時(shí),在去加載對(duì)應(yīng)的數(shù)據(jù)

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è)小工具,就介紹那么多了,具體細(xì)節(jié)代碼,可以直接查看Github代碼倉(cāng)庫(kù),github.com/DingProg/Ne…


總結(jié)


其實(shí)關(guān)于抓包工具,有一些成熟的方案。

  • 電腦端的有Fiddler、Charels,Wireshark等,但是不是特別方便。

  • APP可以抓其他包的工具,如NetWorkPacketCapture/抓包精靈/AndroidHttpCapture,但是都一些限制條件,要么代碼沒開源,廣告多。要么就是只能在WIFI下,或者要么就是需要Root等,不太好定制。


本文,主要是介紹OkHttp的攔截器,并從中發(fā)現(xiàn)可以干很多事情。如文中有錯(cuò)誤,還忘指正,感謝。



關(guān)鍵字: OKHttp 攔截器 開封網(wǎng)站建設(shè)

文章連接: http://www.hsjyfc.com.cn/wzjss/649.html

版權(quán)聲明:文章由 晨展科技 整理收集,來源于互聯(lián)網(wǎng)或者用戶投稿,如有侵權(quán),請(qǐng)聯(lián)系我們,我們會(huì)立即刪除。如轉(zhuǎn)載請(qǐng)保留