三方庫源碼筆記(12)-OkHttp / Retrofit 開發調試利器

前陣子定了個小目標,打算來深入瞭解下幾個常用的開源庫,看下其源碼和實現原理,進行總結並輸出成文章。初定的目標是 EventBus、ARouter、LeakCanary、Retrofit、Glide、OkHttp、Coil 等七個。目前已經完成了十一篇關於 EventBus、ARouter、LeakCanary、Retrofit、Glide、OkHttp 的文章,本篇是第十二篇,是關於 OkHttp 的實戰內容,來實現一個 OkHttp 的可視化抓包工具,希望對你有所幫助😎😎

在使用 OkHttp 或者 Retrofit 的時候,我覺得大部分開發者會做得最多的自定義實現就是攔截器了。因爲 OkHttp 的攔截器真的是太有用了,我們的很多需求:添加 Header、計算並添加簽名信息、網絡請求記錄等都可以通過攔截器來自動完成,只要定義好規則,就可以覆蓋到全局的 OkHttp 網絡請求

按照我寫 [三方庫源碼筆記] 這系列文章的習慣,我是每寫一篇關於源碼講解的文章,就會接着寫一篇關於該三方庫的自定義實現或者是擴展閱讀。所以,承接上一篇文章:三方庫源碼筆記(11)-OkHttp 源碼詳解 本篇文章就來寫關於 OkHttp 的實戰內容,來實現一個在移動端的可視化抓包工具:Monitor

一、Monitor

Monitor 適用於使用了 OkHttp/Retrofit 作爲網絡請求框架的項目,只要添加了 MonitorInterceptor 攔截器,Monitor 就會自動記錄並保存所有的網絡請求信息且自動彈窗展示

最後實現的效果如下所示:

二、實現思路

這裏來簡單地介紹下 Monitor 的實現思路

其實 Monitor 是我蠻久前寫的一個開源庫了,剛好和我現在要寫的文章主題相符,就趁着這機會做了一次整體重構,完全使用 Kotlin 語言來實現,請放心食用。其核心思路就是通過 Interceptor 拿到 Request 和 Response,記錄各項請求信息,存到數據庫中進行持久化保存,在實現思路上類似於 squareup 官方的ogging-interceptor,只是說 Monitor 會更加直接和方便😋😋

debug 版本的 MonitorInterceptor 的大體框架如下所示。HttpInformation 是對 request 和 response 的一個實體封裝,也是最終會存到數據庫中的實體類。通過 chain 拿到 request,先對本地數據庫進行預先佔位,在 proceed 後拿到 response,對本次請求結果進行解析,所有信息都存到 HttpInformation 中再來更新數據庫,同時彈出 Notification

/**
 * 作者:leavesC
 * 時間:2020/10/20 18:26
 * 描述:
 * GitHub:https://github.com/leavesC
 */
class MonitorInterceptor(context: Context) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val httpInformation = HttpInformation()
        processRequest(request, httpInformation)
        httpInformation.id = insert(httpInformation)
        val response: Response
        try {
            response = chain.proceed(request)
        } catch (e: Throwable) {
            httpInformation.error = e.toString()
            throw e
        } finally {
            update(httpInformation)
        }
        processResponse(response, httpInformation)
        update(httpInformation)
        return response
    }

    private fun processRequest(request: Request, httpInformation: HttpInformation) {
        ···
    }

    private fun processResponse(response: Response, httpInformation: HttpInformation) {
        ···
    }

    private fun showNotification(httpInformation: HttpInformation) {
        ···
    }

}

release 版本的 MonitorInterceptor 則不會做任何操作,只是單純將請求轉發出去而已,不會造成多餘的性能消耗和引用

class MonitorInterceptor(context: Context) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        return chain.proceed(request)
    }

}

HttpInformation 包含了單次網絡請求下所有關於 request 和 response 的請求參數和返回值信息,responseBody 只會保存文本類型的返回值(例如 Json 和 XML),圖片這類二進制文件則不會進行保存

class HttpInformation {
    
    var url = ""
    var host = ""
    var path = ""
    var scheme = ""
    var protocol = ""
    var method = ""

    var requestHeaders = mutableListOf<HttpHeader>()
    var responseHeaders = mutableListOf<HttpHeader>()

    var requestBody = ""
    var requestContentType = ""
    var requestContentLength = 0L

    var responseBody = ""
    var responseContentType = ""
    var responseContentLength = 0L

    var requestDate = 0L
    var responseDate = 0L

    var responseTlsVersion = ""
    var responseCipherSuite = ""

    var responseCode = DEFAULT_RESPONSE_CODE
    var responseMessage = ""

    var error: String? = null
    
}

HttpInformation 則是用 Room 數據庫來持久化保存,不得不說的是,Jetpack 中的 Room 和 LiveData 來搭配使用還是很爽的,將 LiveData 作爲數據庫的返回值,可以很方便地以觀察者模式來實時監聽數據庫中的數據變化

/**
 * @Author: leavesC
 * @Date: 2020/11/14 16:14
 * @Desc:
 */
@Dao
interface MonitorHttpInformationDao {

    @Query("SELECT * FROM monitor_httpInformation WHERE id =:id")
    fun queryRecordObservable(id: Long): LiveData<HttpInformation>

    @Query("SELECT * FROM monitor_httpInformation order by id desc limit :limit")
    fun queryAllRecordObservable(limit: Int): LiveData<List<HttpInformation>>

    @Query("SELECT * FROM monitor_httpInformation order by id desc")
    fun queryAllRecordObservable(): LiveData<List<HttpInformation>>
    
}

UI 層則不用自己去考慮線程切換和內存泄露這類問題,直接進行 observe 即可

    private val monitorViewModel by lazy {
        ViewModelProvider(this).get(MonitorViewModel::class.java).apply {
            allRecordLiveData.observe(this@MonitorActivity, Observer {
                monitorAdapter.setData(it)
            })
        }
    }

三、遠程引用

代碼我已經發布到了 jitpack,方便大家直接遠程依賴使用。同時引入 debug 和 release 版本的依賴,release 版本的 MonitorInterceptor 不會做任何操作,避免了信息泄露,也不會增加 Apk 體積大小

        allprojects {
            repositories {
                maven { url 'https://jitpack.io' }
            }
        }

        dependencies {
           debugImplementation 'com.github.leavesC.Monitor:monitor:1.1.3'
           releaseImplementation 'com.github.leavesC.Monitor:monitor-no-op:1.1.3'
        }

只要向 OkHttpClient 添加了 MonitorInterceptor,之後的操作就都會自動完成

        val okHttpClient = OkHttpClient.Builder()
            .addInterceptor(MonitorInterceptor(Context))
            .build()

四、Github

GitHub 鏈接點擊這裏:Monitor

一個人走得快,一羣人走得遠,寫了文章就只有自己看那得有多孤單,只希望對你有所幫助😂😂😂

查看更多文章請點擊關注:字節數組

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章