12

android-网络框架解析–第2篇OkHttp(4.0.x)—请求和响应流程(interceptors)

 3 years ago
source link: http://www.demanmath.com/index.php/2020/10/14/android-wangluokuangjiajiexidi2pianokhttp4-0-xqingqiuhexiangyingliuchenginterceptors/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

请求和响应流程

见图。

OkHttp%E6%97%B6%E8%AE%B8%E5%9B%BE.png
所有的流程是这个时序图。
整个流程处理的网络链接和通信是在ConnectInterceptor & CallInterceptor来进行的。

其中interceptors和netinterceptors是user自定义的拦截器。

interceptor

自定义的拦截器。但是这里的和view的事件分发事件里的拦截器略有区别。
这里不是真正的拦截,因为拦截会打断整个网络请求的流程,所以给予user一个处理事物的入口。
常见的拦截器配置如下

日志拦截器

override fun intercept(chain: Interceptor.Chain): Response {
        val TAG = "OKHttp"
        val request = chain.request()
        BaseLog.i(TAG+request.hashCode(),":${request.url}")

        val headers = request.headers
        for(i in 0 until headers.size){
            var name = headers.name(i)
            if (name == "timestamp" || name == "sign" || name == "openid") {
                continue
            }
            if (!"Content-Type".equals(name, ignoreCase = true) && !"Content-Length".equals(name, ignoreCase = true)) {
                BaseLog.i(TAG + request.hashCode(), name + ": " + headers.value(i))
            }
        }

        var requestBody = request.body
        requestBody?.let {
            var buffer = Buffer()
            if(isPlaintext(buffer)){
                try {
                    requestBody.writeTo(buffer)
                } catch (e: IOException) {
                    e.printStackTrace()
                }
                val contentType = requestBody.contentType()
                if (contentType != null) {
                    val charset =
                        contentType.charset(Charset.forName("UTF-8"))
                    if (charset != null) {
                        BaseLog.i(
                            TAG + request.hashCode(),
                            "requestBody: " + buffer.readString(charset)
                        )
                    }
                }
            }
        }

        val response = chain.proceed(request)
        var responseBody = response.peekBody(1024*1024)
        BaseLog.i(TAG + request.hashCode(), "responseBody: ${responseBody.string()}")

        return response
    }

    private fun isPlaintext(buffer: Buffer): Boolean {
        return try {
            val prefix = Buffer()
            val byteCount = if (buffer.size < 64) buffer.size else 64
            buffer.copyTo(prefix, 0, byteCount)
            for (i in 0..15) {
                if (prefix.exhausted()) {
                    break
                }
                val codePoint = prefix.readUtf8CodePoint()
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(
                        codePoint
                    )
                ) {
                    return false
                }
            }
            true
        } catch (e: EOFException) {
            false // Truncated UTF-8 sequence.
        }
    }

header处理

    override fun intercept(chain: Interceptor.Chain): Response {
        val request =  chain.request()
        val builder = request.newBuilder()
        builder.addHeader("Content-type", "application/json; charset=utf-8")
            .addHeader("Accept", "application/json")
            .addHeader("os", "Android")
        return chain.proceed(request)
    }

RetryInterceptor

重试,这个retry和RetryAndFollowUpInterceptor不是一个东西。那个是重定向interceptor

    private var retryNum = 0;

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        BaseLog.d("retryNum:$retryNum")
        var response = chain.proceed(request)
        while (!response.isSuccessful && retryNum<maxRetry){
            retryNum++
            BaseLog.d("retryNum:$retryNum")
            response = chain.proceed(request)
        }
        return response
    }

netInterceptors

这个设计,是因为interceptors是网路链接之前的数据,这个阶段是拿不到网络请求的相关资料,比如请求端口。

获取本地请求ip&端口

通过android自带的ipaddress是拿不到这个port字段的,通过分析源码,OkHttp是通过Socket链接的,这个是可以拿到port的。

override fun intercept(chain: Interceptor.Chain): Response {
        val request =  chain.request()
        try {
            val realChain = chain as RealInterceptorChain
            val realConnect = realChain.exchange().connection()
            val socket = realConnect?.socket()
            socket?.let {
                AppLog.i("local address:${socket.localAddress.hostAddress},local port:${socket.localPort}")
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return chain.proceed(request)
    }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK