commitAllowingStateLoss on DialogFragment
Asked Answered
B

4

41

I have an IllegalStateException on showing a DialogFragment :

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

i know why its happening but i want to using commitAllowingStateLoss on showing dialog by overriding DialogFragment show function :

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit(); //replace it by commitAllowingStateLoss
}

but i don't access to mDismissed and mShownByMe variables , how can i access those variables to modify them as it's parent did.

Blameworthy answered 24/5, 2015 at 13:57 Comment(1)
IMO, mDismissed and mShownByMe can be ignored when overriding show(FragmentManage, String). I cannot say that it is totally safe to do it. There are some internal handling of mDismissed when mShownByMe is false and mDismissed is by default false. Ignoring them seems not to impact the internal handling at all.Laparotomy
B
56

I think to prevent throwing IllegalStateException on DialogFragment might be better to use :

 YourDialogFragment dialogFragment = new YourDialogFragment();
 fragmentManager.beginTransaction().add(dialogFragment, YourDialogFragment.TAG_FRAGMENT).commitAllowingStateLoss();

instead of using show() on DialogFragment.

Blameworthy answered 25/5, 2015 at 9:26 Comment(2)
But what about the internal flag changes (` mDismissed = false;` and mShownByMe = true;), that won't be updated?Precede
@Precede use vuhung3990 answer for that, it's better for saving state caseTellford
V
13

The solution about commitAllowingStateLoss works if your DialogFragment has no state to save, otherwise they will be lost like the function name told. But I think in most cases we have state to save, that is the major benefit of DialogFragment: Android recreate it and maintains its state automatically.

A better solution would be to check if the recreate process done, if not then return to caller, which is either an Activity or a FragmentActivity, it should call mark it and call the show function again later in its onPostResume() or onResumeFragments() callback, which we can make sure all fragments are recreated.

Here is an overridden show() from a subclass of DialogFragment:

public boolean show(FragmentManager fragmentManager) {
   if (fragmentManager.isStateSaved()) return false;
   show(fragmentManager, tagName);
   return true;
}
Visser answered 10/8, 2017 at 19:21 Comment(1)
This is a better solution than accepted one by Arash GM since this allows DialogFragment to keep internal states.Carolynncarolynne
H
13

Origin dialog fragment

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit(); //replace it by commitAllowingStateLoss
}

I don't know mDismissed, mShownByMe variables used for, so should be better if override show(FragmentManager, String) method of DialogFragment and it works fine with me

override fun show(manager: FragmentManager?, tag: String?) {
    if (manager?.isDestroyed == false && !manager.isStateSaved) {
      super.show(manager, tag)
    }
  }

isStateSaved available from appcompat >= 26.0.0 or androidx

Heintz answered 22/11, 2018 at 7:25 Comment(0)
R
0

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

Rentfree answered 1/9, 2023 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.