What is the proper way to launch a coroutine from a click event that is defined in a fragment? From my understanding, GlobalScope.launch
is used if you want to launch a coroutine that is supposed to remain in memory for the entire lifecycle of the app. But since a fragment usually has a shorter lifecycle than the app, GlobalScope.launch
probably isn't the proper way. I assume that if I used GlobalScope.launch
, it might keep the fragment from being garbage collected?
I really only need to launch the coroutine from a click event handler, which means that there is no parent functions that I would be calling from.
You need a job
to handle the coroutine cancelation to prevent leaks:
//inside Fragment
val job = Job()
val uiScope = CoroutineScope(Dispatchers.Main + job)
//late in the button click
button.setOnClickListener{
uiScope.launch(Dispatchers.IO){
//asyncOperation
withContext(Dispatchers.Main){
//ui operation
}
}
}
//later in on destroy:
override fun onDestroy(){
job.cancel()
super.onDestroy()
}
You can also use LifecycleScope extension from Google:
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(longTextContent, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
}
Edit, in case you would re-fire another operation. Just use the same scope:
//let's assume you have a second button
button2.setOnClickListener{
uiScope.launch(Dispatchers.IO){
//perform second operation
}
}
GlobalScope.launch
is used if you want to launch a coroutine that is supposed to remain in memory for the entire lifecycle of the app.
This isn't necessarily so, it could be used for any coroutine that isn't coupled to an activity or phase of the app that the user could navigate away from. For example, you might launch a task that sends a one-way message to your server. It will probably finish quite soon, and you want it to finish whatever the user does later on.
I assume that if I used
GlobalScope.launch
, it might keep the fragment from being garbage collected?
Only if the coroutine retains a reference to the fragment or a part of it, and only if it has the potential to run for a long time.
Specifically, the typical thing you do in an on-click event is perform some action that involves your back end (i.e., networking) and then updates the UI. Clearly this can take a long time (especially in case of bad network) and it retains a reference to the UI element it's going to touch later on. This should be bound to the lifecycle of the fragment.
What is the proper way to launch a coroutine from a click event that is defined in a fragment?
The easiest way is like this, following the official documentation:
class MyFragment : Fragment, CoroutineScope by MainScope {
override fun onDestroy() {
cancel() // extension on CoroutineScope
}
... rest of your fragment code ...
}
This captures the idiom that you previously had to write by hand (as seen in the other answer here).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With