fix crash in ViewThreadFragment (#4401)

Fragments don't necessarily have the same lifecycle as their views, so
their is a rare race condition when collecting a Flow from the
`lifecycleScope` of the Fragment where the view is already disposed but
the collect lambda is still executed. Accessing the view from such a
lambda can therefore led to a crash.
Collecting from the `viewLifecycleOwner.lifecycleScope` fixes the
problem.

```
Exception java.lang.IllegalStateException:
  at androidx.fragment.app.Fragment.requireView (Fragment.java:2062)
  at com.keylesspalace.tusky.util.ViewLifecycleLazy.getValue (ViewBindingExtensions.kt:32)
  at com.keylesspalace.tusky.components.viewthread.ViewThreadFragment.getBinding (ViewThreadFragment.kt:79)
  at com.keylesspalace.tusky.components.viewthread.ViewThreadFragment.access$getBinding (ViewThreadFragment.kt:67)
  at com.keylesspalace.tusky.components.viewthread.ViewThreadFragment$onViewCreated$3$1.emit (ViewThreadFragment.java:250)
  at com.keylesspalace.tusky.components.viewthread.ViewThreadFragment$onViewCreated$3$1.emit (ViewThreadFragment.java:248)
  at kotlinx.coroutines.flow.SharedFlowImpl.collect$suspendImpl (SharedFlow.kt:392)
  at kotlinx.coroutines.flow.SharedFlowImpl$collect$1.invokeSuspend (SharedFlow.kt:13)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:231)
  at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined (DispatchedTask.kt:187)
  at kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:159)
  at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume (CancellableContinuationImpl.kt:470)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl (CancellableContinuationImpl.kt:504)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default (CancellableContinuationImpl.kt:493)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeWith (CancellableContinuationImpl.kt:364)
  at kotlinx.coroutines.flow.SharedFlowImpl.tryEmit (SharedFlow.kt:409)
  at kotlinx.coroutines.flow.SharedFlowImpl.emit$suspendImpl (SharedFlow.kt:414)
  at kotlinx.coroutines.flow.SharedFlowImpl.emit (SharedFlow.kt:1)
  at com.keylesspalace.tusky.components.viewthread.ViewThreadViewModel$loadThread$1.invokeSuspend (ViewThreadViewModel.kt:172)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:104)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loopOnce (Looper.java:210)
  at android.os.Looper.loop (Looper.java:299)
  at android.app.ActivityThread.main (ActivityThread.java:8319)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:556)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1038)
```
This commit is contained in:
Konrad Pozniak 2024-04-29 19:52:08 +02:00 committed by GitHub
parent 05b78a2a00
commit b2547c5eef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -244,7 +244,7 @@ class ViewThreadFragment :
}
}
lifecycleScope.launch {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.errors.collect { throwable ->
Log.w(TAG, "failed to load status context", throwable)
Snackbar.make(binding.root, R.string.error_generic, Snackbar.LENGTH_SHORT)