查看原文
其他

Android 主流开源库源码分析(十二)- OkHttp & Retrofit 开发调试利器

业志陈 字节数组 2023-09-13

对于 Android Developer 来说,很多开源库都是属于开发必备的知识点,从使用方式到实现原理再到源码解析,这些都需要我们有一定程度的了解和运用能力。所以我打算来写一系列关于开源库源码解析实战演练的文章,初定的目标是 EventBus、ARouter、LeakCanary、Retrofit、Glide、OkHttp、Coil 等七个知名开源库,希望对你有所帮助 🤣🤣

在使用 OkHttp 或者 Retrofit 的时候,我觉得大部分开发者做得最多的自定义实现就是拦截器了。因为 OkHttp 的拦截器真的是太有用了,我们的很多需求:添加 Header、计算并添加签名信息、网络请求记录等都可以通过拦截器来自动完成,只要定义好规则,就可以覆盖到全局的 OkHttp 网络请求

按照我写 [Android 主流开源库源码分析] 这系列文章的习惯,我是每写一篇关于源码讲解的文章,就会接着写一篇关于该三方库的自定义实现或者是扩展阅读。所以,承接上一篇文章,本篇文章就来写关于 OkHttp 的实战内容,来实现一个在移动端的可视化抓包工具

Part1一、Monitor

Monitor 适用于使用了 OkHttp/Retrofit 作为网络请求框架的项目,只要添加了 MonitorInterceptor 拦截器,Monitor 就会自动记录并保存所有的网络请求信息且自动弹窗展示

最后实现的效果如下所示:

Part2二、实现思路

这里来简单地介绍下 Monitor 的实现思路

其实 Monitor 是我蛮久前写的一个开源库了,刚好和我现在要写的文章主题相符,就趁着这机会做了一次整体重构,完全使用 Kotlin 语言来实现,请放心食用。其核心思路就是通过 Interceptor 拿到 Request 和 Response,记录各项请求信息,存到数据库中进行持久化保存,在实现思路上类似于 squareup 官方的ogging-interceptor,只是说 Monitor 会更加直接和方便 😋😋

debug 版本的 MonitorInterceptor 的大体框架如下所示。HttpInformation 是对 request 和 response 的一个实体封装,也是最终会存到数据库中的实体类。通过 chain 拿到 request,先对本地数据库进行预先占位,在 proceed 后拿到 response,对本次请求结果进行解析,所有信息都存到 HttpInformation 中再来更新数据库,同时弹出 Notification

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */

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: leavesCZY
 * @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)
        })
    }
}

Part3三、远程引用

代码我已经发布到了 jitpack,方便大家直接远程依赖使用

同时引入 debug 和 release 版本的依赖,release 版本的 MonitorInterceptor 不会做任何操作,避免了信息泄露,也不会增加 Apk 体积大小

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

dependencies {
    debugImplementation 'com.github.leavesCZY.Monitor:monitor:1.1.5'
    releaseImplementation 'com.github.leavesCZY.Monitor:monitor-no-op:1.1.5'
}

只要向 OkHttpClient 添加了 MonitorInterceptor,之后的操作就都会自动完成

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(MonitorInterceptor(context = application))
    .build()

Part4四、Github

GitHub 链接点击这里:https://github.com/leavesCZY/Monitor

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存