运行 使用 Kotlin 协程按顺序评估 Javascript() 函数
Run evaluateJavascript() function sequentially using Kotlin Corutines
我正在做一个宠物项目,我正在尝试使用 WebView
创建一个混合应用程序。我在WebView
中运行的网络平台通过一个@JavascriptInterface
对象向WebView/App发送事件。我还可以 命令 网络导航,方法是 运行 使用 evaluateJavascript(String, (String) -> Unit)
通过 WebView
针对网络平台设置一组 javascript 函数] 函数。
我现在想要实现的是,我通过evaluateJavascript(String, (String) -> Unit)
函数运行顺序执行的这些命令。我可能同时从许多不同的地方执行这些命令,所以我希望它们 运行,等待 evaluateJavascript()
函数的回调被调用,然后执行队列中的下一个命令.
这是我的习惯 WebView
class:
val scriptQueue = mutableListOf<String>()
fun queueEvaluateJavascript(script: String) {
if (webViewIsLoading) {
scriptQueue.add(script)
} else {
scriptQueue.add(script)
runScriptQueue()
}
}
fun runScriptQueue() {
for (script in scriptQueue) {
evaluateJavascript(script, { })
}
scriptQueue.clear()
}
如您所见,这是一种超级基本的方法,我并没有真正考虑 evaluateJavascript()
回调。理想情况下,我想找到一种方法 flat map 每个 evaluateJavascript()
调用,这样我们一个接一个地执行,但等待回调完成。
使用 RxJava 我想我会创建一个 Observable,然后让 evaluateJavascript()
回调触发订阅者的 onNext()
。因为,我正在使用 Kotlin Coroutines 我想用 Coroutines 做一些事情,所以我可以对这些 evaulateJavascript()
调用进行排队。但我不是 100% 确定这里的等价物是什么。
用协程解决这个问题会很好。
将基于回调的 API 转换为挂起函数的常用方法如下:
suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
然后您可以将它与 Channel
(用作队列)和处理此通道的协程结合使用:
class MyWebView(context: Context) : WebView(context) {
private val jsQueue = Channel<String>(BUFFERED)
fun startJsProcessingLoopIn(scope: CoroutineScope) {
scope.launch {
for (script in jsQueue) {
evaluateJs(script)
}
}
}
// you could also make this function non-suspend if necessary by calling
// sendBlocking (or trySend depending on coroutines version)
suspend fun queueEvaluateJavascript(script: String) {
jsQueue.send(script)
}
private suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
}
或者你可以创建你自己的协程作用域,并确保将它与你的 webview 的某种生命周期联系起来(我不熟悉 WebView
所以我会让你判断哪种方法正确):
class MyWebView2(context: Context) : WebView(context) {
// you can even further customize the exact thread pool used here
// by providing a particular dispatcher
private val jsProcessingScope = CoroutineScope(CoroutineName("js-processing"))
private val jsQueue = Channel<String>(BUFFERED)
// this starts the loop right away but you can also put this in a method
// to start it at a more appropriate moment
init {
jsProcessingScope.launch {
for (script in jsQueue) {
evaluateJs(script)
}
}
}
// you could also make this function non-suspend if necessary by calling
// sendBlocking (or trySend depending on coroutines version)
suspend fun queueEvaluateJavascript(script: String) {
jsQueue.send(script)
}
private suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
fun someCloseOrDisposeCallback() {
jsProcessingScope.cancel()
}
}
我正在做一个宠物项目,我正在尝试使用 WebView
创建一个混合应用程序。我在WebView
中运行的网络平台通过一个@JavascriptInterface
对象向WebView/App发送事件。我还可以 命令 网络导航,方法是 运行 使用 evaluateJavascript(String, (String) -> Unit)
通过 WebView
针对网络平台设置一组 javascript 函数] 函数。
我现在想要实现的是,我通过evaluateJavascript(String, (String) -> Unit)
函数运行顺序执行的这些命令。我可能同时从许多不同的地方执行这些命令,所以我希望它们 运行,等待 evaluateJavascript()
函数的回调被调用,然后执行队列中的下一个命令.
这是我的习惯 WebView
class:
val scriptQueue = mutableListOf<String>()
fun queueEvaluateJavascript(script: String) {
if (webViewIsLoading) {
scriptQueue.add(script)
} else {
scriptQueue.add(script)
runScriptQueue()
}
}
fun runScriptQueue() {
for (script in scriptQueue) {
evaluateJavascript(script, { })
}
scriptQueue.clear()
}
如您所见,这是一种超级基本的方法,我并没有真正考虑 evaluateJavascript()
回调。理想情况下,我想找到一种方法 flat map 每个 evaluateJavascript()
调用,这样我们一个接一个地执行,但等待回调完成。
使用 RxJava 我想我会创建一个 Observable,然后让 evaluateJavascript()
回调触发订阅者的 onNext()
。因为,我正在使用 Kotlin Coroutines 我想用 Coroutines 做一些事情,所以我可以对这些 evaulateJavascript()
调用进行排队。但我不是 100% 确定这里的等价物是什么。
用协程解决这个问题会很好。
将基于回调的 API 转换为挂起函数的常用方法如下:
suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
然后您可以将它与 Channel
(用作队列)和处理此通道的协程结合使用:
class MyWebView(context: Context) : WebView(context) {
private val jsQueue = Channel<String>(BUFFERED)
fun startJsProcessingLoopIn(scope: CoroutineScope) {
scope.launch {
for (script in jsQueue) {
evaluateJs(script)
}
}
}
// you could also make this function non-suspend if necessary by calling
// sendBlocking (or trySend depending on coroutines version)
suspend fun queueEvaluateJavascript(script: String) {
jsQueue.send(script)
}
private suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
}
或者你可以创建你自己的协程作用域,并确保将它与你的 webview 的某种生命周期联系起来(我不熟悉 WebView
所以我会让你判断哪种方法正确):
class MyWebView2(context: Context) : WebView(context) {
// you can even further customize the exact thread pool used here
// by providing a particular dispatcher
private val jsProcessingScope = CoroutineScope(CoroutineName("js-processing"))
private val jsQueue = Channel<String>(BUFFERED)
// this starts the loop right away but you can also put this in a method
// to start it at a more appropriate moment
init {
jsProcessingScope.launch {
for (script in jsQueue) {
evaluateJs(script)
}
}
}
// you could also make this function non-suspend if necessary by calling
// sendBlocking (or trySend depending on coroutines version)
suspend fun queueEvaluateJavascript(script: String) {
jsQueue.send(script)
}
private suspend fun evaluateJs(script: String) = suspendCoroutine<String> { cont ->
evaluateJavascript(script) { result ->
cont.resume(result)
}
}
fun someCloseOrDisposeCallback() {
jsProcessingScope.cancel()
}
}