现在的位置: 首页 > 综合 > 正文

onResume() called after onSaveInstanceState in FragmentActivity causing DialogFragmet.show() to cras

2014年10月11日 ⁄ 综合 ⁄ 共 13935字 ⁄ 字号 评论关闭

https://code.google.com/p/android/issues/detail?id=23096


Reported by makkus...@gmail.comDec
18, 2011
I am targeting platform 2.x and 4.0, therefore I set the build target to Android 2.1 and add the Support Package v4 (revision 6) to build path.

In my application I want to display a dialog no matter which activity the user is in, therefore I created a FragmentActivity (let's call it AlarmActivity) with launchMode = singleTask and taskAffinity different from the main application. Then AlarmActivity will create a DialogFragment to display a dialog. AlarmActivity is started at specific time using AlarmManager.

Moreover, I want my dialog to pop up even if the device is sleeping, so I have used the following code:

Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

My code worked perfectly in 2.x emulators and Nexus One running on 2.3.6, however my application crashed in the following situations, when I tested on Android 4.0 emulator and Galaxy Nexus:

case 1:
When the device is sleeping, AlarmActivity successfully wakes up the device, however the dialog cannot be displayed. The following error messages are found in LogCat:

12-18 23:57:01.005: E/AndroidRuntime(4267): FATAL EXCEPTION: main
12-18 23:57:01.005: E/AndroidRuntime(4267): java.lang.RuntimeException: Unable to resume activity {com.mydomain.myapp/com.mydomain.myapp.AlarmActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2443)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2471)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1172)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.os.Handler.dispatchMessage(Handler.java:99)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.os.Looper.loop(Looper.java:137)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.app.ActivityThread.main(ActivityThread.java:4340)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at java.lang.reflect.Method.invokeNative(Native Method)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at java.lang.reflect.Method.invoke(Method.java:511)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at dalvik.system.NativeStart.main(Native Method)
12-18 23:57:01.005: E/AndroidRuntime(4267): Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1299)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1310)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:541)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:525)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.support.v4.app.DialogFragment.show(DialogFragment.java:123)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at com.mydomain.myapp.AlarmActivity.showAlarm(AlarmActivity.java:357)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at com.mydomain.myapp.AlarmActivity.onResume(AlarmActivity.java:241)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1154)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.app.Activity.performResume(Activity.java:4539)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2433)
12-18 23:57:01.005: E/AndroidRuntime(4267): 	... 10 more

I added some debug code and found the activity life cycle callbacks of AlarmActivity are called in the following sequence:

AlarmActivity onCreate
AlarmActivity onStart
AlarmActivity onResume
AlarmActivity onsaveInstanceState
AlarmActivity onResume
(Then the application crashed)

case 2:
I have another Activity (let's call it MainActivity) which load some data from database and then display the data to user. I want to reload the data every time MainActivity become visible to user (e.g after user select the activity from the recent application list, or when another Activity is started by MainActivity, then user return to MainActivity by pressing the back button). A dialog is displayed to let user know some time consuming I/O is in progress, so that he/she will be patient. I used the following code in onResume():

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

  showLoadingAlarmListDialog();  // create and display dialog
  (new LoadAlarmListThread()).start();  // create thread to load data from database
}

When AlarmActivity pops up when user is at MainActivity, the dialog in AlarmActivity is displayed correctly. However after AlarmActivity is finished, MainActivity crashed when it try to display the dialog to tell user DB I/O is in progress.

The following error messages are found in LogCat:

12-19 00:45:18.745: E/AndroidRuntime(6211): FATAL EXCEPTION: main
12-19 00:45:18.745: E/AndroidRuntime(6211): java.lang.RuntimeException: Unable to resume activity {com.mydomain.myapp/com.mydomain.myapp.MainActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2443)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2471)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1172)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.os.Handler.dispatchMessage(Handler.java:99)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.os.Looper.loop(Looper.java:137)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.app.ActivityThread.main(ActivityThread.java:4340)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at java.lang.reflect.Method.invokeNative(Native Method)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at java.lang.reflect.Method.invoke(Method.java:511)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at dalvik.system.NativeStart.main(Native Method)
12-19 00:45:18.745: E/AndroidRuntime(6211): Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1299)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1310)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:541)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:525)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.support.v4.app.DialogFragment.show(DialogFragment.java:123)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at com.mydomain.myapp.MainActivity.showLoadingAlarmListDialog(MainActivity.java:324)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at com.mydomain.myapp.MainActivity.onResume(MainActivity.java:253)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1154)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.app.Activity.performResume(Activity.java:4539)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2433)
12-19 00:45:18.745: E/AndroidRuntime(6211): 	... 10 more

The following is the call sequence of activity life cycle callbacks:

MainActivity onCreate
MainActivity onStart
MainActivity onResume
(another activity is started from MainActivity)
MainActivity onSaveInstanceState
(user pressed back button)
MainActivity onStart
MainActivity onResume
(PendingIntent fired by AlarmManager)
MainActivity onSaveInstanceState
AlarmActivity onCreate
AlarmActivity onStart
AlarmActivity onResume
(AlarmActivity is finished)
MainActivity onResume
(MainActivity crashed)


Everything worked again if I do not use the support package, i.e. remove the support package from build path, set the build target to Android 4.0 and replace FragmentActivity with Activity and every getSupportFragmentManager() call with getFragmentManager(). However my apps will no worker be compatible with older version of Android.

Again, I have catpured the call sequence of activity life cycle callbacks:

For case 1:
AlarmActivity onCreate
AlarmActivity onStart
AlarmActivity onResume
AlarmActivity onSaveInstanceState
AlarmActivity onResume

For case 2:
MainActivity onCreate
MainActivity onStart
MainActivity onResume
(another activity is started from MainActivity)
MainActivity onSaveInstanceState
(user pressed back button)
MainActivity onStart
MainActivity onResume
(PendingIntent fired by AlarmManager)
MainActivity onSaveInstanceState
AlarmActivity onCreate
AlarmActivity onStart
(AlarmActivity is finished)
AlarmActivity onResume
MainActivity onResume

The call sequence is just the same as if the support package is used, so I think there may be some problem with the support package.
Dec 20, 2011

#1 danita....@gmail.com

I have the same sort of problem. When an activity is paused when running on honeycomb the mStateSaved flag is set when the instance state is saved. While this flag is set any attempt to show a dialog fragment will fail with the exception noted. It should be possible for an activity being resumed to show a dialog fragment in its onResume(), however if that activity is never stopped then the activity's onResume() will be invoked before the FragmentManager clears the mStateSaved flag (this happens when the fragments are resumed during the activity's onPostResume()) thus resulting in the exception. I suspect you could work around this by moving the code that shows the dialog into your activity's onPostResume() method. Just be sure to call through to super.onPostResume() before showing your dialog fragment.  
Dec 21, 2011

#2 makkus...@gmail.com

Thanks Danita, your suggestion worked perfectly for my application. However I think Google should change the behaviour of FragmentActivity as:

1. The behaviour of native ICS Activity is different from that of FragmentActivity, even if the FragmentActivity is being started in ICS.
2. The documentation of onPostResume() says "Applications will generally not implement this method", and onPostResume() is not documented in the activity lifecycle.

Thanks again for your help.
Jan 9, 2012

#3 flavioar...@gmail.com

I was having the same issue. Danita's suggestion made it work. =)
Jan 31, 2012

#4 jm...@android.com

I was running into this issue as well and talked with hackbod about this, naturally she had a solution.

The root of the problem is that for cross-platform compatibility reasons FragmentManager sometimes needs to delay restarting its fragments. Happily there is a simple and reliable workaround.

Attach a Fragment (probably a one just for this purpose and probably a non-UI one) to FragmentManager. When that Fragment receives onResume your app will known its okay to proceed with activities like the one here that is resulting in a crash.
Mar 7, 2012

#5 Murphy2...@gmail.com

Thanks for the reliable workaround!

Here's my empty fragment if someone needs, to use it:
- add this fragment in your activity,
- implement the listener in your activity.
The listener will be called each time the fragment pass onResume.

public class EmptyFragmentWithCallbackOnResume extends Fragment {
    OnFragmentAttachedListener mListener = null;

    @Override
    public void onAttach(SupportActivity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnFragmentAttachedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnFragmentAttachedListener");
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mListener != null) {
            mListener.OnFragmentAttached();
        }
    }

    public interface OnFragmentAttachedListener {
        public void OnFragmentAttached();
    }
}
Jul 12, 2012

#6 coolc...@gmail.com

I have been hunting to this post for months. Thank you, the onPostResume() and using some Boolean got my app back on track.
Aug 16, 2012

#7 coderroa...@gmail.com

When the fragment is being created from inside a handler's handleMessage function, I still see this behavior.  Is that intended?
Sep 4, 2012

#8 adolfo.b...@gmail.com

thanks for your workaround. sometimes working with fragments seems like walking on eggs
Nov 5, 2012

#9 jeffrey.blattman@gmail.com

here's an easy, though inelegant solution,

public abstract class CommitSafeDialogFragment extends DialogFragment {

    @Override
    public int show(FragmentTransaction transaction, String tag) {
        try {
            return super.show(transaction, tag);
        } catch (IllegalStateException e) {
            // ignore
        }
        return -1;
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            super.show(manager, tag);
        } catch (IllegalStateException e) {
            // ignore
        }
    }
Nov 19, 2012

#10 matej.sm...@gmail.com

overriding onPostResume() seems like the best workaround so far...
Dec 11, 2012

#11 joao.ro...@gmail.com

Is the onPostResume method override in the fragment or Activity?
Dec 11, 2012

#12 joao.ro...@gmail.com

I currently have a fragment that calls another dialogfragment with ok/no buttons. The ok button will call a progressdialogfragment. It seems that if i call the first dialog and then rotate the screen and press the ok button i get this error in the line where it calls the second fragment(progressdialogfragment). Any solution for this?
Jun 23, 2013

#13 jbq@android.com

(No comment was entered for this change.)
Labels: Component-Framework Version-4.0 
Aug 27, 2013

#14 arnoult....@gmail.com

For the first time i've seen this error. The problem is not triggered in onResume() but in onCreate() because I show dialogfragment only once in fact of previous activities so I can't do it in onPostResume(). Is there another solutino ?
Oct 4, 2013

#15 jndcummi...@gmail.com

hi,

i'm getting the error in onDestroy(). i'm displaying an AlertDialog to see if user wants to save db changes.

john
Nov 12, 2013

#16 ken.fear...@gmail.com

i am try this : tx.commitAllowingStateLoss(); and it is ok 
/*
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
		tx.replace(R.id.main, Fragment.instantiate(mystyle, str));
		tx.commitAllowingStateLoss(); 
*/
Nov 13, 2013

#17 msc.meis...@gmail.com

Using commitAllowingStateLoss can have unwanted siteeffects.

See http://stackoverflow.com/questions/17184653/commitallowingstateloss-in-fragment-activities for more information on that.

Therefore onPostResume should be the better solution (or empty fragment).


抱歉!评论已关闭.