A little bit late to the party but hopefully it will help others in the future.
The reason this issue occurs is the DialogFragment.show() is called while the Activity is in at most in stopped state, that usually happens due to us developers trying to show the dialog as a response to an API call failure or something similar, and the user puts the app in the background, which then causes the DialogFragment.show() to get called when the activity has stopped.
To work around that we can do something really simple, we create a LiveData that holds the instance of the dialog, then, we let the activity/fragment observe the LiveData and if there's a value we just show the dialog.
Now this is going to work since LiveData only sends events to observers when the lifecycle is at least started, so the dialog is guaranteed to be shown when the Activity/Fragment is at least in started state.
From documentation of LiveData.observe -
Adds the given observer to the observers list within the lifespan of the given owner. The events are dispatched on the main thread. If LiveData already has data set, it will be delivered to the observer.
The observer will only receive events if the owner is in Lifecycle.State.STARTED or Lifecycle.State.RESUMED state (active).
This is how the code looks like -
In your Fragment/Activity create a LiveData -
private val dialogLiveData = MutableLiveData<DialogFragment?>()
Next we'll observe the LiveData -
Fragment -
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialogLiveData.observe(viewLifecycleOwner) {
if (it?.isAdded?.not() == true) {
it.show(childFragmentManager, "dialog")
}
}
}
Activity -
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dialogLiveData.observe(this) {
if (it?.isAdded?.not() == true) {
it.show(supportFragmentManager, "dialog")
}
}
}
now when we want to show the dialog we're simply going to do -
fun showDialog() {
dialogLiveData.value = DialogFragment()
}
then when we want to dismiss the dialog this is what we're going to do -
fun dismissDialog() {
val dialog = dialogLiveData.value
dialog?.let {
if (dialog.isAdded) {
if (dialog.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED).not()) {
dialogLiveData.value?.dismissAllowingStateLoss()
} else {
dialogLiveData.value?.dismiss()
}
}
dialogLiveData.value = null
}
}
Note: The LiveData is better to hold inside the VM, otherwise the DialogFragment's instance could get lost upon screen rotations
mDismissed
andmShownByMe
can be ignored when overridingshow(FragmentManage, String)
. I cannot say that it is totally safe to do it. There are some internal handling ofmDismissed
whenmShownByMe
isfalse
andmDismissed
is by defaultfalse
. Ignoring them seems not to impact the internal handling at all. – Laparotomy