java.lang.IllegalStateException: impossible d'exécuter cette action après onSaveInstanceState

135

J'utilise la bibliothèque d'assistance de mon application. Dans mon FragmentActivity, j'utilise une AsyncTask pour télécharger des données depuis Internet. Dans la méthode onPreExecute (), j'ajoute un fragment et dans la méthode onPostExecute () je le supprime à nouveau. Lorsque l'orientation est modifiée entre les deux, j'obtiens l'exception mentionnée ci-dessus. Veuillez jeter un œil aux détails:

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");
    ...
}

Je reçois 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)

Dans d'autres threads sur des problèmes similaires, la raison semble être que la méthode onPostExecute est appelée avant que la méthode onResume () ne soit appelée. Mais j'obtiens l'exception même si onResume () est appelé avant.

Est-ce que quelqu'un sait ce qui ne va pas?

L'activité ressemble à ceci:

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);
    }
}

Lorsque vous utilisez commitAllowingStateLoss (), j'obtiens l'exception suivante:

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)

J'obtiens le même IllegalStateExeption lorsque j'implémente l'AsynTask comme suit, car la méthode findFragmentById () renvoie un pointeur nul.

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();
    }

Dans l'étape suivante, j'utilise un gestionnaire pour l'ajout et la suppression du DummyFragment. De plus, j'ai ajouté une sortie de débogage supplémentaire.

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();
            }
        });

Je reçois 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)

Dans onPreExecute (), FriendListFragment a l'id = 0x7f0a0002. À l'intérieur du gestionnaire, le DummyFragment est créé avec id = 0x7f0a0004. Dans onPostExecute (), les deux ID sont nuls. Dans onPreExecute (), l'adresse de MyFragmentActivity est 45e38358. mais dans onPostExecute (), il est nul. Mais dans les deux méthodes, l'adresse FragmentManager est 45e384a8. Je suppose que onPostExecute utilise un FragmentManager invalide. Mais pourquoi?

samo
la source
1
J'ai eu ce problème une fois et je l'ai résolu en remplaçant le commit par ceci: commitAllowingStateLoss (), pouvez-vous essayer ceci?
Cata
J'ai déjà essayé cela, mais sans succès. Selon le LogCat, le fragment doit être dans le bon état.
samo
Pouvez-vous s'il vous plaît publier votre code d'activité?
Robert Estivill
Quand j'utilise commitAllowingStateLoss (), j'obtiens une exception différente (voir ci-dessus).
samo
6
Pour ceux d'entre vous qui recherchent encore une solution ... consultez ce billet de blog sur ce sujet pour plus d'informations.
Alex Lockwood du

Réponses:

97

Vous devez effectuer la transaction Handlercomme suit:

@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
la source
12
Cela n'aide pas. Le comportement est le même qu'avant.
samo
@samo, avez-vous pu résoudre le problème? J'ai un lien de
Lisa Anne
3
Considérez ce code:private static WeakReference<FragmentActivity> mActivity = null;
Oleg Vaskevich
2
En un mot, cela WeakReferencevous empêche de divulguer l'activité ... vous devez appeler mActivity.get()pour obtenir réellement l'instance, et elle sera nulle si l'activité a été détruite. Pour le mettre à jour, vous devrez écrire mActivity = new WeakReference<FragmentActivity>(this);- il y a un bon endroit onCreate()- qui mettra à jour la référence.
Oleg Vaskevich
107
Pour ceux d'entre vous qui recherchent encore une solution ... consultez ce billet de blog sur ce sujet pour plus d'informations.
Alex Lockwood
55

Merci Oleg Vaskevich. L'utilisation d'un WeakReferencedes a FragmentActivityrésolu le problème. Mon code ressemble maintenant à ceci:

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();
            }
        }
samo
la source
l'idée de référence faible est en effet très intelligente, cela permettrait à l'objet d'être facilement ramassé lorsque cela est nécessaire. bravo samo!
Jimmy Ilenloa
Pourquoi la statique est-elle utilisée ici? Et si j'utilise MyFragmentActivity mActivity = this ?With out static & WeakReference
Bharat
La référence statique est une assez mauvaise ingénierie, vous devriez plutôt lier votre asynchtask au cycle de vie et l'annuler lorsque cela est nécessaire
breakline
38

Je pense que la bonne réponse à cette question est la méthode suivante.

public abstract int commitAllowingStateLoss ()

Comme commit () mais permet au commit d'être exécuté après la sauvegarde de l'état d'une activité. Ceci est dangereux car la validation peut être perdue si l'activité doit être restaurée ultérieurement à partir de son état, cela ne doit donc être utilisé que dans les cas où l'état de l'interface utilisateur peut changer de manière inattendue sur l'utilisateur.

La description ci-dessus concerne ce procédé.

protected void onSaveInstanceState(android.os.Bundle outState)

Ce problème se produit précisément lorsque l'appareil se met en veille.

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

Alex
la source
25

Solution courte et fonctionnelle:

Suivez des étapes simples:

Étape 1 : Remplacer l' onSaveInstanceStateétat dans le fragment respectif. Et supprimez-en la super méthode.

@Override
public void onSaveInstanceState(Bundle outState) {
}

Étape 2 : Utilisez CommitAllowingStateLoss();au lieu d' commit();opérations de fragment while.

fragmentTransaction.commitAllowingStateLoss();
Basbous
la source
2
Merci. Cela fonctionne pour moi, mais je sais que ce n'est pas la meilleure solution.
wendigo
2
suppression de la super méthode, désactive également l'enregistrement de vos états de fragment.
Juan Mendez
1
Merci beaucoup. Cela générait une exception, cette solution fonctionnait bien ..
Deepak
11

Vérifiez si l'activité isFinishing()avant d'afficher le fragment.

Exemple:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}
Naskov
la source
5

J'ai eu un problème similaire que j'ai résolu en déplaçant un fragment de code de transaction de onResume()vers onStart().

Pour être plus précis: mon application est un lanceur. Après avoir appuyé sur le bouton Accueil d'Android, l'utilisateur peut choisir un lanceur jusqu'à ce que sa décision soit mémorisée. En retournant "en arrière" à ce stade (par exemple en tapant dans la zone grisâtre), l'application s'est plantée.

Peut-être que cela aide quelqu'un.

Alex
la source
4

Utilisez à la commitAllowingStateLoss()place de commit().

lorsque vous l'utilisez, commit()il peut lever une exception en cas de perte d'état, mais commitAllowingStateLoss()enregistre la transaction sans perte d'état, de sorte que cela ne lève pas d'exception en cas de perte d'état.

Felhi
la source
2

Cela m'est arrivé, car commit()j'invoquais à partir d'un sous-fragment qui fuyait une activité. Il a gardé l'activité en tant que propriété et sur une variable d'activité de rotation n'a pas été mise à jour par onAttach();Donc j'essayais de valider la transaction sur l'activité zombie par (setRetainInstance(true);)fragment conservé .

Malachiasz
la source
2

La raison de l'exception est la recréation du FragmentActivitypendant l'exécution du AsyncTasket l'accès au précédent, détruit FragmentActivitypar la onPostExecute()suite.

Le problème est d'obtenir une référence valide au nouveau FragmentActivity. Il n'y a pas de méthode pour cela ni getActivity()ni findById()ni quelque chose de similaire. Ce forum est plein de fils en fonction de ce problème (par exemple, recherche de "Activity context in onPostExecute"). Certains d'entre eux décrivent des solutions de contournement (jusqu'à présent, je n'en ai pas trouvé une bonne).

Peut-être que ce serait une meilleure solution d'utiliser un service pour mon objectif.

samo
la source
2

Il existe une solution alternative (PAS la meilleure solution) pour ce problème, mais cela fonctionne. En utilisant le drapeau, vous pouvez le gérer, comme ci-dessous

/**
 * 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;
    .....
}

Et vous pouvez vérifier cette booleanvaleur lors de la transaction de fragment.

private void fragmentReplace(Fragment fragment, String fragmentTag){
    if (!isOnSaveInstanceStateCalled) {
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.layout_container, fragment, fragmentTag)
                .commit();
    }
}
Pankaj Kumar
la source
1

Pour ce que ça vaut; J'ai eu cette erreur sur une application qui exécutait des services en arrière-plan. Sur l'un d'eux, une boîte de dialogue de temporisation a dû être présentée à l'utilisateur. Cette boîte de dialogue était à l'origine de cette erreur si l'application ne fonctionnait plus au premier plan.

Dans notre cas, afficher la boîte de dialogue n'était pas utile lorsque l'application était en arrière-plan, nous avons donc juste gardé une trace de cela (booléen marqué onPause en onResume), puis n'afficher la boîte de dialogue que lorsque l'application est réellement visible par l'utilisateur.

hcpl
la source
1

Solution 1: remplacez onSaveInstanceState()et supprimez le super appel qu'il contient.

@Override
public void onSaveInstanceState(Bundle outState) {
}

Solution 2: remplacez onSaveInstanceState()et supprimez votre fragment avant le super appel

@Override
public void onSaveInstanceState(Bundle outState) {
     // TODO: Add code to remove fragment here
     super.onSaveInstanceState(outState);
}
Lingkun Kong
la source
1

Ce problème se produit lorsqu'un processus tente de manipuler une activité qui onStop()a été appelée. Ce n'est pas nécessairement lié à la transaction fragmentée mais aussi à d'autres méthodes comme onBackPressed ().

En plus d'AsyncTask, une autre source de ce problème est le mauvais placement de l'abonnement au modèle de bus. Habituellement, l'abonnement Event Bus ou RxBus est enregistré pendant le onCreate d'Activité et désenregistré dans onDestroy. Si une nouvelle activité démarre et publie un événement intercepté par les abonnés de l'activité précédente, elle peut produire cette erreur. Si cela se produit, une solution consiste à déplacer l'inscription et la désinscription de l'abonnement vers onStart()et onStop().

inmyth
la source
1

Cela a résolu mon problème: Code Kotlin:

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

En quoi est commitAllowingStateLoss()-ce différent de commit()?

Selon la documentation:

Like commit()mais autorise l'exécution du commit après l'enregistrement de l'état d'une activité. https://developer.android.com/reference/android/app/FragmentTransaction#commitAllowingStateLoss ()

PS: vous pouvez afficher les boîtes de dialogue de fragments ou charger des fragments par cette méthode. Applicable pour les deux.

Wajid Ali
la source
0

Mon application a un fragment à charger en 3 secondes, mais lorsque le premier écran se prépare à s'afficher, j'appuie sur le bouton d'accueil et je recommence à l'exécuter, il affiche la même erreur, donc il modifie mon code et il s'est déroulé très bien:

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();
        }
    });

REMARQUE: ajoutez commitAllowingStateLoss () au lieu de commit ()

nobjta_9x_tq
la source
0

À partir de la version 24.0.0 de la bibliothèque de support, vous pouvez appeler la FragmentTransaction.commitNow()méthode qui valide cette transaction de manière synchrone au lieu d'appeler commit()suivie deexecutePendingTransactions()

Volodymyr
la source
0

IllegalStateException est rencontré si vous validez une transaction de fragment après que l'activité a perdu son état - L'activité n'est pas au premier plan. Cela se produit généralement lorsque vous essayez de valider un fragment dans AsyncTask ou après une demande réseau.

Pour éviter ce crash, il vous suffit de retarder toute transaction de fragment jusqu'à ce que l'état d'activité soit restauré. Voici comment cela se fait

Déclarez deux variables booléennes privées

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;

Maintenant, dans onPostResume () et onPause, nous définissons et annulons notre variable booléenne isTransactionSafe. L'idée est de marquer la transaction comme sûre uniquement lorsque l'activité est au premier plan afin qu'il n'y ait aucune chance de perte d'état.

/*
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();
    }
}

Ce que nous avons fait jusqu'à présent sauvera de IllegalStateException mais nos transactions seront perdues si elles sont effectuées après que l'activité passe en arrière-plan, un peu comme commitAllowStateloss (). Pour vous aider, nous avons la variable booléenne isTransactionPending

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;
 }
}

Cet article explique en détail pourquoi cette exception est rencontrée et compare différentes méthodes pour la résoudre. Hautement recommandé

IrshadKumail
la source
0

J'ai eu la même exception et j'ai essayé de nombreux extraits de code que j'ai trouvés ici dans cette discussion stackoverflow, mais aucun extrait n'a fonctionné pour moi.

Mais j'ai pu résoudre tous les problèmes, je partagerai avec vous les solutions:

  • Dans une première partie: j'ai essayé d'afficher un DialogFragment sur une activité mais à partir d'une autre classe java. Ensuite, en vérifiant l'attribut de cette instance, j'ai trouvé qu'il s'agissait d'une ancienne instance de l'activité, ce n'était pas l'activité en cours d'exécution. [Plus précisément, j'utilisais socket.io, et j'ai oublié de faire un socket.off ("exemple", exemple) ... donc il s'est attaché à une ancienne instance de l'activité. ]

  • Dans une deuxième partie: j'essayais de montrer un DialogFragment dans une activité quand j'y reviens avec une intention, mais quand j'ai vérifié mes journaux, j'ai vu que quand il a essayé de montrer le fragment, l'activité n'était toujours pas dans la méthode onStart , il a donc planté l'application car il n'a pas trouvé la classe Activity pour afficher le fragment dessus.

Quelques conseils: vérifiez avec certains attributs si vous n'utilisez pas une ancienne instance de votre activité avec laquelle vous essayez d'afficher votre fragment, ou vérifiez le cycle de vie de votre activité avant d'afficher votre fragment et assurez-vous que vous êtes dans onStart ou onResume avant de l'afficher .

J'espère que ces explications vous aideront.

Raj
la source