You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android WebView基于API响应拦截请求并过滤URL的问题

问题根源分析
  1. 异步请求与同步拦截方法不兼容:shouldInterceptRequest是同步执行的方法,但你在里面发起了异步API请求,此时shouldBlock的状态还未被API响应更新,导致拦截逻辑完全失效,后续调用loadUrl又触发新请求,形成循环。
  2. LiveData重复订阅:每次调用filterRequest都会给filterResult添加新的Observer,未移除旧订阅,导致同一响应触发多次处理。
  3. 全局状态变量竞争:shouldBlock作为全局变量,多请求同时处理时会互相覆盖状态,引发逻辑混乱。
  4. 不当的页面加载触发:在filterRequest和shouldInterceptRequest中重复调用loadUrl,每次加载都会触发新的请求拦截,无限循环。
修复方案

1. 重构ViewModel,处理单次事件

给LiveData添加单次事件包装类,避免粘性事件和重复回调:

class FilterViewModel : ViewModel() {
    private val _filterResult = MutableLiveData<Event<WebFilterModel>>()
    val filterResult: LiveData<Event<WebFilterModel>> = _filterResult

    fun onFilter(url: String) {
        viewModelScope.launch {
            val result = filterUseCase.invoke(url)
            if (result.isSuccessful) {
                result.body()?.let {
                    _filterResult.value = Event(it)
                }
            }
        }
    }

    // 单次事件包装类,防止LiveData重复回调
    data class Event<out T>(private val content: T) {
        var hasBeenHandled = false
            private set

        fun getContentIfNotHandled(): T? {
            return if (hasBeenHandled) {
                null
            } else {
                hasBeenHandled = true
                content
            }
        }
    }
}

2. 重写WebViewClient逻辑,分离同步与异步处理

放弃在shouldInterceptRequest中处理异步逻辑,改用shouldOverrideUrlLoading拦截初始请求,异步处理后再决定加载行为:

class CustomWebViewClient(private val viewModel: FilterViewModel, private val activity: Activity) : WebViewClient() {
    // 标记当前是否正在处理过滤,防止重复请求
    private var isFiltering = false

    init {
        // 仅订阅一次LiveData,避免重复回调
        viewModel.filterResult.observe(activity) { event ->
            event.getContentIfNotHandled()?.let { filterModel ->
                isFiltering = false
                val webView = activity.findViewById<WebView>(R.id.web_view) // 替换为你的WebView ID
                if (filterModel.result == "ok") {
                    webView.loadUrl(filterModel.loadFilterUrl)
                } else {
                    webView.loadUrl("https://www.google.com")
                }
            }
        }
    }

    override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
        val url = request.url.toString()
        // 正在过滤时拦截重复请求
        if (isFiltering) {
            return true
        }
        // 触发异步过滤请求,阻止WebView立即加载
        isFiltering = true
        viewModel.onFilter(url)
        return true
    }

    // 仅在该方法中处理同步拦截逻辑(如已知黑名单资源)
    override fun shouldInterceptRequest(
        view: WebView?,
        request: WebResourceRequest?
    ): WebResourceResponse? {
        return super.shouldInterceptRequest(view, request)
    }
}

3. 关键优化点

  • 使用单次事件包装类处理LiveData,避免粘性事件导致的重复处理。
  • 在shouldOverrideUrlLoading中拦截请求,返回true阻止WebView立即加载,等待异步API响应后再加载目标URL。
  • 用isFiltering标记防止同一请求被重复处理,避免循环。
  • 移除GlobalScope,改用ViewModel的viewModelScope管理协程生命周期。
  • 删除全局shouldBlock变量,通过LiveData单次事件传递状态,避免多请求状态竞争。
额外注意事项
  • 确保WebView的webViewClient设置为自定义的CustomWebViewClient,并传入正确的ViewModel和Activity实例。
  • 仅对主页面URL使用异步过滤逻辑,不要在子资源请求(如图片、脚本)中触发异步API,否则会导致大量请求和循环。

内容的提问来源于stack exchange,提问作者Thatta Systems

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 17:15:34