Careeroza
Custom Scopes and GlobalScope
Why Not GlobalScope
GlobalScope.launch creates a coroutine with no parent. Nobody cancels it, nobody waits for it, and its errors are not reported to a caller. If the screen that started it is closed, it keeps running and may leak memory. The compiler marks it as a delicate API.
// Bad: leaks if the screen closes
GlobalScope.launch { repository.sync() }
// Good: tied to a lifecycle
viewModelScope.launch { repository.sync() }Your Own Scope
Example
Classes that live longer than one function (a repository, a download manager) create their own scope and cancel it in close().
import kotlinx.coroutines.*
class DownloadManager : AutoCloseable {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("downloads"))
private val jobs = mutableListOf<Job>()
fun download(file: String, ms: Long) {
jobs += scope.launch {
try {
delay(ms)
println("Downloaded $file")
} finally {
if (!isActive) println("Stopped $file")
}
}
}
override fun close() = scope.cancel()
suspend fun awaitJobs() = jobs.joinAll()
}
fun main() = runBlocking {
val manager = DownloadManager()
manager.download("song.mp3", 100)
delay(300)
manager.download("movie.mp4", 5000)
delay(100)
manager.close() // cancels the movie download
manager.awaitJobs()
println("Manager closed")
}Output
Downloaded song.mp3 Stopped movie.mp4 Manager closed
Note: Use SupervisorJob() in long-lived scopes so one failed task does not kill the whole scope.