Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can not perform this action after onSaveInstanceState WHEN commit

Tags:

android

I have got exception when ft.commit() and I don't know why.

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1448)
    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1466)
    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:634)
    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:613)
    at MainActivity.attachFragment(MainActivity.java:242)
    at MainActivity.attachFragment(MainActivity.java:225)
    at MainActivity.showHome(MainActivity.java:171)
    at MainActivity.onComplete(MainActivity.java:278)
    at MDownloadManager.onDownloadComplete(MDownloadManager.java:83)
    at DownloadRequestQueue$CallBackDelivery$2.run(DownloadRequestQueue.java:61)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:149)
    at android.app.ActivityThread.main(ActivityThread.java:5257)
    at java.lang.reflect.Method.invokeNative(Method.java)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
    at dalvik.system.NativeStart.main(NativeStart.java)

Here is my method where crash is comming.

FragmentManager     fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();

if(addToBackStack) {
    ft.addToBackStack(null);
    ft.add(R.id.frame_container, fragment, tag);
} else {
    ft.replace(R.id.frame_container, fragment, tag);
}

ft.commit();

Have you got any idea what is wrong?

I don't use onSaveInstanceState in my project.

like image 370
Unmerciful Avatar asked Dec 19 '22 23:12

Unmerciful


1 Answers

Complete solution at Solution for IllegalStateException

Overriding onSaveInstanceSate is a hack which doesnt necessarily work for all the scenerios. Also using commitAllowingStateLoss() is dangerous and could lead to UI irregularities.

We need to understand that IllegalStateException is encountered when we try to commit a fragment after the Activity state is lost - Activity is not in foreground (to understand more about Activity states read this). Therefore to avoid (resolve) this exception we just delay our fragment transaction until the state is restored

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;
 }
}
like image 191
Irshad Kumail Avatar answered May 23 '23 17:05

Irshad Kumail