Android webView - 拦截所有请求 - 基于 api 响应的 Url 过滤

问题描述 投票:0回答:2

我正在尝试拦截 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的异步,这是最大的问题。

我将不胜感激任何帮助,如果您需要更多代码,我将编辑问题并复制它。非常感谢您的帮助。

android kotlin webview retrofit2 kotlin-coroutines
2个回答
0
投票

我已经找到了这个解决方案,我认为它不是最好的,但它可以达到目的

自定义网络客户端停止加载并调用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 方法中进行一次调用。我知道这不是最好的解决方案,但它似乎有效。

欢迎任何改进代码逻辑的帮助!


0
投票

此解决方案避免了 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)

    }

一如既往,它可以改进很多,我很感激任何改进它的提示或建议。

© www.soinside.com 2019 - 2024. All rights reserved.