Reputation: 32243
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
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
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
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
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
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
Reputation: 5598
If using a DialogFragment, the only thing that worked for me was to dismiss the Fragment with dissmissAllowingStateLoss()
Upvotes: 4
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
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