Android how organise coroutines in elegant way?

648 views Asked by At

What is the best way to organize coroutines for application in elegant way? I understand, the question seems odd. Let me show the example of Executors

Create an object AppExecutors.kt

object AppExecutors {

    private val main: Executor = MainThreadExecutor()
    private val util: Executor = Executors.newFixedThreadPool(3)

    fun main(f: () -> Unit) {
        main.execute(f)
    }

    fun util(f: () -> Unit) {
        util.execute(f)
    }

    class MainThreadExecutor : Executor {
        private val mainThreadHandler = Handler(Looper.getMainLooper())
        override fun execute(command: Runnable?) {
            mainThreadHandler.post(command)
        }
    }
}

Now, we can use this. Simple, minimal code, etc

val exe = AppExecutors
exe.util {
    val first = calculateFirst()
    val second = calculateSecond()
    val str = ("first = $first | second = $second")
    exe.main {
        Toast.makeText(activity, "Executors $str", Toast.LENGTH_LONG).show()
    }
}

Now, i'v tried to use this approach on coroutines AppCoRoutines.kt

object AppCoRoutines{
    private val uiContext: CoroutineContext = Dispatchers.Main
    private val ioContext: CoroutineContext = Dispatchers.IO
    private val networkContext: CoroutineContext = Executors.newFixedThreadPool(3).asCoroutineDispatcher()
    private val singleContext: CoroutineContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()

    val ui: CoroutineScope = CoroutineScope(uiContext)
    val io: CoroutineScope = CoroutineScope(ioContext)
    val net: CoroutineScope = CoroutineScope(networkContext)
    val single: CoroutineScope = CoroutineScope(singleContext)
}

Now, use this:

val coRout = AppCoRoutines
coRout.ui.launch {
    val str: String = withContext(coRout.net.coroutineContext){
        val first = async { calculateFirst() }
        val second = async { calculateSecond() }
        ("first = $first | second = $second")
    }
    Toast.makeText(activity, "CoRoutine $str", Toast.LENGTH_LONG).show()
}

No so elegant at all. Maybe someone could suggest more simple way? I'm not so good in this right now, so i'm using coroutines for just simple taks.

Thank you in advance!

1

There are 1 answers

0
Adib Faramarzi On

You need to extend CoroutineScope on the call-site (where ever you want to use your coroutines, e.g. coRout.ui.launch in the current implementation) of your coroutines. That way, you don't have to write and repeat coRout.ui.launch everywhere and also, you can avoid memory leaks in the current code (because your coroutines can be alive whilst your calling object wants to be destroyed)

For example, If you are inside a class, you can make your class extend CoroutineScope like below:

class SomeClass: CoroutineScope {
    val job = Job()
    override val coroutineContext: CoroutineContext
        get() = job + coRout.net.coroutineContext
}

Inside this class, you can use launch and withContext to easily handle your coroutines. You can also cancel() the job object when you know your class is being destroyed (like onDestory for android activities), so that all the coroutines for this class get cancelled.