150

I'm using the support library for my app. In my FragmentActivity I'm using an AsyncTask for downloading data from internet. In the onPreExecute() method I add a Fragment and in the onPostExecute() method I remove it again. When the orientation is changed in between, I get the above mentioned exception. Please take a look at the details:

private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> {
    DummyFragment dummyFragment; 
    FragmentManager fm;
    FragmentTransaction ft;

@Override
protected void onPreExecute() {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute");
    dummyFragment = DummyFragment.newInstance();
    fm = getSupportFragmentManager();
    ft = fm.beginTransaction();
    ft.add(dummyFragment, "dummy_fragment");
    ft.commit();
}

@Override
protected void onPostExecute(String result) {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute");
    ft = fm.beginTransaction();
    ft.remove(dummyFragment);
    ft.commit();
}

@Override
protected String doInBackground(String... name) {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/doInBackground");
    ...
}

I get following LogCut:

01-05 23:54:19.958: V/MyFragmentActivity(12783): onFriendAddedAsyncTask/onPreExecute
01-05 23:54:19.968: V/DummyFragment(12783): onAttach
01-05 23:54:19.968: V/DummyFragment(12783): onCreate
01-05 23:54:19.968: V/MyFragmentActivity(12783): onFriendAddedAsyncTask/doInBackground
01-05 23:54:19.973: V/DummyFragment(12783): onCreateView
01-05 23:54:19.973: V/DummyFragment(12783): onActivityCreated
01-05 23:54:19.973: V/DummyFragment(12783): onStart
01-05 23:54:19.973: V/DummyFragment(12783): onResume
01-05 23:54:21.933: V/MyFragmentActivity(12783): onSaveInstanceState
01-05 23:54:21.933: V/DummyFragment(12783): onSaveInstanceState
01-05 23:54:21.933: V/MyFragmentActivity(12783): onPause
01-05 23:54:21.933: V/DummyFragment(12783): onPause
01-05 23:54:21.938: V/MyFragmentActivity(12783): onStop
01-05 23:54:21.938: V/DummyFragment(12783): onStop
01-05 23:54:21.938: V/MyFragmentActivity(12783): onDestroy
01-05 23:54:21.938: V/DummyFragment(12783): onDestroyView
01-05 23:54:21.938: V/DummyFragment(12783): onDestroy
01-05 23:54:21.938: V/DummyFragment(12783): onDetach
01-05 23:54:21.978: V/MyFragmentActivity(12783): onCreate
01-05 23:54:21.978: V/DummyFragment(12783): onAttach
01-05 23:54:21.978: V/DummyFragment(12783): onCreate
01-05 23:54:22.263: V/MyFragmentActivity(12783): onStart
01-05 23:54:22.313: V/DummyFragment(12783): onCreateView
01-05 23:54:22.313: V/DummyFragment(12783): onActivityCreated
01-05 23:54:22.313: V/DummyFragment(12783): onStart
01-05 23:54:22.323: V/MyFragmentActivity(12783): onResume
01-05 23:54:22.323: V/MyFragmentActivity(12783): onPostResume
01-05 23:54:22.323: V/MyFragmentActivity(12783): onResumeFragments
01-05 23:54:22.323: V/DummyFragment(12783): onResume
01-05 23:54:27.123: V/MyFragmentActivity(12783): onFriendAddedAsyncTask/onPostExecute
01-05 23:54:27.123: D/AndroidRuntime(12783): Shutting down VM
01-05 23:54:27.123: W/dalvikvm(12783): threadid=1: thread exiting with uncaught exception (group=0x4001d7d0)
01-05 23:54:27.138: E/AndroidRuntime(12783): FATAL EXCEPTION: main
01-05 23:54:27.138: E/AndroidRuntime(12783): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1314)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1325)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:532)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at com.xyz.dummy.MyFragmentActivity$onFriendAddedAsyncTask.onPostExecute(MyFragmentActivity.java:447)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at com.xyz.dummy.MyFragmentActivity$onFriendAddedAsyncTask.onPostExecute(MyFragmentActivity.java:1)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.os.AsyncTask.finish(AsyncTask.java:417)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.os.AsyncTask.access$300(AsyncTask.java:127)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.os.Handler.dispatchMessage(Handler.java:99)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.os.Looper.loop(Looper.java:123)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at android.app.ActivityThread.main(ActivityThread.java:4627)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at java.lang.reflect.Method.invokeNative(Native Method)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at java.lang.reflect.Method.invoke(Method.java:521)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
01-05 23:54:27.138: E/AndroidRuntime(12783):    at dalvik.system.NativeStart.main(Native Method)

In other threads about similar problems the reason seems to be that the onPostExecute method is called before the onResume() method is called. But I get the exception even though onResume() is called before.

Does someone knows what's wrong?

The Activity looks like this:

public class MyFragmentActivity extends FragmentActivity implements OnFriendSelectedListener, OnFriendAddedListener, OnFriendOptionSelectedListener, LoaderCallbacks<Cursor> {

@Override
public void onCreate(Bundle savedInstanceState) {
    Log.v("MyFragmentActivity", "onCreate");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fragment_activity_layout);
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    FriendListFragment friendListFragment = (FriendListFragment)fm.findFragmentById(R.id.friend_list_fragment_layout);
    if (friendListFragment == null) {
        friendListFragment = new FriendListFragment(); 
        ft.add(R.id.friend_list_fragment_layout, friendListFragment);
        ft.commit();
        fm.executePendingTransactions();
        startService(new Intent(this, MyIntentService.class));
        getSupportLoaderManager().initLoader(CHECK_EMPTY_DATABASE, null, this);
    }
}

    @Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.fragment_activity_options_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    super.onOptionsItemSelected(item);
    switch (item.getItemId()) {
    case R.id.add_friend_menu_item:
        AddFriendDialogFragment addFriendDialogFragment = AddFriendDialogFragment.newInstance();
        addFriendDialogFragment.show(getSupportFragmentManager(), "add_friend_dialog_fragment");
        return true;
    default:
        return false;
    }
}

@Override
public void onFriendAdded(String name) {
    name = name.trim();
    if (name.length() > 0) {
        new onFriendAddedAsyncTask().execute(name);
    }
}

When using commitAllowingStateLoss() I get the following exception:

01-06 14:54:29.548: E/AndroidRuntime(18020): FATAL EXCEPTION: main
01-06 14:54:29.548: E/AndroidRuntime(18020): java.lang.IllegalStateException: Activity has been destroyed
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1329)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:536)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at com.xyz.dummy.FadiaFragmentActivity$onFriendAddedAsyncTask.onPostExecute(FadiaFragmentActivity.java:461)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at com.xyz.dummy.FadiaFragmentActivity$onFriendAddedAsyncTask.onPostExecute(FadiaFragmentActivity.java:1)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.os.AsyncTask.finish(AsyncTask.java:417)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.os.AsyncTask.access$300(AsyncTask.java:127)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.os.Handler.dispatchMessage(Handler.java:99)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.os.Looper.loop(Looper.java:123)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at android.app.ActivityThread.main(ActivityThread.java:4627)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at java.lang.reflect.Method.invokeNative(Native Method)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at java.lang.reflect.Method.invoke(Method.java:521)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
01-06 14:54:29.548: E/AndroidRuntime(18020):    at dalvik.system.NativeStart.main(Native Method)

I get the same IllegalStateExeption when I implement the AsynTask as follows, because the findFragmentById() method returns a null pointer.

private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> {

    protected void onPreExecute() {
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute");
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        DummyFragment dummyFragment = DummyFragment.newInstance();
        ft.add(R.id.dummy_fragment_layout, dummyFragment);
        ft.commit();
    }

    protected void onPostExecute(String result) {
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute");
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        DummyFragment dummyFragment = (DummyFragment) fm.findFragmentById(R.id.dummy_fragment_layout);
        ft.remove(dummyFragment);
        ft.commitAllowingStateLoss();
    }

In the next step I use a handler for the adding and removing the DummyFragment. Additionally I've added some more debug output.

private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> {

    @Override
    protected void onPreExecute() {
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute " + getSupportFragmentManager());
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute " + getSupportFragmentManager().findFragmentById(R.id.dummy_fragment_layout));
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute " + getSupportFragmentManager().findFragmentById(R.id.friend_list_fragment_layout));

        new Handler().post(new Runnable() {
            public void run() {
                Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute " + getSupportFragmentManager());
                Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute " + getSupportFragmentManager().findFragmentById(R.id.dummy_fragment_layout));
                Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute " + getSupportFragmentManager().findFragmentById(R.id.friend_list_fragment_layout));
                FragmentManager fm = getSupportFragmentManager();
                FragmentTransaction ft = fm.beginTransaction();
                DummyFragment dummyFragment = DummyFragment.newInstance();
                ft.add(R.id.dummy_fragment_layout, dummyFragment);
                ft.commit();
            }
        });

    @Override
    protected void onPostExecute(String result) {
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute " + getSupportFragmentManager());
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute " + getSupportFragmentManager().findFragmentById(R.id.dummy_fragment_layout));
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute " + getSupportFragmentManager().findFragmentById(R.id.friend_list_fragment_layout));

        new Handler().post(new Runnable() {
            public void run() {
                Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute " + getSupportFragmentManager());
                Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute " + getSupportFragmentManager().findFragmentById(R.id.dummy_fragment_layout));
                Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute " + getSupportFragmentManager().findFragmentById(R.id.friend_list_fragment_layout));
                FragmentManager fm = getSupportFragmentManager();
                FragmentTransaction ft = fm.beginTransaction();
                DummyFragment dummyFragment = (DummyFragment) fm.findFragmentById(R.id.dummy_fragment_layout);
                ft.remove(dummyFragment);
                ft.commitAllowingStateLoss();
            }
        });

I get following LogCut:

01-07 19:00:17.273: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPreExecute FragmentManager{45e384a8 in MyFragmentActivity{45e38358}}
01-07 19:00:17.273: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPreExecute null
01-07 19:00:17.273: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPreExecute FriendListFragment{45e38ab0 #0 id=0x7f0a0002}
01-07 19:00:17.283: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPreExecute FragmentManager{45e384a8 in MyFragmentActivity{45e38358}}
01-07 19:00:17.288: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/doInBackground
01-07 19:00:17.288: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPreExecute null
01-07 19:00:17.288: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPreExecute FriendListFragment{45e38ab0 #0 id=0x7f0a0002}
01-07 19:00:17.308: V/DummyFragment(4124): onAttach DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:17.308: V/DummyFragment(4124): onCreate DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:17.308: V/DummyFragment(4124): onCreateView DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:17.308: V/DummyFragment(4124): onActivityCreated DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:17.308: V/DummyFragment(4124): onStart DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:17.313: V/DummyFragment(4124): onResume DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.098: V/MyFragmentActivity(4124): onSaveInstanceState DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.098: V/DummyFragment(4124): onSaveInstanceState DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.098: V/MyFragmentActivity(4124): onPause DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.098: V/DummyFragment(4124): onPause DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.103: V/MyFragmentActivity(4124): onStop DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.103: V/DummyFragment(4124): onStop DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.103: V/MyFragmentActivity(4124): onDestroy DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.103: V/DummyFragment(4124): onDestroyView DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.108: V/DummyFragment(4124): onDestroy DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.113: V/DummyFragment(4124): onDetach DummyFragment{45dd7498 #2 id=0x7f0a0004}
01-07 19:00:18.138: V/MyFragmentActivity(4124): onCreate
01-07 19:00:18.138: V/FriendListFragment(4124): FriendListFragment
01-07 19:00:18.138: V/FriendListFragment(4124): onAttach FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.138: V/FriendListFragment(4124): onCreate FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.148: V/DummyFragment(4124): onAttach DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.153: V/DummyFragment(4124): onCreate DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.523: V/MyFragmentActivity(4124): onStart DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.543: V/FriendListFragment(4124): onActivityCreated FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.548: V/DummyFragment(4124): onCreateView DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.548: V/DummyFragment(4124): onActivityCreated DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.548: V/FriendListFragment(4124): onLoadFinished FragmentManager{45d8e478 in MyFragmentActivity{45e4a6d8}}
01-07 19:00:18.548: V/FriendListFragment(4124): onLoadFinished FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.553: V/DummyFragment(4124): onStart DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.553: V/FriendListFragment(4124): onLoadFinished FragmentManager{45d8e478 in MyFragmentActivity{45e4a6d8}}
01-07 19:00:18.553: V/FriendListFragment(4124): onLoadFinished FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.558: V/MyFragmentActivity(4124): onResume DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.558: V/MyFragmentActivity(4124): onPostResume DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.558: V/MyFragmentActivity(4124): onResumeFragments DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.558: V/FriendListFragment(4124): onResume FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.563: V/FriendListFragment(4124): onCreateLoader FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.563: V/DummyFragment(4124): onResume DummyFragment{45d7d1a0 #2 id=0x7f0a0004}
01-07 19:00:18.723: V/FriendListFragment(4124): onLoadFinished FragmentManager{45d8e478 in MyFragmentActivity{45e4a6d8}}
01-07 19:00:18.723: V/FriendListFragment(4124): onLoadFinished FriendListFragment{45e4a7f8 #0 id=0x7f0a0002}
01-07 19:00:18.893: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPostExecute FragmentManager{45e384a8 in null}}
01-07 19:00:18.893: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPostExecute null
01-07 19:00:18.893: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPostExecute null
01-07 19:00:18.923: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPostExecute FragmentManager{45e384a8 in null}}
01-07 19:00:18.923: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPostExecute null
01-07 19:00:18.923: V/MyFragmentActivity(4124): onFriendAddedAsyncTask/onPostExecute null
01-07 19:00:18.928: D/AndroidRuntime(4124): Shutting down VM
01-07 19:00:18.928: W/dalvikvm(4124): threadid=1: thread exiting with uncaught exception (group=0x4001d7d0)
01-07 19:00:18.938: E/AndroidRuntime(4124): FATAL EXCEPTION: main
01-07 19:00:18.938: E/AndroidRuntime(4124): java.lang.IllegalStateException: Activity has been destroyed
01-07 19:00:18.938: E/AndroidRuntime(4124):     at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1329)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:536)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at com.xyz.dummy.MyFragmentActivity$onFriendAddedAsyncTask$2.run(MyFragmentActivity.java:476)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at android.os.Handler.handleCallback(Handler.java:587)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at android.os.Handler.dispatchMessage(Handler.java:92)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at android.os.Looper.loop(Looper.java:123)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at android.app.ActivityThread.main(ActivityThread.java:4627)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at java.lang.reflect.Method.invokeNative(Native Method)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at java.lang.reflect.Method.invoke(Method.java:521)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
01-07 19:00:18.938: E/AndroidRuntime(4124):     at dalvik.system.NativeStart.main(Native Method)

In onPreExecute() the FriendListFragment has the id=0x7f0a0002. Inside the handler the DummyFragment is created with id=0x7f0a0004. In onPostExecute() both IDs are null. In onPreExecute() the address of MyFragmentActivity is 45e38358. but in onPostExecute() it's null. But in both methods the FragmentManager address is 45e384a8. I guess onPostExecute uses an invalid FragmentManager. But why?

samo
  • 373
  • 3
  • 8
  • 23
  • 1
    I had this issue once and fixed it by replacing the commit with this: commitAllowingStateLoss(), can you try this? – Cata Jan 06 '13 at 00:04
  • I've already tried this, but without success. According the LogCat the Fragment should be in the right state. – samo Jan 06 '13 at 01:09
  • Can you please post your Activity code? – Robert Estivill Jan 06 '13 at 01:12
  • When I use commitAllowingStateLoss() I get a different exception (see above). – samo Jan 06 '13 at 14:01
  • Is the `DummyFragment` set to retain it's instance? Did you tried to modify the `onPostExecute` to first search for the `DummyFragment` instance using the "dummy_fragment" tag? – user Jan 06 '13 at 15:42
  • I was just asking about `setRetainInstance`, don't use it. Did you tried the last recommendation for the `onPostExecute()`(this: `DummyFragment df = getSupportFragmentManager().findFragmentByTag("dummy_fragment");FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.remove(df); ft.commit();`)? – user Jan 06 '13 at 16:09
  • I tried this, but without success. – samo Jan 06 '13 at 16:26
  • I'm just reading [link](http://stackoverflow.com/a/12681526/1624366) and [link](http://code.google.com/p/android/issues/detail?id=23096#c4). But it seems like it's about a different issue. – samo Jan 06 '13 at 17:21
  • It's dangerous to commit `FragmentTransaction`s from inside asynchronous callback methods such as `onPostExecute()`. See the end of this [**blog post**](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) for a discussion of why this is true. – Alex Lockwood Aug 20 '13 at 23:09
  • To be perfectly honest, showing a dialog at the beginning of an `AsyncTask` and dismissing it when it finishes isn't great for the user experience. If possible, you should avoid doing this (simply showing a indeterminate dialog spinner in the screen's content view is way less janky). – Alex Lockwood Aug 20 '13 at 23:12
  • 7
    For those of you who are still searching for a solution... see this [**blog post**](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) about this topic for more information. – Alex Lockwood Oct 11 '13 at 20:45
  • I had this exception recently: My case is that I make a request (HTTP) to the server in the onCreate method of the main Activity. Request goes in the doInBackground of the AsyncTask. The result of this is processed in runOnUiThread of the activity, where I pass the response in the bundle to a fragment that I show in a transaction. Every thing is fine, but when I minimize on the first request (minimize when the splash screen activity is about to launch this main activity), When I maximize, this exception comes. I handled the exception showing the message in the dialog box to user to try again. – Abhinav Saxena Apr 28 '14 at 05:31
  • possible duplicate of [getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"](http://stackoverflow.com/questions/7469082/getting-exception-illegalstateexception-can-not-perform-this-action-after-onsa) – rds Jul 20 '15 at 15:09

18 Answers18

101

You should do the transaction in a Handler as follows:

@Override
protected void onPostExecute(String result) {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute");
    new Handler().post(new Runnable() {
            public void run() {
                fm = getSupportFragmentManager();
                ft = fm.beginTransaction();
                ft.remove(dummyFragment);
                ft.commit();
            }
        });
}
Oleg Vaskevich
  • 12,444
  • 6
  • 63
  • 80
  • 14
    It doesn't help. Behaviour is the same like before. – samo Jan 06 '13 at 09:59
  • @samo please have you been able to solve the issue? I have a very similar situation [link](http://stackoverflow.com/questions/14182432/on-rotation-i-get-unable-to-destroy-activity-can-not-perform-this-action-af) – Lisa Anne Jan 06 '13 at 13:38
  • No, I'm still searching for a solution – samo Jan 06 '13 at 15:38
  • The reason is that you are leaking your `Activity` because you're keeping a reference to the `FragmentManager`. Try my edit above which should require the `FragmentManager` and make sure to set `fm` when your `Activity` is destroyed. – Oleg Vaskevich Jan 06 '13 at 23:36
  • Also with the handler it doesn't work. I guess it's because getSupportFragmentManager() returns the old, but invalid reference. Please see above for details. – samo Jan 07 '13 at 18:35
  • How do you keep the reference up-to-date? Do you use additional attach/detach methods to update the reference? – samo Jan 08 '13 at 05:52
  • 3
    Consider this code: `private static WeakReference mActivity = null;` – Oleg Vaskevich Jan 08 '13 at 06:15
  • 2
    In a nutshell `WeakReference` prevents you from leaking the activity... you need to call `mActivity.get()` to actually get the instance, and it will be null if the activity was destroyed. To update it, you will need to write `mActivity = new WeakReference(this);` - a good place is in `onCreate()` - which will update the reference. – Oleg Vaskevich Jan 08 '13 at 06:17
  • 115
    For those of you who are still searching for a solution... see this [**blog post**](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) about this topic for more information. – Alex Lockwood Aug 20 '13 at 23:07
  • Excelent Blog Post @AlexLockwood! That fixed my problem while educating me :). Thanks! – albertosh Oct 11 '13 at 14:50
  • Thanks man, you are a hero for me... nothing worked only your answer. keep up the good work – Harsh Jun 03 '15 at 14:36
  • can anyone help me in http://stackoverflow.com/questions/43617600/illegal-state-exception-in-androidcan-not-perform-this-action-after-onsaveinsta – vm345 Apr 28 '17 at 18:29
  • Great. It solved my problem. But could anyone please explain how? – Wijay Sharma May 17 '18 at 08:05
  • @WijaySharma I suggest you to read this to understand Activity states better - https://androidclarified.wordpress.com/2018/06/13/illegalstateexception-cannot-perform-this-action-after-onsaveinstancestate/ – Irshad Kumail Jul 01 '18 at 10:30
  • @IrshadKumail This has nothing to do with activity lifecycles. This can happen when you try to pop a fragment when there is another fragment added to the same fragment manager that hosts the fragment you want to pop. – Farid Jan 17 '22 at 20:24
55

Thanks Oleg Vaskevich. Using a WeakReference of the FragmentActivity solved the problem. My code looks as follows now:

public class MyFragmentActivity extends FragmentActivity implements OnFriendAddedListener {

    private static WeakReference<MyFragmentActivity> wrActivity = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        wrActivity = new WeakReference<MyFragmentActivity>(this);
        ...

    private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> {

        @Override
        protected void onPreExecute() {
            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commit();
        }

        @Override
        protected void onPostExecute(String result) {
            final Activity activity = wrActivity.get();
            if (activity != null && !activity.isFinishing()) {
                FragmentManager fm = activity.getSupportFragmentManager();
                FragmentTransaction ft = fm.beginTransaction();
                DummyFragment dummyFragment = (DummyFragment) fm.findFragmentById(R.id.dummy_fragment_layout);
                ft.remove(dummyFragment);
                ft.commitAllowingStateLoss();
            }
        }
Piyush
  • 18,895
  • 5
  • 32
  • 63
samo
  • 373
  • 3
  • 8
  • 23
  • the weak reference idea is indeed a very smart one, this would allow the object be easily garbage collected when necessary. thumbs up samo! – Jimmy Ilenloa Feb 18 '14 at 14:49
  • Why static is used here ? What if I use `MyFragmentActivity mActivity = this ?` With out static & WeakReference – Bharath May 29 '17 at 05:40
  • Static reference is pretty bad engineering, you should rather tie your asynchtask to the lifecycle and cancel when its necessary – breakline Jun 06 '18 at 09:10
  • A weak reference is still a dumb idea. The weak reference naming misleads a lot of people. Just to be clear; if there is a reference to a weak reference object your view will still leak even if the view is destroyed – Farid Jan 17 '22 at 20:21
41

I believe the correct answer to this question is the following method.

public abstract int commitAllowingStateLoss ()

Like commit() but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.

The above description relates to this method.

protected void onSaveInstanceState(android.os.Bundle outState)

This problem occurs precisely when the device goes to sleep.

http://developer.android.com/reference/android/app/FragmentTransaction.html

alex
  • 104
  • 4
  • 10
31

Short And working Solution :

Follow Simple Steps :

Step 1 : Override onSaveInstanceState state in respective fragment. And remove super method from it.

@Override
public void onSaveInstanceState(Bundle outState) {
}

Step 2 : Use CommitAllowingStateLoss(); instead of commit(); while fragment operations.

fragmentTransaction.commitAllowingStateLoss();
Piyush
  • 18,895
  • 5
  • 32
  • 63
Basbous
  • 3,927
  • 4
  • 34
  • 62
13

Check if the activity isFinishing() before showing the fragment.

Example:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}
Naskov
  • 4,121
  • 5
  • 38
  • 62
9

Use commitAllowingStateLoss() instead of commit().

when you use commit() it will can throw an exception if state loss occurs but commitAllowingStateLoss() saves transaction without state loss so that will doesn't throw an exception if state loss occurs.

5

I had a similar problem which I fixed by moving some fragment transaction code from onResume() into onStart().

To be more precise: My app is a launcher. After pressing the Android Home button, the user can choose a launcher until his/her decision is remembered. When going "back" at this point (e. g. by tapping in the greyish area) the app crashed.

Maybe this helps somebody.

Piyush
  • 18,895
  • 5
  • 32
  • 63
Alex
  • 1,395
  • 1
  • 13
  • 22
4

This solved my problem: Kotlin Code:

val fragmentTransaction = activity.supportFragmentManager.beginTransaction()
fragmentTransaction.add(dialogFragment, tag)
fragmentTransaction.commitAllowingStateLoss()

How is commitAllowingStateLoss() is different than commit() ?

As per documentation:

Like commit() but allows the commit to be executed after an activity's state is saved. https://developer.android.com/reference/android/app/FragmentTransaction#commitAllowingStateLoss()

P.S: you can show Fragment Dialogs or can load fragments by this method. Applicable for both.

Wajid
  • 2,235
  • 1
  • 19
  • 29
2

The reason for the exception is the re-creation of the FragmentActivity during the runtime of the AsyncTask and the access to the previous, destroyed FragmentActivity in onPostExecute() afterwards.

The problem is to get a valid reference to the new FragmentActivity. There is no method for this neither getActivity() nor findById() or something similar. This forum is full of threads according this issue (e.g. search for "Activity context in onPostExecute"). Some of them are describing workarounds (until now I didn't find a good one).

Maybe it would be a better solution to use a Service for my purpose.

Piyush
  • 18,895
  • 5
  • 32
  • 63
samo
  • 373
  • 3
  • 8
  • 23
2

That happened for me, because I was invoking commit() from subfragment which was leaking activity. It kept activity as a property and on a rotation activity variable was not updated by onAttach(); So I was trying to commit transaction on zombie Activity by retained (setRetainInstance(true);) fragment.

Piyush
  • 18,895
  • 5
  • 32
  • 63
Malachiasz
  • 7,126
  • 2
  • 35
  • 49
1

For what it's worth; I had this error on a app that was running services in the background. On one of them a timeout dialog had to be shown to the user. That dialog was the issue causing this error if the app was no longer running in the foreground.

In our case showing the dialog wasn't useful when app was in background so we just kept track of that (boolean flagged onPause en onResume) and then only show the dialog when the app is actually visible to the user.

hcpl
  • 17,382
  • 7
  • 72
  • 73
1

Solution 1: override onSaveInstanceState() and remove the super call in it.

@Override
public void onSaveInstanceState(Bundle outState) {
}

Solution 2: override onSaveInstanceState() and remove your fragment before the super call

@Override
public void onSaveInstanceState(Bundle outState) {
     // TODO: Add code to remove fragment here
     super.onSaveInstanceState(outState);
}
Piyush
  • 18,895
  • 5
  • 32
  • 63
Lingkun Kong
  • 346
  • 2
  • 6
1

This problem happens when a process tries to manipulate an Activity whose onStop() has been called. It's not necessarily tied to fragment transaction but also other methods like onBackPressed().

In addition to AsyncTask, another source of such problem is the misplacement of bus pattern's subscription. Usually Event Bus or RxBus's subscription is registered during Activity's onCreate and de-registered in onDestroy. If a new Activity starts and publishes an event intercepted by subscribers from the previous Activity then it may produce this error. If this happens then one solution is to move subscription registration and de-registration to onStart() and onStop().

inmyth
  • 8,880
  • 4
  • 47
  • 52
1

There is one alternate solution (NOT the best solution) for this problem, but works. Using flag you can handle it, like below

/**
 * Flag to avoid "java.lang.IllegalStateException: Can not perform this action after
 * onSaveInstanceState". Avoid Fragment transaction until onRestoreInstanceState or onResume
 * gets called.
 */
private boolean isOnSaveInstanceStateCalled = false;


@Override
public void onRestoreInstanceState(final Bundle bundle) {
    .....
    isOnSaveInstanceStateCalled = false;
    .....
}

@Override
public void onSaveInstanceState(final Bundle outState) {
    .....
    isOnSaveInstanceStateCalled = true;
    .....
}

@Override
public void onResume() {
    super.onResume();
    isOnSaveInstanceStateCalled = false;
    .....
}

And you can check this boolean value while doing fragment transaction.

private void fragmentReplace(Fragment fragment, String fragmentTag){
    if (!isOnSaveInstanceStateCalled) {
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.layout_container, fragment, fragmentTag)
                .commit();
    }
}
Pankaj Kumar
  • 81,967
  • 29
  • 167
  • 186
0

My app has a fragment to loading in 3 secs, but when the fist screen is preparing to show, I press home button and resume run it, it show the same error, so It edit my code and it ran very smooth:

new Handler().post(new Runnable() {
        public void run() {
            if (saveIns == null) {
                mFragment = new Fragment_S1_loading();
                getFragmentManager().beginTransaction()
                        .replace(R.id.container, mFragment).commit();
            }
            getActionBar().hide();
            // Loading screen in 3 secs:
            mCountDownTimerLoading = new CountDownTimer(3000, 1000) {

                @Override
                public void onTick(long millisUntilFinished) {

                }

                @Override
                public void onFinish() {
                    if (saveIns == null) {// TODO bug when start app and press home
                                            // button
                        getFragmentManager()
                                .beginTransaction()
                                .replace(R.id.container,
                                        new Fragment_S2_sesstion1()).commitAllowingStateLoss();
                    }
                    getActionBar().show();
                }
            }.start();
        }
    });

NOTE: add commitAllowingStateLoss() instead of commit()

nobjta_9x_tq
  • 1,205
  • 14
  • 16
0

IllegalStateException is encountered if you commit any fragment transaction after the activity has lost its state- Activity is not in foreground. This is commonly encountered when you try to commit any fragment in AsyncTask or after a network request.

To avoid this crash you just need to delay any fragment transaction untill the state of activity is restored. Following is how it is done

Declare two private boolean variables

public class MainActivity extends AppCompatActivity {

    //Boolean variable to mark if the transaction is safe
    private boolean isTransactionSafe;

    //Boolean variable to mark if there is any transaction pending
    private boolean isTransactionPending;

Now in onPostResume() and onPause we set and unset our boolean variable isTransactionSafe. Idea is to mark trasnsaction safe only when the activity is in foreground so there is no chance of stateloss.

/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
 */
public void onPostResume(){
    super.onPostResume();
    isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
 */

public void onPause(){
    super.onPause();
    isTransactionSafe=false;

}

private void commitFragment(){
    if(isTransactionSafe) {
        MyFragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.frame, myFragment);
        fragmentTransaction.commit();
    }
}

What we have done so far will save from IllegalStateException but our transactions will be lost if they are done after the activity moves to background, kind of like commitAllowStateloss(). To help with that we have isTransactionPending boolean variable

public void onPostResume(){
   super.onPostResume();
   isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
   if (isTransactionPending) {
      commitFragment();
   }
}


private void commitFragment(){

 if(isTransactionSafe) {
     MyFragment myFragment = new MyFragment();
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     fragmentTransaction.add(R.id.frame, myFragment);
     fragmentTransaction.commit();
     isTransactionPending=false;
 }else {
     /*
     If any transaction is not done because the activity is in background. We set the
     isTransactionPending variable to true so that we can pick this up when we come back to
foreground
     */
     isTransactionPending=true;
 }
}

This article explains quite in detail of why this exception is encountered and compares various methods to resolve it . Highly recommended

Irshad Kumail
  • 1,243
  • 11
  • 10
  • I did something similar, but I used lifecycle and execute the transaction only if it's at least resumed. In emulator works fine, but on a real device it crashes. Why did you use lifecycle in your example? – rtsketo Jul 28 '21 at 22:51
0

I had the same exception and I tried many snippet that I found here on this stackoverflow discussion, but no snippets worked for me.

But I was able to resolve all issues, I will share with you the solutions :

  • In a First part : I tried to show a DialogFragment on a Activity but from another java class. Then by checking the attribute of that instance, I found that was an old instance of the Activity, it was not the current running Activity. [More precisly I was using socket.io, and I forgot to do a socket.off("example",example) ... so it attached to an old instance of the activity. ]

  • In a Second part : I was trying to show a DialogFragment in a Activity when I come back to it with an intent, but when I checked my logs, I saw that when it tried to show the fragment the activity was still not in onStart method, so it crashed the app because it didn't find the Activity class to show the fragment on it.

Some tips : check with some attributes if you are not using an old instance of your activity with which one you are trying to show your fragment, or check your activity lifecycle before showing your fragment and be sure you are in onStart or onResume before showing it.

I hope those explanations will help you.

Raj
  • 197
  • 1
  • 2
  • 11
0

Error: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState when interstitial adclosed with onBackPressed()/getSupportFragmentManager().popBackStack()

OP asked about async that no more use in nowdays. But I want to add this answer here because 95% people come here due to same error come when interstitial ad closed in fragment.

Problem: This happen when we call on onBackPressed() in onInterstitialAdClosed() that time onBackPressed() called before Activity onResume(). So this error comes.

Solution: after adClose, inside onResume we call function that check If adClosed & ActivityResumed then call onBackPressed()

make sure this all code inside activity not in fragment class.

//make 2 boolean that we use for check both condition
public static boolean bool_Is_ActivityResume= false;
public static boolean bool_Is_InterstitialAdClosed= false;

@Override
public void onInterstitialAdClosed() {
    //this function of Intersitital from "adLISTENER"
    IronSource.loadInterstitial();
    Log.d("AKG", "onInterstitialAdClosed Listener");
    //onBackPressed(); now here we don't use onBackPress
    bool_Is_InterstitialAdClosed=true;
}
/////////////////////////////////////////////////////

@Override
protected void onPause() {
    super.onPause();
    IronSource.onPause(this); //ignore im using IronSource

    bool_Is_ActivityResume=false;
}

@Override
protected void onResume() {
    super.onResume();
    IronSource.onResume(this);

    //use these code below "super.onResume();
    bool_Is_ActivityResume=true;
    //after activity resume we check condition of interstitial and then go back
    funInterstitialAdClosedAndResumedNowGOBACK();
}

public void funInterstitialAdClosedAndResumedNowGOBACK(){
    Log.e("AKG", "funInterstitialAdClosedNowGOBACK :" );
    //im using loge because it highlight your log inside "Logcat" hahaha

    if (bool_Is_ActivityResume && bool_Is_InterstitialAdClosed){
        //if "interstitial Ad closed" & Activity "Resumed" then call fragment back
        try {
            //Now you can call your "onBackPress" or "getSupportFragmentManager().popBackStack()"  here
            onBackPressed();

            //after call back..  we again do false to bool_Is_InterstitialAdClosed
            bool_Is_InterstitialAdClosed=false
        } catch (Exception e){
            Log.e("AKG", "Error in : "+e.getMessage() );
        }
    }
}

Happy Coding:)

TexD
  • 162
  • 1
  • 10