前陣子定了個小目標,打算來深入瞭解下幾個常用的開源庫,看下其源碼和實現原理,進行總結並輸出成文章。初定的目標是 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