Search code examples
androidandroid-asynctaskillegalstateexception

IllegalStateException on AsyncTask


I'm getting a following error when trying to replace a fragment upon receiving a response from AsyncTask:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

The thing is, I get this error randomly upon restarting my app through Android Studio. In a simplified version my activity contains 4 key methods (onCreate, taskCompleted, parseJSON and fragmentReplace), that determine which fragment should the user see at the start:

private AsyncTask mMyTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mMyTask = new AsyncTask(this, this);
    mMyTask.executeTaskCall("check_user");
}


@Override
public void taskCompleted(String results) {
    try {
        JSONObject jsonBody = new JSONObject(results);
        parseJSON(jsonBody);
    }
    catch (JSONException je){
    }
}

private void parseJSON(JSONObject jsonBody) throws JSONException {
    boolean userActive = jsonBody.getBoolean("result");

    if (userActive){
        fragmentReplace(new FirstFragment(), "FirstFragment");
    }
    else {
        fragmentReplace(new SecondFragment(), "SecondFragment");
    }
}

public void fragmentReplace(Fragment fragment, String fragmentTag){
    getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.layout_container, fragment, fragmentTag)
            .commit();
}

What is the reason of this exception happening so random?


Solution

  • You should read WeakReference solution (or may be other also) at java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState.


    There is one alternate solution for this problem. 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();
        }
    }
    

    Update for API 26.1+ (contributed by Stephen M)

    Fragment.isStateSaved() has been added since 26.1.0, which can also be used for same purpose.