stefan
stefan

Reputation: 1376

DialogFragment causes IllegalStateException

I'm using this code to load my view into a DialogFragment:

    @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    SyncPopup syncPopup = new SyncPopup(getActivity(), this,_callback);

    getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
    getDialog().getWindow().setSoftInputMode(STYLE_NO_INPUT);
    getDialog().setCanceledOnTouchOutside(false);


    return syncPopup;
}

this code is in my custom DialogFragment. So this points to DialogFragment. SyncPopup holds all my view logic. I'm using this pattern so that I can reuse SyncPopup elsewhere.

Inside my SyncPopup I'm running a AsyncTask and in onPostExecute I want to close the DialogFragment. -> _parentFragment was injected via constructor see code above (used this pointer).

SyncInfo si = new SyncInfo(email, Start, End);
        new AsyncTask<SyncInfo, Void, String>() {
            @Override
            protected void onPostExecute(String result) {
                super.onPostExecute(result);

                _callback.onSyncFinish();
                _parentFragment.dismiss();

            }

            @Override
            protected String doInBackground(SyncInfo... params) {
                SyncInfo si = params[0];
                return Cache.sync(si.Email, si.Start, si.End);
            }
        }.execute(si);

I've tested this code but in Google Analytics I see that _parentFragment.dismiss(); causes -> IllegalStateException (@SyncPopup$8:onPostExecute:221) {main}

I guess it has nothing to do with the AsyncTask since I also get this problem with other DialogFragments where I use the same pattern.

Am I doing anything wrong? I already read that you shouldn't close DialogFragment with -> getDialog().dismiss() but this isn'T the case. I close it by calling dismiss on the reference I passed into my custom view.

UPDATE:

Can't say why but now the exception ocurred on my android 4.03 device while I tried to debug something other. So I can provide the stack trace:

Shutting down VM
 threadid=1: thread exiting with uncaught exception (group=0x40c131f8)
 FATAL EXCEPTION: main
 java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1343)
    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1354)
    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.dismissInternal(DialogFragment.java:189)
    at android.support.v4.app.DialogFragment.dismiss(DialogFragment.java:155)
    at com.mxp.time.popups.SyncPopup$8.onPostExecute(SyncPopup.java:239)
    at com.mxp.time.popups.SyncPopup$8.onPostExecute(SyncPopup.java:1)
    at android.os.AsyncTask.finish(AsyncTask.java:602)
    at android.os.AsyncTask.access$600(AsyncTask.java:156)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4493)
    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:788)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:555)
    at dalvik.system.NativeStart.main(Native Method)
 Sending signal. PID: 24095 SIG: 9
 GC_CONCURRENT freed 1081K, 21% free 13393K/16747K, paused 4ms+3ms
 Starting setup.

right now I'm trying this:

@Override
    public void onSaveInstanceState(Bundle outState) {
        //first saving my state, so the bundle wont be empty.
        //http://code.google.com/p/android/issues/detail?id=19917
        outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
        super.onSaveInstanceState(outState);
    }

Cheers, Stefan

Upvotes: 1

Views: 4164

Answers (1)

Giulio Piancastelli
Giulio Piancastelli

Reputation: 15817

You can't perform a fragment transaction when the activity that fragment belongs to is on the background. Since your dialog is really a fragment, dismissing it triggers a fragment transaction. I guess that the AsyncTask you are using to dismiss the dialog terminates its execution by calling onPostExecute while the activity that holds the fragment used to start it is on the background. Lo and behold, there the IllegalStateException is thrown.

There are at least a couple of workarounds. The simplest is to just rework your UI design avoiding a dialog to show the progess of your task. It's the suggestion contained in the official documentation guide on Dialogs, in the first aside on the right, just next to the green vertical bar.

The other possible solution I know of is slightly more complex. Well, it's just a different kind of complexity: code, rather than user interface design. You need to use a Handler to collect messages from the AsyncTask. Instead of directly dismissing the dialog, the AsyncTask sends a handler the message that it has finished. The handler receives the message, and processes it, dismissing the fragment. However, the handler must be capable of pausing and resuming message processing whenever the activity holding it goes on the background (e.g. in onPause) and gets resumed from the background (e.g. in onResume). In this way, when the activity goes on the background, and the AsyncTask finishes triggering the dialog dismissal, the handler receives the dismissal message, but holds it until the activity is resumed and it's now safe to perform the implied fragment transaction.

Upvotes: 6

Related Questions