给 WebView 加载流程添加一个拦截器

345 阅读2分钟

需求:需要一个 WebView 作为运营活动的 Web 页面的容器,其中需要实现拦截 url 的功能,对符合某些规则的 url 进行替换

方式: 通过拦截器实现对流程的解耦

介绍:什么是拦截器 借鉴okhttp的Interceptors Interceptor 可以获取到一个当前的 request 和 response,实现「监测」;在此基础上,每个 Interceptor 可以修改传给下一个 Interceptor 的 request 和返回给上一个 Interceptor 的 response,实现对数据的「重写」。

这时okhttp的示例图:

image.png

多个 Interceptor 构成一个调用栈,每个 Interceptor 可以在执行 chain.proceed 之前修改 request,在 return response 之前修改 response。整体结构有些像递归调用,只是每个Interceptor调用的不是自身的 intercept 方法,而是下一个 Interceptor 的 intercept 方法。

拦截器简单构成:

1.IWebInterceptor

interface IWebInterceptor {
    suspend fun intercept(chain: Chain): Response
    interface Chain {
        fun request(): Request
        suspend fun proceed(request: Request): Response
    }
}

2.RealInterceptorChain主要作用就是操作拦截器的集合类,通过链式递归调用了,直到最后一个response返回,才会依次返回到第一个interceptor。

class RealInterceptorChain(
    interceptor: List<IWebInterceptor>?,
    index: Int,
    request: Request) : IWebInterceptor.Chain {
    private val interceptors: List<IWebInterceptor>? = interceptor

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f9e7813595934225a67defa95f686896~tplv-k3u1fbpfcp-watermark.image?)
    private val index = index
    private val request: Request = request


    override fun request(): Request {
        return request
    }

    override suspend fun proceed(request: Request): Response {
        if (index >= (interceptors?.size ?: 0)) return Response(request.originalData)

        //调用下一个在chain中的拦截器
        val next = RealInterceptorChain(
            interceptors, index + 1, request
        )
        val interceptor: IWebInterceptor = interceptors!![index]
        return interceptor.intercept(next)
    }
}

运用:

val interceptors: MutableList<IWebInterceptor> = ArrayList()
//业务逻辑 start
interceptors.add(WillActInterceptor())
interceptors.add(WebSpecificInterceptor())
//业务逻辑 end
interceptors.add(WebNormalInterceptor())

val request = Request(data)
val chain = RealInterceptorChain(
    interceptors,
    0,
    Request(data))
val response = chain.proceed(request)

具体拦截器实现

/**
 * 拦截活动链接
 */
class WillActInterceptor : IWebInterceptor {
    override suspend fun intercept(chain: IWebInterceptor.Chain): Response {
        val request = chain.request()
        val data = request.originalData
        val url = data.getString("url")

        return if (true == url?.contains("activity/cultural")) {
            data.putString("url",getH5Url())
            Response(data)
        } else {
            chain.proceed(request)
        }
    }

  
class WebSpecificInterceptor : IWebInterceptor {
    override suspend fun intercept(chain: IWebInterceptor.Chain): Response {
        val request = chain.request()
        val data = request.originalData
        val url = data.getString("url")
        val urlType = data.getString("url_type")

        if (true == url?.startsWith("youth_app://")) {
            return Response(data, false, url)
        }

        return when (urlType) {
            "agreement" -> {
                data.putString("url", HTTP_AGREEMENT)
                Response(data)
            }
            "privacy" -> {
                data.putString("url", HTTP_PRIVACY)
                Response(data)
            }
            else -> {
                chain.proceed(request)
            }
        }
    }