0

I have a Custom DialogFragment, got java.lang.IllegalStateException: Fragment already added. First I got the same crash when I click on the button quickly to show the DialogFragment. Then I override show() , remove the fragment before show.it seems good on my phone. But still seen on crashlytics, and I couldn't reproduce it on my phone by the previous way.

    @Override
    public void show(@NonNull FragmentManager manager, String tag) {
        try {
            manager.beginTransaction().remove(this).commitNowAllowingStateLoss();
            super.show(manager, tag);
        } catch (Exception ignored) {

        }
    }

    @Override
    public void dismiss() {
        if (getFragmentManager() != null) {
            super.dismiss();
        }
    }

Here's the code of my DialogFragment. I have a Builder for outside button to show it

public class BMBottomSheetDialogFragment extends BottomSheetDialogFragment {
    private static BMBottomSheetDialogFragment fragment;
    public static BMBottomSheetDialogFragment newInstance(Builder builder) {
        if (fragment == null) {
            fragment = new BMBottomSheetDialogFragment();
        }
        final Bundle args = new Bundle();
        ...

        fragment.setArguments(args);
        return fragment;
    }

    public static class Builder {
        public BMBottomSheetDialogFragment build() {
            return newInstance(this);
        }
        public void show(FragmentManager fragmentManager, String tag) {
            BMBottomSheetDialogFragment dialog = build();
            dialog.show(fragmentManager, tag);
        }
    }
}

So, why didn't the override show()make sense and How to fix this crash?

mywjyw
  • 1
  • Do not keep static reference to `BMBottomSheetDialogFragment` this causes leak . Just open `BMBottomSheetDialogFragment` in conventional way . if you want to prevent opening multiple Dialogs . You can put a thrashhold on Click event . [Like this](https://stackoverflow.com/questions/5608720/android-preventing-double-click-on-a-button). Also you do not need a `Builder` for this . Keep it simple – ADM Jan 25 '22 at 07:44

1 Answers1

0

instead of using commitNowAllowingStateLoss use commit in show method.

OneDev
  • 557
  • 3
  • 14
  • May you explain the reason? – mywjyw Jan 25 '22 at 08:16
  • This happens when we try to add the same fragment or DialogFragment twice before dismissing I think, read [this](https://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) link may help you, – OneDev Jan 25 '22 at 09:33
  • I know the crash reason, but I'm confused why even use `manager.beginTransaction().remove(this).commitNowAllowingStateLoss();` which should remove the fragment before add, would cause this crash. – mywjyw Jan 25 '22 at 09:49
  • maybe there is no fragment to remove – OneDev Jan 25 '22 at 10:01
  • Will no fragment to remove cause this Exception? – mywjyw Jan 25 '22 at 10:10
  • I think commitNowAllowingStateLoss call executePendingTransactions() I'm not sure, I must see the source code – OneDev Jan 25 '22 at 10:29
  • Commit -> Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready. A transaction can only be committed with this method prior to its containing activity saving its state. If the commit is attempted after that point, an exception will be thrown. This is because the state after the commit can be lost if the activity needs to be restored from its state. See commitAllowingStateLoss() for situations where it may be okay to lose the commit. – OneDev Jan 25 '22 at 10:32
  • commitAllowingStateLoss -> Like commit but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user. – OneDev Jan 25 '22 at 10:32
  • commitNow -> Commits this transaction synchronously. Any added fragments will be initialized and brought completely to the lifecycle state of their host and any removed fragments will be torn down accordingly before this call returns. Calling commitNow is preferable to calling commit() followed by FragmentManager.executePendingTransactions() as the latter will have the side effect of attempting to commit all currently pending transactions whether that is the desired behavior or not. – OneDev Jan 25 '22 at 10:33
  • commitNowAllowingStateLoss -> Like commitNow but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user. – OneDev Jan 25 '22 at 10:34
  • 1
    I think of a situation that causes the crash: `remove.commitNowAllowingStateLoss` execute immediately before the previous `add.commit`, so it did't remove the previous fragment actually. – mywjyw Jan 25 '22 at 10:35
  • yes, I commented for you each method how works – OneDev Jan 25 '22 at 10:54