What I am trying to do is I am loading a webpage. If the pages takes too long to load I want to write a log message, if it loads in a predefined amount of time I want to cancel a job so that the log message does not get written
Here is what I have
override fun onStart() {
super.onStart()
loadTimer = launch {
delay(TimeUnit.SECONDS.toMillis(5))
ensureActive()
logUtil.w(TAG, "Loading took longer than expected")
}
webView.loadUrl(url)
}
Cancel when page is loaded
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
logUtil.i(TAG, "Finished loading $url")
launch{
loadTimer?.cancelAndJoin()
}
}
The problem I am having is that even after canceling, the log message still gets written. How can I cancel this properly so that it does not get written?
CodePudding user response:
I'm not sure which CoroutineScope you're launching those coroutines from. Since they are called on implicit this
, I suppose you created your own, or you made your Activity implement CoroutineScope directly? A lot of old tutorials showed that pattern, but it is obsolete now that Android Jetpack provides lifecycleScope
, which is already set up to use Dispatchers.Main
(.immediate
) and cancels its jobs automatically in onDestroy()
.
Anyway, I think maybe the problem is that you're cancelling your Job inside another launched coroutine. If your CoroutineScope is not using Dispatchers.Main.immediate
, it will take a few ms until it makes the call to cancel. And since your initial Job was not on the main thread, there's a second race condition there. You don't need to use cancelAndJoin()
, so you can eliminate the first race by calling join()
synchronously.
Here's how to fix both of those race conditions:
override fun onStart() {
super.onStart()
loadTimer = lifecycleScope.launch { // or with your current scope, use launch(Dispatchers.Main)
delay(TimeUnit.SECONDS.toMillis(5))
logUtil.w(TAG, "Loading took longer than expected")
}
webView.loadUrl(url)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
logUtil.i(TAG, "Finished loading $url")
loadTimer?.cancel()
}
This theory would only give the two race conditions a window of a few ms where the page could load but the five second timer just runs out at almost the same time, so I'm not totally confident it is the issue.
CodePudding user response:
You don't need to launch anything. Just use withTimeout
to handle the cancellation and timeout correctly directly.