Android WebView基于API响应拦截请求并过滤URL的问题
问题根源分析
- 异步请求与同步拦截方法不兼容:
shouldInterceptRequest是同步执行的方法,但你在里面发起了异步API请求,此时shouldBlock的状态还未被API响应更新,导致拦截逻辑完全失效,后续调用loadUrl又触发新请求,形成循环。 - LiveData重复订阅:每次调用
filterRequest都会给filterResult添加新的Observer,未移除旧订阅,导致同一响应触发多次处理。 - 全局状态变量竞争:
shouldBlock作为全局变量,多请求同时处理时会互相覆盖状态,引发逻辑混乱。 - 不当的页面加载触发:在
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
相关产品推荐
相关产品推荐

