Search code examples
androidandroid-fragmentsonitemclicklistenerillegalstateexceptionfragmenttransaction

FragmentTransaction commit in OnClick “IllegalStateException: Can not perform this action after onSaveInstanceState”


I have an app in the market and I get the following exception:

Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
   at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1343)
   at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1361)
   at android.app.BackStackRecord.commitInternal(BackStackRecord.java:729)
   at android.app.BackStackRecord.commit(BackStackRecord.java:705)
   at com.mysupercoolapp.ui.fragments.ResultFragment$2.onItemClick(ResultFragment.java:139)
   at android.widget.AdapterView.performItemClick(AdapterView.java:339)
   at android.widget.AbsListView.performItemClick(AbsListView.java:1544)
   at android.widget.AbsListView$PerformClick.run(AbsListView.java:3721)
   at android.widget.AbsListView$3.run(AbsListView.java:5660)
   at android.os.Handler.handleCallback(Handler.java:739)
   at android.os.Handler.dispatchMessage(Handler.java:95)
   at android.os.Looper.loop(Looper.java:145)
   at android.app.ActivityThread.main(ActivityThread.java:6837)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)

I already read most of the topics regarding this issue but I do not know what I am doing wrong in my code. It's not like I am replacing a Fragment in the onResume method. I am also not caching a FragmentTransaction instance. I replace the Fragment when an user clicks an item in a ListView.

My Fragment's code workflow is the following

OnCreateView > executes AsyncTask to load some items from the database > onPostExecute calls a method which sets the adapter for the ListView and it adds an OnItemClickListener so when the user presses an item in the ListView it opens up the item in an other Fragment.

Here's the code for it:

@Override
protected void onPostExecute(ResultAdapter adapter) {
     // post execute in AsyncTask
     setListAdapter(adapter);
}

public void setListAdapter(final ResultAdapter adapter) {
    if (adapter != null && isAdded()) {
        resultList.setAdapter(adapter);
        resultList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final FragmentTransaction ft = getFragmentManager().beginTransaction();
                SomeFragment fragment = new SomeFragment();
                ft.replace(R.id.content_frame, fragment, "main_fragment");
                ft.addToBackStack(null);
                ft.commit();
            }
        });
        adapter.notifyDataSetChanged();
    }
}

Is there a better solution than calling commitAllowingStateLoss() or overriding onSaveInstanceState() ?

P.S.: I am targetting API > 14 and am using the standard android.app.FragmentTransaction package.


Solution

  • This problem will occur when onPostExecute() is called when the application is minimized, especially when onPostExecute() is called after the onSaveInstanceState() method of the activity is called.

    If you are worried about the state loss, then I suggest that you cancel your AsyncTask when the user has minimized the application, (probably in the onStop() method). Then in your onPostExecute(), call setListAdapter(adapter) only if the AsyncTask is not cancelled. This would ensure that you don't end up setting the adapter after the onSaveInstanceState() is called.

    More info on how to accomplish this is here: https://stackoverflow.com/a/12342937/4747587