Antonio Santoro
Antonio Santoro

Reputation: 907

What's the difference between launching a coroutine inside a fragment using `MainScope()` and `requireActivity().lifecycleScope`?

Does that make sense to launch operations, like db writes, that needs to continue after the fragment lifecycle?

Code sample:

requireActivity().lifecycleScope.launch {
         // suspend function invocation
}

MainScope().launch { 
        // suspend function invocation
}

Upvotes: 5

Views: 1235

Answers (1)

Dmitri
Dmitri

Reputation: 2972

The most important difference between MainScope and lifecycleScope is in the cancellation management of the launched coroutine.

I know your question is about requireActivity().lifecycleScope, but let me do it step by step.

With lifecycleScope, cancellation is done for you automatically - in the onDestroy() event in either Fragment or Activity, depending on whose lifecycle your hooked into.

With the mainScope, you’re on your own, and would have to add the scope.cancel() yourself, like below:

class MyActivity: Activity {
    private val scope = MainScope()
    // launch a coroutine on this scope in onViewCreated
    override fun onDestroy() {
        super.onDestroy()
        //you have to add this manually
        scope.cancel()
    }
}

More info at: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html

In other words, what you do (or supposed to do) manually with main scope, lifecycleScope does it for you automatically.

Now, at this point, you could (maybe) say, aha - it is the main Scope that I want, because my operation is not automatically cancelled, it’s exactly what I want, so i can just skip adding the cancellation call.

In a way, yes, you will get what you wish, as indeed it will keep running for some indeterminate perid of time until the Fragment and its variables are garbage collected, but you will no longer have access to that scope - the fragment is gone, and when you navigate into the fragment again, a new instance is going to be created, along with a new main scope. And of course you have no guarantee on how long it is going to be before the garbage collection kicks in. And what about exceptions?

Now on to requireActivity().lifecycleScope.

If you use requireActivity().lifeCycleScope, you obviously jump on the Activity lifecycle and get two advantages - a longer life cycle (presumably, there are other fragments in the activity, and say navigation back from the fragment in question just navigates to another fragment within the same activity, and not exits the app), and automatic cancellation in OnDestroy().

That may be enough. But a) your job handle, if you need one, will still stay in the fragment, and yet again, once you lose the fragment, you no longer have access to the job (assuming you need it), and b) your activity will not survive a configuration change (unless you expressly prohibit them). If you want to allow and handle configuration changes, go with the viewModelScope on the activity (and use a shared view model between activity and fragment).

The ViewModel class allows data to survive configuration changes such as screen rotations.

And, finally, if none of that is enough (your “Db save” operation is taking a really long time), use a Service (or WorkManager, etc). But at that point, the proper question to ask will be “why is it taking so long?” and focus on that.

Upvotes: 3

Related Questions