I am really happy that I switched my long running tasks, which constantly produce results to UI thread to coroutines. It improved performance and decreased memory usage by 3 times and all memory leaks disappeared compared to AsyncTask or regular Threads in Android.
The only problem remains is that, I don't know how should I restart my long running operation after exception has occurred at some time...
I feel I did not understand exception handling in coroutines at all after reading tons of article. Let me know how can I achieve desired behaviour.
lateinit var initEngineJob: Job
override val coroutineContext: CoroutineContext
get() = initEngineJob + Dispatchers.Main
fun initWorkEngineCoroutine()
{
launch {
while(true) {
val deferred = async(Dispatchers.Default) {
getResultsFromEngine()
}
val result = deferred.await()
if (result != null) {
//UI thread
draw!!.showResult(result)
}
}
}
}
fun getResultsFromEngine() :Result? {
result = // some results from native c++ engine, which throws exception at some times
return result
}
i don't know where should I put try catch. I tried to surround deferred.await() with try catch, but I could not call same method in catch block to retry long running task. I tried SupervisorJob(), but no success either. I still could not call initWorkEngineCoroutine() again and start new coroutine...
Help to solve this issue finally :)
You should treat your code as linear imperative and try/catch where it makes the most logical sense in your code. With this mindset, your question is probably less about coroutines and more about try/catch retry. You might do something like so:
fun main() {
GlobalScope.launch {
initWorkEngineCoroutine()
}
}
suspend fun initWorkEngineCoroutine() {
var failures = 0
val maxFailures = 3
while(failures <= maxFailures) {
try {
getResultsFromEngine()?.let {
draw!!.showResult(it)
}
} catch (e: Exception) {
failures++
}
}
}
// withContext is like async{}.await() except an exception occuring inside
// withContext can be caught from inside the coroutine.
// here, we are mapping getResultFromEngine() to a call to withContext and
// passing withContext the lambda which does the work
suspend fun getResultsFromEngine() :Result? = withContext(Dispatchers.Default) {
Result()
}
I've included some logic to prevent infinite loop. It's probably not going to fit your requirements, but you might consider some sort of thing to prevent an issue where exceptions are raised immediately by getResultsFromEngine()
and end up causing an infinite loop that could result in unexpected behavior and potential stackoverflow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With