I have a hybrid app; some of my Activities use a WebView to display web content. The web app that I show in the WebView has a JS interface that lets me send commands to the web app to navigate different places or do other things.
For example, if I need my web app to navigate to the "user profile" page, I execute a command like:
class SomeActivity: AppCompatActivity {
...
webView.evaluateJavascript("navigateTo(\"userprofile\")")
...
}
Then, I get a response via the JS interface, and the app reacts accordingly.
I introduced a JS queue to improve performance, so the JS commands are executed sequentially. Instead of calling the evaluateJavascript() function directly on the WebView, I've created a custom WebView component with this JS queue set as a property.
class SomeActivity: AppCompatActivity {
...
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
...
}
Now I would like to add a new behaviour on top of that, which is being able to pre-process the commands within the queue. What I mean by pre-processing is that if I ever queue commands of the same "type", like:
class SomeActivity: AppCompatActivity {
...
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"about-me\")")
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"user-list\")")
...
}
What I would like to happen is that the queue is smart enough to ditch those two first "navigate" commands - "navigateTo(\"userprofile\")" and "navigateTo(\"about-me\")" - because I don't want my WebView to navigate to those two places just to finally navigate to "navigateTo(\"user-list\")".
The implementation of this JS queue looks like this:
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)
}
}
}
- How can I pre-process the js commands in the
Channel<String>so I ditch duplicated js commands? - Also, sometimes my WebView will become invisible, and I want to pause the queue when that happens. I wonder if there's any way to programmatically pause a Channel?
Edit #1
Also, sometimes my WebView will become invisible, and I want to pause the queue when that happens. I wonder if there's any way to programmatically pause a Channel?
I've tried using this PausableDispatcher implementation, and it seems to be doing the trick.
All of the command examples you gave follow a specific pattern: they're all functions. We can use this to our advantage!
First, let's create some terminology.
navigateTo()is a function (of course!). And lets call thenavigateTopart of the function atype.An example of some
types are:I just made this terminology up. But it will help you understand the logic.
Now, what we need to do is look at the array of commands, understand it's
type, and check if any other commands have the same type. If they do, they need to be excluded from the queue before the queue is executed.Easy!
I've written a sample code that you can integrate with your script:
Let me know if I missed the mark answering this question. Otherwise, cheers to a great queue system!