Addev
Addev

Reputation: 32243

Actions in onActivityResult and "Error Can not perform this action after onSaveInstanceState"

Implementing an app where the user can log in I have the following situation: If the user is logged in perform the action else start the login activity for result and if the result is Activity.RESULT_OK do the action.

My problem is that the action to perfom is to show a DialogFragment, but calling

DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel);
newFragment.show(ft, "dialog")

in the onActivityResult callback throws an exception:

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

So how can I solve this? I'm thinking in raising a flag there and show the dialog in the onResume but I see this solution a little dirty

Edit: Added more code (Im following this example for showing the DialogFragment

When the action is requested by the user:

... 
if (!user.isLogged()){
 startActivityForResult(new Intent(cnt, Login.class), REQUEST_LOGIN_FOR_COMMENT);
}

In the same fragment

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_LOGIN_FOR_COMMENT && resultCode == Activity.RESULT_OK) {
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        DialogFragment newFragment = MyDialogFragment.newInstance();
        newFragment.show(ft, "dialog")
    }
}

And if the user logs in the Login activity calls;

setResult(Activity.RESULT_OK);
finish();

Upvotes: 49

Views: 25719

Answers (9)

Farhazul Mullick
Farhazul Mullick

Reputation: 300

A fragment can be hosted in an activity/fragment. Its state is saved in fragmentManager. If we try to call fm.commit() after state is saved then this java.lang.IllegalStateException will be thrown.

The implementation of dialog.show(fm, tag) is something like this

public void show(@NonNull FragmentManager manager, @Nullable String tag) {
        mDismissed = false;
        mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.setReorderingAllowed(true);
        ft.add(this, tag);
        ft.commit(); // <- this causes crash.
    }

So before calling show() we can check if entries of this fragmentManager is saved or not.

fun DialogFragment.showSafeDialogs(fm: FragmentManager, tag: String?) {
    if (fm.isStateSaved) return
    else show(fm, tag)
}

Upvotes: 0

Gil SH
Gil SH

Reputation: 3868

You should call super.onActivityResult() before showing the dialog

Upvotes: 1

CoolMind
CoolMind

Reputation: 28809

I simply check whether an activity is being destroyed.

if (!getActivity().isFinishing()) {
    DialogFragment fragment = MyFragment.newInstance();
    fragment.show(getActivity().getSupportFragmentManager(), MyFragment.TAG);
}

See also https://stackoverflow.com/a/41813953/2914140. In DialogFragment write:

override fun show(manager: FragmentManager?, tag: String?) {
    try {
        val ft = manager?.beginTransaction()
        ft?.add(this, tag)
        ft?.commitAllowingStateLoss()
    } catch (ignored: IllegalStateException) {
    }
}

Upvotes: 4

jimmy0251
jimmy0251

Reputation: 16463

This exception is thrown whenever a FragmentTrasaction is commited after FragmentManager has saved its state. The easy and clean way is to check if FragmentManager has already saved state before showing a DialogFragment.

if(!getSupportFragmentManager.isStateSaved()) {
    MyDialogFragment dialogFragment = new MyDialogFragment()
    dialogFragment.show(getSupportFragmentManager, TAG);
}

Upvotes: 0

Serg Burlaka
Serg Burlaka

Reputation: 2496

This real works.

CheckinSuccessDialog dialog = new CheckinSuccessDialog();
//dialog.show(getSupportFragmentManager(), null);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(dialog, null);
ft.commitAllowingStateLoss();

But but still bad, because got error “Activity has been destroyed”

ava.lang.IllegalStateException: Activity has been destroyed fragmentTransaction.commitAllowingStateLoss();

So my solution is add check if (!isFinishing()&&!isDestroyed())

CheckinSuccessDialog fragment = CheckinSuccessDialog.newInstance();

     if (fragment instanceof DialogFragment) {
                DialogFragment dialog = (DialogFragment) fragment;
                if (!dialog.isAdded()) {
                    fragmentTransaction.add(dialog, 
                          CheckinSuccessDialog.class.getName());
                    if (!isFinishing()&&!isDestroyed()) {
                        fragmentTransaction.commitAllowingStateLoss();
                    }
                }

on dismiss:

FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        Fragment fragment = getSupportFragmentManager().findFragmentByTag(CheckinSuccessDialog.class.getName());
        if (fragment != null && fragment instanceof DialogFragment) {
            DialogFragment dialog = (DialogFragment) fragment;
            dialog.dismiss();
            if (!isFinishing()&&!isDestroyed()) {
                fragmentTransaction.commitAllowingStateLoss();
            }
        }

Upvotes: 0

georgehardcore
georgehardcore

Reputation: 1055

Override show(Fragment manager, String tag) function with allowing state lose and change mDismissed = false; mShownByMe = true; from origibal function by reflection:

public class DialogParent extends DialogFragment {

    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            Field mDismissed = DialogFragment.class.getDeclaredField("mDismissed");
            Field mShownByMe = DialogFragment.class.getDeclaredField("mShownByMe");
            mDismissed.setAccessible(true);
            mShownByMe.setAccessible(true);
            mDismissed.setBoolean(this, false);
            mShownByMe.setBoolean(this, true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commitAllowingStateLoss();
    }
}

Upvotes: 2

GaRRaPeTa
GaRRaPeTa

Reputation: 5598

If using a DialogFragment, the only thing that worked for me was to dismiss the Fragment with dissmissAllowingStateLoss()

Upvotes: 4

Cheok Yan Cheng
Cheok Yan Cheng

Reputation: 42710

Here is the workaround that works fine for me.

private void alert(final String message) {
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
        public void run() {
            AlertDialogFragment alertDialogFragment = AlertDialogFragment.newInstance(message);
            alertDialogFragment.show(getFragmentManager(), ALERT_DIALOG_FRAGMENT);
        }
    });        
}

Upvotes: 14

Nathan Schwermann
Nathan Schwermann

Reputation: 31503

Best thing I've come up with is to not use .show() but rather do this.

CheckinSuccessDialog dialog = new CheckinSuccessDialog();
//dialog.show(getSupportFragmentManager(), null);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(dialog, null);
ft.commitAllowingStateLoss();

Upvotes: 101

Related Questions