我正在尝试拦截 webview 请求并根据 API 响应过滤它们。我正在使用一个 viewModel,它有一个公共的 onFilter 方法,可以通过存储库调用 API:
private val _filterResult = MutableLiveData<WebFilterModel>()
val filterResult: LiveData<WebFilterModel> = _filterResult
fun onFilter(url: String) {
viewModelScope.launch(Dispatchers.Main) {
val result = filterUseCase.invoke(url)
if (result.isSuccessful) {
Log.d("filter", "result onFilter: " + result.body()!!.result)
_filterResult.value = result.body()
}
}
}
viewModel 有一个 LiveData 类型的变量,它在我的自定义 webView 客户端的私有方法中观察到,这个称为 filterRequest 的方法用于 web 客户端的重写方法,shouldInterceptRequest。有时,shouldOverrideLoading 方法返回 false:
@OptIn(DelicateCoroutinesApi::class)
private fun filterRequest(view: WebView, request: WebResourceRequest?) {
GlobalScope.launch(Dispatchers.Main) {
viewModel.onFilter(request!!.url.toString())
viewModel.filterResult.observe(activity, Observer{
if (it.result == "ok") {
shouldBlock = false
view.loadUrl(it.loadFilterUrl)
Log.d("filter", "filterRequest ok")
} else {
view.post(Runnable {
view.stopLoading()
})
val url = "https://www.google.com"
shouldBlock = true
view.post(Runnable {
view.loadUrl(url)
})
Log.d("filter", "filterRequest no")
}
})
}
}
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
return false
}
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
var webResourceResponse: WebResourceResponse? = null
filterRequest(view!!, request)
if (shouldBlock) {
Log.d("filtro", "interceptRequest NULL")
webResourceResponse = WebResourceResponse("text/javascript", "UTF-8", null)
}
if (!shouldBlock) {
view!!.post(Runnable {
view.loadUrl(BrowserAPIProvider.filter.loadFilterUrl)
webResourceResponse = null
})
}
return webResourceResponse
}
我遇到的主要问题是调用是在无限循环中产生的,网络无法加载并且每秒发出许多请求。
这是代码的当前状态,但是有些代码行来自更改和测试,我不知道如何在webview重写方法中开发与api的异步,这是最大的问题。
我将不胜感激任何帮助,如果您需要更多代码,我将编辑问题并复制它。非常感谢您的帮助。
我已经找到了这个解决方案,我认为它不是最好的,但它可以达到目的
自定义网络客户端停止加载并调用api:
var shouldBlock: Boolean? = null
private var yetFilter = false
private fun filterRequest(view: WebView, request: WebResourceRequest?) {
view.post(Runnable {
view.stopLoading()
})
viewModel.onFilter(request!!.url.toString())
yetFilter = true
}
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
yetFilter = false
return false
}
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
var webResourceResponse: WebResourceResponse? = null
if (!yetFilter) {
filterRequest(view!!, request)
}
if (shouldBlock != null) {
if (shouldBlock == true) {
Log.d("filter", "interceptRequest NULL")
webResourceResponse = null
} else {
webResourceResponse = super.shouldInterceptRequest(view, request)
Log.d("filter", "interceptRequest LOAD OK")
}
}
return webResourceResponse
}
当存放webview的fragment通过LiveData观察器收到api的response时,根据response加载:
viewModel.filterResult.observe(viewLifecycleOwner, Observer {
if (it.result == "ok") {
objectWebClient.shouldBlock = false
binding.webView.loadUrl(it.loadFilterUrl)
Log.d("filter", "filterRequest ok")
} else {
val url = "https://www.google.com"
objectWebClient.shouldBlock = true
binding.webView.loadUrl(url)
Log.d("filter", "filterRequest no")
}
objectWebClient.shouldBlock = null
})
当再次启动 Web 请求时,再次设置变量以允许在 shouldOverrideUrlLoading 方法中进行一次调用。我知道这不是最好的解决方案,但它似乎有效。
欢迎任何改进代码逻辑的帮助!
此解决方案避免了 WebView 组件事件处理中的深层错误。
我已经将 api 调用移至 Web 客户端,这使得实现 api 调用更加容易。我会尽力以最好的方式解释解决方案:
shouldOverrideUrlLoading 方法是第一个在我实现了 api 调用的客户端中调用的方法,它的优点是通过 webview.loadUrl() 或通过内部导航调用 webview 组件的每个新加载方法组件:
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
nextUrl = leanGoogleNextUrl(request.url.toString())
cleanGoogleNextUrl(nextUrl)
if (!yetFilter && !shouldLoad) {
view.post(Runnable {
kotlin.run {
view.stopLoading()
filterRequest(view)
}
})
}
return false
}
这些是调用 API 的方法,第一个使用回调,因为我需要通过具有两个布尔值的结构来控制过滤器,这两个布尔值补充了过滤器的建立方式,因为我遇到了一个问题面临的是Loading each web component递归的经过了webview客户端的一些方法
private fun onAirFilter(
url: String,
filterCallback: (filtered: Boolean, mustFilter: Boolean) -> Unit
) {
if (!ActiveTabs.forcedTabsMode) {
Application().applicationScope.launch(Dispatchers.Main) {
val urlToTest = checkSafeBrowsing(url)
val result = filterUseCase.invoke(urlToTest)
if (result.isSuccessful) {
if (result.body()!!.result == "000") {
filterCallback.invoke(true, false)
} else {
filterCallback.invoke(true, true)
}
shouldLoad = true
}
}
}
}
private fun filterRequest(view: WebView) {
view.post(Runnable {
kotlin.run {
if (!yetFilter) {
view.stopLoading()
onAirFilter(nextUrl) { filtered, mustFilter ->
if (!mustFilter) {
view.loadUrl(nextUrl)
} else {
loadLazarusErrorPage(nextUrl, "1", view)
}
setYetFilterState(filtered)
}
}
}
})
}
private fun setYetFilterState(state: Boolean) {
yetFilter = state
}
我认为重要的webview客户端的最后一个方法是重置控制是否需要调用api的变量。
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
if (shouldLoad) {
setYetFilterState(false)
}
return super.shouldInterceptRequest(view, request)
}
一如既往,它可以改进很多,我很感激任何改进它的提示或建议。