【发布时间】:2022-01-01 23:04:20
【问题描述】:
我有一个混合应用程序;我的一些活动使用 WebView 来显示 Web 内容。我在 WebView 中显示的 Web 应用程序有一个 JS 界面,可让我向 Web 应用程序发送命令以导航不同的位置或执行其他操作。
例如,如果我需要我的网络应用导航到“用户配置文件”页面,我执行如下命令:
class SomeActivity: AppCompatActivity {
...
webView.evaluateJavascript("navigateTo(\"userprofile\")")
...
}
然后,我通过 JS 接口得到响应,应用程序做出相应的反应。
我引入了一个JS队列来提高性能,所以JS命令是顺序执行的。我没有直接在 WebView 上调用 evaluateJavascript() 函数,而是创建了一个自定义 WebView 组件,将此 JS 队列设置为属性。
class SomeActivity: AppCompatActivity {
...
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
...
}
现在我想在此基础上添加一个新行为,即能够预处理队列中的命令。我所说的预处理的意思是,如果我曾经对相同“类型”的命令进行排队,例如:
class SomeActivity: AppCompatActivity {
...
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"about-me\")")
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"user-list\")")
...
}
我想要发生的是队列足够聪明,可以放弃这两个第一个“导航”命令 - "navigateTo(\"userprofile\")" 和 "navigateTo(\"about-me\")" - 因为我不希望我的 WebView 导航到这两个地方只是为了最后导航到"navigateTo(\"user-list\")"。
这个JS队列的实现是这样的:
class JsQueue(
private val webView: WebView,
private val scope: CoroutineScope
) {
init {
scope.launch {
for (jsScript in jsChannel) {
runJs(jsScript)
}
}
}
private val jsChannel = Channel<String>(BUFFERED)
fun queueEvaluateJavascript(script: String) {
runBlocking {
jsChannel.send(script)
}
}
suspend fun runJs(script: String) = suspendCoroutine<String> { cont ->
webView.evaluateJavascript(script) { result ->
cont.resume(result)
}
}
}
- 如何预处理
Channel<String>中的js命令,所以我 抛弃重复的 js 命令? - 另外,有时我的 WebView 会变得不可见,我想在这种情况发生时暂停队列。我想知道是否有任何方法可以以编程方式暂停频道?
编辑#1
另外,有时我的 WebView 会变得不可见,我想 发生这种情况时暂停队列。我想知道是否有任何方法可以以编程方式暂停 Channel?
我尝试过使用 this PausableDispatcher 实现,它似乎可以解决问题。
【问题讨论】:
-
在 JavaScript 中实现这种行为会比尝试捕获
evaluateJavascript调用更容易吗?如果您必须在 Kotlin 中执行此操作,您可以轻松创建queueJavascript和executeQueue方法,它们有选择地将给定的命令保存在数组中,并在运行executeQueue命令时通过evaluateJavascript调用所有命令。
标签: android kotlin webview kotlin-coroutines kotlinx.coroutines.channels