Coroutines are very lightweight, but creating them is still a cost. You should remember that on Android we’re running on a resource constrained system, so we need to do everything possible to reduce our footprint.
How about quantifying the cost? A coroutine consists of the continuation object and the coroutine context, which is just several more objects. I measured it at 140 bytes. In terms of CPU time, the cost of creation is 100 nanoseconds, which would become non-negligible only if you had a tight loop that did nothing but create coroutines. Even the analogy of “lightweight threads” doesn’t convey the difference. A Java thread acquires system resources and performs JNI system calls. It pre-allocates the call stack. It may take up to several milliseconds to launch a native thread. Nothing of the sort happens with coroutines, which are a pure JVM construct and have a stackless design.
“Creating a coroutine” isn’t even a well-defined concept. In a way, you create a new coroutine with each suspend fun
call because that creates another continuation object and adds it to the call chain. withContext
could be said to create a new coroutine because it adds an object to the current coroutine context. The cost of these operations is commensurate with the cost of a launch
.
The true benefit of withContext
is the programming model: it gives you a super-simple way to “weave” your coroutine execution across threads while the code retains the appearance of pure sequential execution.
The main problem of withContext
that I perceive is lack of exposure to the users, which you witnessed as well. Of every 10 coroutine-related questions I answer on Stack Overflow, the answer to 9 is “here’s how to use withContext
properly”.