Dialogs and activity lifecycle

Here's a typical situation with Android:

  1. Make an HTTP request specifying a callback.
  2. When the callback is executed, show a dialog requesting user input.
  3. Continue with your app's flow based on that input.

Unfortunately, your activity may have been dismissed between steps 1 and 2. So when you actually try to show the dialog, you'll get a nice exception:

 java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
 FragmentManager.java:1343 in "android.support.v4.app.FragmentManagerImpl.checkStateLoss"
FragmentManager.java:1361 in "android.support.v4.app.FragmentManagerImpl.enqueueAction"  
BackStackRecord.java:595 in "android.support.v4.app.BackStackRecord.commitInternal"  
BackStackRecord.java:574 in "android.support.v4.app.BackStackRecord.commit"  
DialogFragment.java:127 in "android.support.v4.app.DialogFragment.show"  

The context of the request is still valid, so the callback will be executed anyways. We should be using the DialogFragment's utilities to manage the dialog's lifecycle, but unfortunately that's available only for API level >= 11.

Another option is to check if the activity is paused and only show the dialog if it's not. A modular way to do that is to specify a base activity and make all your activities inherit from that one:

public class BaseFragmentActivity extends SherlockFragmentActivity {

    private boolean mIsPaused = false;

    protected boolean isPaused() {
        return mIsPaused;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mIsPaused = false;
    }
    @Override
    protected void onPause() {
        super.onPause();
        mIsPaused = true;
    }
    @Override
    protected void onResume() {
        super.onResume();
        mIsPaused = false;
    }
    @Override
    protected void onStart() {
        super.onStart();
        mIsPaused = false;
    }

}

Then in our activity we can check if it is paused. If not, then show the dialog:

public class MyActivity extends BaseFragmentActivity {

    ...

    protected void someCallback() {
        if (!this.isPaused()) {
            dialog.show();
        }
    }

    ...
}

Trying to crash the activity

Now we would like to test if this is working. An easy way to pause your activity can be done calling a new activity or app with adb. For example, to open the browser you can run the following command from the terminal:

adb shell am start -a android.intent.action.VIEW -d 'http://www.google.com'  

References and inspiration

http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
http://stackoverflow.com/questions/3512198/need-command-line-to-start-web-browser-using-adb
http://stackoverflow.com/questions/10685460/how-to-determine-in-onpostexecute-that-activity-is-not-paused-now-or-destroyed

comments powered by Disqus