Some users are reporting, if they use the quick action in the notification bar, they are getting a force close.
I show a quick action in the notification who calls the "TestDialog" class. In the TestDialog class after pressing the button "snooze", I will show the SnoozeDialog.
private View.OnClickListener btnSnoozeOnClick() {
return new View.OnClickListener() {
public void onClick(View v) {
showSnoozeDialog();
}
};
}
private void showSnoozeDialog() {
FragmentManager fm = getSupportFragmentManager();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(fm, "snooze_dialog");
}
The error is *IllegalStateException: Can not perform this action after onSaveInstanceState*.
The code line where the IllegarStateException gets fired is:
snoozeDialog.show(fm, "snooze_dialog");
The class is extending "FragmentActivity" and the "SnoozeDialog" class is extending "DialogFragment".
Here is the complete stack trace of the error:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
at com.test.testing.TestDialog.f(TestDialog.java:538)
at com.test.testing.TestDialog.e(TestDialog.java:524)
at com.test.testing.TestDialog.d(TestDialog.java:519)
at com.test.testing.g.onClick(TestDialog.java:648)
at android.view.View.performClick(View.java:3620)
at android.view.View$PerformClick.run(View.java:14292)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)
I can't reproduce this error, but I am getting a lot of error reports.
Can anybody help that how can I fix this error?
After few days I want share my solution how I've fixed it, to show DialogFragment you should to override
show()
method of it and callcommitAllowingStateLoss()
onTransaction
object. Here is example in Kotlin:I have run in to this problem for years.
The Internets are littered with scores (hundreds? thousands?) of discussions about this, and confusion and disinformation in them seems aplenty.
To make the situation worse, and in the spirit of the xkcd "14 standards" comic, I am throwing in my answer in to the ring.
The
cancelPendingInputEvents()
,commitAllowingStateLoss()
,catch (IllegalStateException e)
, and similar solutions all seem atrocious.Hopefully the following easily shows how to reproduce and fix the problem:
Though it's not officially mentioned anywhere but I faced this problem couple of times. In my experience there is something wrong in compatibility library supporting fragments on older platforms which causes this problem. You use test this by using normal fragment manager API. If nothing works then you can use the normal dialog instead of dialog fragment.
This is common issue. We solved this issue by overriding show() and handling exception in DialogFragment extended class
Note that applying this method will not alter the internal fields of the DialogFragment.class:
This may lead to unexpected results in some cases. Better use commitAllowingStateLoss() instead of commit()
ref: link
The following implementation can be used to solve the problem of performing safely state changes during the
Activity
lifecycle, in particular for showing dialogs: if the instance state has already been saved (e.g. due to a configuration change), it postpones them until the resumed state has been performed.Then using a class like this:
You can safely show dialogs without worrying about the app state:
and then call
TestDialog.show(this)
from within yourXAppCompatActivity
.If you want to create a more generic dialog class with parameters, you can save them in a
Bundle
with the arguments in theshow()
method and retrieve them withgetArguments()
inonCreateDialog()
.The whole approach could seem a bit complex, but once you have created the two base classes for activities and dialogs, it is quite easy to use and is perfectly working. It can be used for other
Fragment
based operations which could be affected by the same problem.