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 t
Many views post high-level events such as click handlers to the event queue to run deferred. So the problem is that "onSaveInstanceState" has already been called for the Activity but the event queue contains deferred "click event". Hence when this event is dispatched to your handler
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
and your code does show
the IllegalStateException is thrown.
The simplest solution is to clean event queue, in onSaveInstanceState
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// ..... do some work
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
findViewById(android.R.id.content).cancelPendingInputEvents();
}
}
I have found an elegant solution for this problem by using reflection. Problem of all above solutions is that fields mDismissed and mShownByMe do not change their state.
Just override method "show" in your own custom bottom sheet dialog fragment like sample below (Kotlin)
override fun show(manager: FragmentManager, tag: String?) {
val mDismissedField = DialogFragment::class.java.getDeclaredField("mDismissed")
mDismissedField.isAccessible = true
mDismissedField.setBoolean(this, false)
val mShownByMeField = DialogFragment::class.java.getDeclaredField("mShownByMe")
mShownByMeField.isAccessible = true
mShownByMeField.setBoolean(this, true)
manager.beginTransaction()
.add(this, tag)
.commitAllowingStateLoss()
}
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
public class CustomDialogFragment extends DialogFragment {
@Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
} catch (IllegalStateException e) {
Log.d("ABSDIALOGFRAG", "Exception", e);
}
}
}
Note that applying this method will not alter the internal fields of the DialogFragment.class:
boolean mDismissed;
boolean mShownByMe;
This may lead to unexpected results in some cases. Better use commitAllowingStateLoss() instead of commit()
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 call commitAllowingStateLoss()
on Transaction
object. Here is example in Kotlin:
override fun show(manager: FragmentManager?, tag: String?) {
try {
val ft = manager?.beginTransaction()
ft?.add(this, tag)
ft?.commitAllowingStateLoss()
} catch (ignored: IllegalStateException) {
}
}
Using the new lifecycle scopes of Activity-KTX its as simple as the following code sample:
lifecycleScope.launchWhenResumed {
showErrorDialog(...)
}
This method can directly be called after onStop() and will successfully show the dialog once onResume() has been called upon returning.