Marcin Moskala Android retweetledi

I waited years for this warning! I’ve seen so many issues that are a consequence of using a job as an argument for a coroutine starter, I explained this best practice in my book and taught it in my workshops, and yet I could still see it in so many projects. With this warning I hope this will finally change. Let’s explain it for the last time why Job shouldn’t be used as an argument for a coroutine.
The key misunderstanding here is that Job is the only context that cannot be overridden by an argument. If you use any other context, it will be used in the coroutine and its children, but not Job. Why? Because every coroutine creates its own job. A job contains the state and relations of a coroutine, it cannot be shared or enforced from outside. The Job that is used as an argument isn’t going to be a job of this coroutine; instead, it overrides Job from the scope and becomes a parent. This breaks structured concurrency.
Let’s see an example. Using withContext(SupervisorJob()) { … } works completely differently than supervisorScope { … }. supervisorScope creates a coroutine that is a child of the caller of this function, and uses a supervisor job (so it doesn’t propagate its children’s exceptions). On the other hand, withContext(SupervisorJob()) creates a regular coroutine, which is a child of SupervisorJob, so it has no relation to the caller.
A Job used as an argument breaks the relationship with the caller. In the case belo,w updateToken won’t be related to the caller of getToken. This is generally discouraged, as it breaks structured concurrency. The standard approach would be to just sequentially call updateToken. If we really want to detach updateToken from getToken, it is a better practice to start the launch on a different scope, like the backgroundScope we define in our application for background tasks. This way new coroutine is still attached to a scope, just a different one.

English



















