I am setting up my Fragment like suggested in Google docs as such:
private var _binding: MyBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = MyBinding.inflate(inflater, container, false)
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Now I'm calling a coroutine that to my understanding should be scoped to the lifecycle of this fragment. It has a longer network call, then a success:
lifecycleScope.launch(Dispatchers.Main) {
when (myViewModel.loadFromNetwork(url)) {
true -> responseSuccess()
false -> responseFailure()
}
}
private suspend fun responseSuccess() {
binding.stateSuccess.visibility = View.VISIBLE
// ...
}
Now when I press the Android system-back button while loadFromNetwork
is still loading the fragment gets destroyed and onDestroyView()
is called. As such binding
now is null
. I'm getting a kotlin.KotlinNullPointerException
. What I not quite getting is why responseSuccess()
is still being executed even though I thought that lifecycleScope
is specifically meant for these kind of situations. According to Google Docs:
A LifecycleScope is defined for each Lifecycle object. Any coroutine launched in this scope is canceled when the Lifecycle is destroyed.
I understand this code can be fixed by a few changes and some manual null-checks, but I would like to understand how to fix this without boilerplate and in the intended manner. What is the purpose of using lifecycleScope to be lifecycle aware if not exactly this?
Coroutines cancellation is cooperative. It means it's a responsibility of a coroutine itself to check for cancellation. Most (or may be all) suspend operations in the coroutines library check for cancellation, but if you don't call any of them, you need to make your code cancellable as described here.
A better option to work with views in coroutines is to use lifecycle extensions which suspend / cancel the coroutine automatically when the lifecycle state is not in a required state.
Also please note, that cancellation is just a regular
CancellationException
, so check you don't accidentally catch it.