Android ShowDialog : IllegalStateException: Can not perform this action after onSaveInstanceState
来源:互联网 发布:知乎问题 编辑:程序博客网 时间:2024/05/22 04:47
项目中遇到报错如下:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1413) at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1431) at android.app.BackStackRecord.commitInternal(BackStackRecord.java:693) at android.app.BackStackRecord.commit(BackStackRecord.java:669) at android.app.DialogFragment.show(DialogFragment.java:230) at com.zw.coolweather.sample.MainActivity$2.onPageFinished(MainActivity.java:139) at com.android.webview.chromium.WebViewContentsClientAdapter.onPageFinished(WebViewContentsClientAdapter.java:515) at org.chromium.android_webview.AwContentsClientCallbackHelper$MyHandler.handleMessage(AwContentsClientCallbackHelper.java:188) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5441) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
报错的地方:定位于at android.app.DialogFragment.show(DialogFragment.java:230)
mainDialogFrag = new MainDialogFrag(); Bundle bundle = new Bundle(); bundle.putString("url", mainWebDataModel.url); bundle.putString("title", mainWebDataModel.title); bundle.putString("msgtype", mainWebDataModel.msgtype); mainDialogFrag.setArguments(bundle); mainDialogFrag.show(getFragmentManager(), "MainDialogFrag");
修改如下:
解决办法就是把commit()方法替换成 commitAllowingStateLoss()就行了。
mainDialogFrag = new MainDialogFrag(); Bundle bundle = new Bundle(); bundle.putString("url", mainWebDataModel.url); bundle.putString("title", mainWebDataModel.title); bundle.putString("msgtype", mainWebDataModel.msgtype); mainDialogFrag.setArguments(bundle); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.add(mainDialogFrag, "mainDialogFrag"); transaction.commitAllowingStateLoss();
看commitAllowingStateLoss源码:
/** * Like {@link #commit} but allows the commit to be executed after an * activity's state is saved. This is dangerous because the commit can * be lost if the activity needs to later be restored from its state, so * this should only be used for cases where it is okay for the UI state * to change unexpectedly on the user. */ public abstract int commitAllowingStateLoss();
看出错的show方法
/** * Display the dialog, adding the fragment to the given FragmentManager. This * is a convenience for explicitly creating a transaction, adding the * fragment to it with the given tag, and committing it. This does * <em>not</em> add the transaction to the back stack. When the fragment * is dismissed, a new transaction will be executed to remove it from * the activity. * @param manager The FragmentManager this fragment will be added to. * @param tag The tag for this fragment, as per * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. */ public void show(FragmentManager manager, String tag) { mDismissed = false; mShownByMe = true; FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commit(); }
在看commit方法
/** * Schedules a commit of this transaction. The commit does * not happen immediately; it will be scheduled as work on the main thread * to be done the next time that thread is ready. * * <p class="note">A transaction can only be committed with this method * prior to its containing activity saving its state. If the commit is * attempted after that point, an exception will be thrown. This is * because the state after the commit can be lost if the activity needs to * be restored from its state. See {@link #commitAllowingStateLoss()} for * situations where it may be okay to lose the commit.</p> * * @return Returns the identifier of this transaction's back stack entry, * if {@link #addToBackStack(String)} had been called. Otherwise, returns * a negative number. */ public abstract int commit();
在使用Fragment的过程中,常常会遇到在Activity的onSaveInstanceState方法调用之后,操作commit或者popBackStack而导致的crash.
因为在onSaveInstanceState方法之后的操作状态可能会丢失,因此Android framework默认会抛出一个异常.
对于commit方法来说,单纯避免这个异常很简单,使用commitAllowingStateLoss方法即可.但是popBackStack以及 popBackStackImmediate也都会检查state(checkStateLoss),特别需要注意的是Activity的 onBackPressed方法
如果onBackPressed在onSavedInstanceState之后调用,那么就会crash.
onBackPressed的调用时机:
- targetSdkVersion <= 5,在onKeyDown中调用
- targetSdkVersion > 5,在onKeyUp中调用
onSavedInstanceState的调用时机(如果调用的话):
- 一定在onStop之前
- 可能在onPause之前,也可能在onPause与onStop之间
需要注意的是: onSavedInstanceState方法不一定会调用,只有在Activity因为某些原因而被Framework销毁,并且之后还需要重新创建的情况,才需要调用(例如:旋屏,或者内存不足而回收返回栈中的某些Activity)
举例:
- Activity A在前台时,屏幕逐渐变暗直至锁屏,那么A的onSavedInstanceState会被调用
- Activity A start Activity B,Activity A的onSavedInstanceState会被调用
- Activity A因为返回键或者finish调用而返回到上一个界面,那么A的onSavedInstanceState不会被调用
因此,当onBackPressed在onSavedInstanceState方法之后调用,就一定会crash.解决方法主要有两种:
1、重写Activity的onSavedInstanceState()方法,并且注释掉super调用.
这种方法能避免crash,但是它会导致整个Activity的状态丢失.以DialogFragment为例,正常情况下,显示的 DialogFragment在旋屏Activity重新创建之后,不需要我们处理,Dialog会自动显示出来(参见 DialogFragment.onStart()),但是注释掉Activity的onSavedInstanceState()方法之 后,Fragment状态丢失,Activity重新创建之后,Dialog也就不会再显示出来了.
2、更好且通用的做法:在调用commit,popBackStack以及onBackPressed方法之前,判断 onSavedInstanceState()方法是否已经执行,并且onResume方法还没有执行,如果不是,那么直接操作,否则加入到 pending队列,等待onResumeFragments或者onPostResume之后再执行.
注意:不要在onResume中操作,因为这时候FragmentManager中的mStateSaved依然可能是true.(如果执行顺序是 onSavedInstanceState()->onPause()->onResume() 或者 onPause()->onSavedInstanceState()->onResume());
public void endPaintingPager(int index) { if (mFirstLevel == PAINTING_PAGER) { mFirstLevel = PAINTER_START; if (!mIsStateSaved) { getSupportFragmentManager().popBackStack(); } else { mPopBackStackRunnable = new Runnable() { @Override public void run() { getSupportFragmentManager().popBackStack(); } }; } } }
@Override protected void onPostResume() { super.onPostResume(); if (mPopBackStackRunnable != null) { mPopBackStackRunnable.run(); } }
onSaveInstanceState
在activity的一个生命周期中,onSaveInstanceState()并非一定调用。
正如官网对该方法的解释所说:“This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. ” 在“可能被系统杀死”之前调用。suo的很准确啊,先明白一点:如果一个activityA不可能在后台被系统主动kill掉。
onSaveInstanceState方法是Activity的成员方法,主要用于在Activity销毁时保存Activity相关的对象信息,而其执行的时机不是我们主动调用的,而是Android系统的framework帮忙调用的,而其调用的时机在onPause方法之后执行在onStop方法之前执行。
参考:Android源码解析(二十四)–>onSaveInstanceState执行时机
1、onSaveInstanceState方法是Activity的生命周期方法,主要用于在Activity销毁时保存一些信息。
2、当Activity只执行onPause方法时(Activity a打开一个透明Activity b)这时候如果App设置的targetVersion大于android3.0则不会执行onSaveInstanceState方法。
3、当Activity执行onStop方法时,通过分析源码我们知道调用onSaveInstanceState的方法直接传值为true,所以都会执行onSaveInstanceState方法。
参考:onSaveInstanceState(Bundle outState)的调用时机
只要Activity不finish,Activity进入后台(比如Home键,跳转到其他的Activity),则其就会调用onSaveInstanceState(Bundle outState)方法,而且这个方法是在onPause、onStop方法之间进行调用的。
如果Activity是执行了finish方法,才进入的后台,则不调用这个onSaveInstanceState(Bundle outState),而且下次再进入时,也不会使用这个保存的数据。
在系统杀掉Activity所在的进程时,onSaveInstanceState(Bundle outState)方法根本就没有调用过。
总结:系统在杀进程时,不可能有时间去执行多余的代码,也只有这种方法,才能保存Activity里的最新数据,所以onSaveInstanceState(Bundle outState)会被执行多次,并不是只有一次。
参考:activity中onSaveInstanceState方法调用时机详解
onResume() 不会被杀。前台应用,系统是不会主动kill的。
onPause() HONEYCOMB(android3.0)之前,可能被杀;3.0之后不会被杀。
onStop() 可能被杀。
明确activity可能在生命周期中被杀的方法之后,根据上边说明便可知:
android3.0之前:
onResume() -- [optional]onSaveInstanceState() -- onPause(),
即调用onPause()之前,可能调用onSaveInstanceState()
android3.0之后:
onResume() -- onPause() -- [optional]onSaveInstanceState() -- onStop(),
即调用onStop()之前,可能调用onSaveInstanceState()
后台的activity被系统主动kill掉才会在它的onPause、onStop之间调用,此时还没被finish(onDestroy)。
如果一个activityA不可能在后台被系统主动kill掉,那么就不会调用该方法。
比如一下逻辑:
activityA.startActivity(activityB)
activityA.finish()
A启动了B,但是A自己把自己finish了,也就是说系统不可能主动kill activityA了,因此虽然A的onPause()、onStop()被调用,onSaveInstanceState()方法也是不会调用到的。
那么同理,默认情况下在一个activity中,返回退出也是不会调用onSaveInstanceState()的。
参考:
[DialogFragment - Can not perform this action after onSaveInstanceState
](https://stackoverflow.com/questions/21634767/dialogfragment-can-not-perform-this-action-after-onsaveinstancestate)
Android ShowDialog : IllegalStateException: Can not perform this action after onSaveInstanceState
解决java.lang.IllegalStateException: Can not perform this action after onSaveInstance
- Android ShowDialog : IllegalStateException: Can not perform this action after onSaveInstanceState
- Android 异常 IllegalStateException: Can not perform this action after onSaveInstanceState
- Android - IllegalStateException: Can not perform this action after onSaveInstanceState
- Android 错误:IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决Fragment IllegalStateException: Can not perform this action after onSaveInstanceState
- IllegalStateException: Can not perform this action after onSaveInstanceState解决办法
- java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- Fragment IllegalStateException: Can not perform this action after onSaveInstanceState
- 解决IllegalStateException: Can not perform this action after onSaveInstanceState
- IllegalStateException: Can not perform this action after onSaveInstanceState
- 1023. Have Fun with Numbers (20)
- 金蝶K3WISE常用数据表(整理)
- 13.css精灵图
- Git管理多个SSH密钥,Git多帐号配置
- 点击跳转页面后加上的效果
- Android ShowDialog : IllegalStateException: Can not perform this action after onSaveInstanceState
- 拾趣小记,input 限制输入数字
- 7-6 统计大写辅音字母
- hdu6180Schedule-(贪心)
- NYOJ117
- Android快速实现横竖屏切换布局自动更改
- HTTP 协议详解
- 7-5 找鞍点
- UVA-129 困难的串