Android java.lang.IllegalStateException: Fragment already added
Asked Answered
S

6

7

I'm getting a loading object waiting for a request from the webservice. But sometimes this object causes my application to end. I couldn't detect exactly what the error involved.

In order to prevent this error, I called the object "dismiss" and "cancel" while passing the activity, but this did not work. I then assigned a value of "null" which didn't work either. I didn't work again because I thought I was dealing with Tag.

My error code;

2019-03-27 11:51:20.502 29685-29685/com.xxxx.app E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.xxxx.app, PID: 29685
    java.lang.IllegalStateException: Fragment already added: DelayedProgressDialog{d8bd442 #1 Delaleyed}
        at androidx.fragment.app.FragmentManagerImpl.addFragment(FragmentManager.java:1916)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:765)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
        at androidx.fragment.app.FragmentManagerImpl$1.run(FragmentManager.java:733)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:6981)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)

My using progress dialog class;

    public class DelayedProgressDialog extends DialogFragment {
    private static final int DELAY_MILLISECOND = 450;
    private static final int MINIMUM_SHOW_DURATION_MILLISECOND = 300;
    private static final int PROGRESS_CONTENT_SIZE_DP = 80;

    private ProgressBar mProgressBar;
    private boolean startedShowing;
    private long mStartMillisecond;
    private long mStopMillisecond;

    private FragmentManager fragmentManager;
    private String tag;
    private Handler showHandler;

    // default constructor. Needed so rotation doesn't crash
    public DelayedProgressDialog() {
        super();
    }

    @NonNull
    @SuppressLint("InflateParams")
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        LayoutInflater inflater = getActivity().getLayoutInflater();

        builder.setView(inflater.inflate(R.layout.dialog_progress, null));
        return builder.create();
    }

    @Override
    public void onStart() {
        super.onStart();
        mProgressBar = getDialog().findViewById(R.id.progress);

        if (getDialog().getWindow() != null) {
            int px = (int) (PROGRESS_CONTENT_SIZE_DP * getResources().getDisplayMetrics().density);
            getDialog().getWindow().setLayout(px, px);
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        }
    }

    @Override
    public void show(FragmentManager fm, String tag) {
        if (isAdded())
            return;

        this.fragmentManager = fm;
        this.tag = tag;
        mStartMillisecond = System.currentTimeMillis();
        startedShowing = false;
        mStopMillisecond = Long.MAX_VALUE;

        showHandler = new Handler();
        showHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                // only show if not already cancelled
                if (mStopMillisecond > System.currentTimeMillis())
                    showDialogAfterDelay();
            }
        }, DELAY_MILLISECOND);
    }

    private void showDialogAfterDelay() {
        startedShowing = true;

        DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(tag);
        if (dialogFragment != null) {
            fragmentManager.beginTransaction().show(dialogFragment).commitAllowingStateLoss();
        } else {
            FragmentTransaction ft = fragmentManager.beginTransaction();
            ft.add(this, tag);
            ft.commitAllowingStateLoss();
        }
    }

    public void cancel() {
        if(showHandler == null)
            return;

        mStopMillisecond = System.currentTimeMillis();
        showHandler.removeCallbacksAndMessages(null);

        if (startedShowing) {
            if (mProgressBar != null) {
                cancelWhenShowing();
            } else {
                cancelWhenNotShowing();
            }
        } else
            dismiss();
    }

    private void cancelWhenShowing() {
        if (mStopMillisecond < mStartMillisecond + DELAY_MILLISECOND + MINIMUM_SHOW_DURATION_MILLISECOND) {
            final Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    dismiss();
                }
            }, MINIMUM_SHOW_DURATION_MILLISECOND);
        } else {
            dismiss();
        }
    }

    private void cancelWhenNotShowing() {
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                dismiss();
            }
        }, DELAY_MILLISECOND);
    }

    @Override
    public void dismiss() {
        FragmentTransaction ft = fragmentManager.beginTransaction();
        ft.remove(this);
        ft.commitAllowingStateLoss();
    }
}
Sienkiewicz answered 27/3, 2019 at 11:48 Comment(8)
why one would want to use AlertDialog.Builder inside a DialogFragment? this is non-sense.Pepi
@MartinZeitler to be like this github.com/Q115/DelayedProgressDialogSienkiewicz
this is still non-sense, because an AlertDialog generally has little to do with a DialogFragment; it is either the one or the other, but not the one instancing the other. only because someone pushed it to GitHub does not imply it would make sense. file an issue there if you have problems with it.Pepi
@MartinZeitler You're right. Do you have a solution for this topic?Sienkiewicz
that's probably not my job. just use a DialogFragment... without the clutter.Pepi
@MartinZeitler All right. Thanks for be interested.Sienkiewicz
Is there a way to check that the fragment is active?Sienkiewicz
I had the same issue after i integrtated Koin to Inject my viewmodels into that Fragment. And i forgot to init the application class. So my Koin components were not injected to Fragment.Upstairs
S
12

I finally found the solution. I was looking for tags with "findFragmentByTag" in my "showDialogAfterDelay" function. But this never came true. I couldn't determine if it was "Added". The piece of code I use to solve this;

        fragmentManager.executePendingTransactions();
Sienkiewicz answered 27/3, 2019 at 14:44 Comment(2)
also according with the documentation If you are committing a single transaction that does not modify the fragment back stack, strongly consider using commitNow() instead. This can help avoid unwanted side effects when other code in your app has pending committed transactions that expect different timing.Legg
showNow: Display the dialog, immediately adding the fragment to the given FragmentManager. This is a convenience for explicitly creating a transaction, adding the fragment to it with the given tag, and calling FragmentTransaction.commitNow(). This does not add the transaction to the fragment back stack. When the fragment is dismissed, a new transaction will be executed to remove it from the activity.Diella
L
2

Just change private void showDialogAfterDelay(). You have to check if the dialog is already added;

Add : if(dialogFragment != null && dialogFragment.isAdded()) { return; }

That code should run before you are trying to show dialog.

Laodicean answered 27/3, 2019 at 11:57 Comment(0)
H
1

You need to check if the dialogFragment is already added; If dialogFragment not added then show it:

if (fragmentManager.findFragmentByTag("progress_dialog") == null) 
    dialogFragment.show(getSupportFragmentManager(), "progress_dialog");
Headlight answered 12/2, 2020 at 9:19 Comment(0)
P
1

On my end same issue error like this,

FATAL EXCEPTION: main java.lang.IllegalStateException: Fragment already added: MessageDialog{b7d37e6}

val dialogMsg = MessageDialog.getInstance()

dialogMsg.show(supportFragmentManager, "")

TO

dialogMsg.show(supportFragmentManager.beginTransaction().remove(dialogMsg),TAG)

After updating the above changes, this issue was fixed and working fine. Thank You

Pairs answered 12/5, 2023 at 13:35 Comment(0)
M
0

You can do a quick check in your code before open the Fragment:

    if(myFragment.isAdded()){
       return;
    }
// else continue with code
Monroe answered 28/4, 2020 at 5:56 Comment(0)
T
0
animal_fragments = new CB_sell_graments();
getChildFragmentManager().beginTransaction().add(R.id.animal_container, animal_fragments).commit();

when change fragments the first create new fragments then change fragments

Truck answered 29/3, 2023 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.