Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to launch coroutines from Globalscope on Android in certain situations (singletons)?

When launching coroutines from Activities, Fragments or Android Architecture Components ViewModels, it makes total sense to use a coroutine scope that is bound to the lifecycle of that view component in order to avoid leaks and free resources by e.g. canceling the network request when the user leaves the screen.

But there are other situations, where you don't want to cancel a coroutine even when the user leaves the screen like when you are performing a network request for analytics or writing into a database. Is it OK to launch coroutines with GlobalScope in such situations? The objects where these coroutines are launched are mostly Singletons, so they live for the lifetime of the application anyway, so there is no danger of leaks, right?

The Kotlin docs are pretty clear on GlobalScope:

Application code usually should use an application-defined CoroutineScope. Using async or launch on the instance of GlobalScope is highly discouraged.

Is it OK to use GlobalScope in these situations? If not, how should my application-defined CoroutineScope look like?

like image 220
Lukas Lechner Avatar asked Jun 17 '19 13:06

Lukas Lechner


People also ask

What is GlobalScope launch in Kotlin?

Quoting definition of Global Scope from Kotlin's documentation– “Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely.” GlobalScope creates global coroutines and these coroutines are not children of some specific scope.

Why launch coroutine builder is known as fire and forget?

launch builder will start a new coroutine that is “fire and forget” — that means it won't return the result to the caller. async builder will start a new coroutine, and it allows you to return a result with a suspend function called await .

What are Kotlin coroutines good for?

Coroutines were added to Kotlin in version 1.3 and are based on established concepts from other languages. On Android, coroutines help to manage long-running tasks that might otherwise block the main thread and cause your app to become unresponsive.


1 Answers

If you have an asynchronous worker whose lifecycles is truly global (they only die/end when your process dies), using GlobalScope or a similar life-long scope, is fine.

Say, you have an Activity that makes a request, but the actual network-request needs to continue even if the Activity goes away, because you'd like to cache it when the network finally returns a response.

You'll add a CoroutineScope to your Activity/Fragment, or better to your ViewModel and have your code that finally puts stuff on the screen run in that scope. When the Activity/Fragment/ViewModel dies, the scope is canceled and nothing will be attempted to show something on a screen that no longer exists.

However, your Fragment/Activity/ViewModel may talk to a data-source/repository that has a lifecycle that only ends when the process dies. You can switch to a GlobalScope in there so that your network-responses get cached, even when no Activity/Fragment/ViewModel is alive to show the result on the screen.

class MyViewModel(context: CoroutineContext, repo: MyRepository) : ViewModel() {
    private val scope = CoroutineScope(context + SuperviserJob())

    override fun onCleared() { scope.cancel() }

    fun getDataFromNetwork() {
        scope.launch {
            myLiveData.value = repo.getDataFromNetwork()
        }
    }

}

// Singleton class
class MyRepositoryImpl(context: CoroutineContext) : MyRepository {
    private val scope = CoroutineScope(context + SupervisorJob())

    override suspend fun getDataFromNetwork() : String {
        return scope.async { // switch scopes
            val data = ... fetch data ...
            saveInCache(data)
        }.await()
    }
}

When your ViewModel ends (onCleared is called), the MyRepositoryImpl's getDataFromNetwork still keeps running and will call saveInCache if all goes right. However, the value returned won't be assigned to myLiveData.value because the coroutine of your ViewModel's scope was cancelled.

like image 108
Streets Of Boston Avatar answered Nov 15 '22 23:11

Streets Of Boston