转发请标明来源:http://blog.csdn.net/rflyee/article/details/74723891
上篇博客(这里这里)从源码层面分析了Can not perform this action after onSaveInstanceState异常产生的原因及流程,
接下来分析下该崩溃的设计原因以及如何避。
大家都知道系统在内存吃紧时会按规则优先kill掉部分非前台activity,为了保证用户体验,系统在kill掉某个activity之前会先调用onSaveInstanceState,将当前window的一些重要状态以bundle的形式持久化,当用户回到该activity时,再通过onCreate、onRestoreInstanceState等方法恢复之前的状态,给用户一种该界面从来没有被kill的假象,从而提升用户体验。
那么问题来了,在系统onSaveXXX保存状态之后,一直到onRestoreXXX恢复状态之前,这段时间里程序如果再操作window,比如操作FragmentTransaction#commit()、操作FragmentDialog#show()/dismiss(),这些操作肯定没有保存在Bundle中,也就是下次onRestoreXXX恢复状态时是没有这些操作的,也就是所谓的lose state丢失状态,这样可能给用户一种前后不一致的用户体验。所以安卓团队为了“提升用户体验”,saveState之后再操作window就抛出一个IllegalStateException
异常,提醒(逼迫)开发者注意喽。
pre-Honeycomb |
post-Honeycomb |
|
---|---|---|
commit() before onPause() |
OK |
OK |
commit() between onPause() and onStop() |
STATE LOSS |
OK |
commit() after onStop() |
EXCEPTION |
EXCEPTION |
FragmentActivity#onResumeFragments()
或者 Activity#onPostResume()
里使用。
RragmentAvtivity#onResume不可以,可以用FragmentAvtivity#
onResumeFragments 因为调用onResume时不能保证已经restore之前的state了(假设之前被kill,并saveState),参见 documentation。
commitAllowingStateLoss
代替commit()。前者允许状态丢失,其他完全一样。也即控制着源码中
allowStateLoss
(5)有blog说覆写onSaveInstanceState()但是不调用
super.onSaveInstanceState(outState);也能避免崩溃,知道原因后的你肯定灰常不建议这么做了(不然onSaveInstanceState还有啥用)。(个人测试,奇酷手机青春版,Android5.1,此方法无效)
@Overrideprotectedvoid onSaveInstanceState(Bundle outState){//No call for super(). Bug on API Level > 11.}
试试这样解决,在BaseActivity中添加:
@Override
protected void onStart() {
super.onStart();
// super.onStart();中将mStateSaved置为false
mStateEnable = true;
}
@Override
protected void onResume() {
// onPause之后便可能调用onSaveInstanceState,因此onresume中也需要置true
mStateEnable = true;
super.onResume();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState();中将mStateSaved置为true
mStateEnable = false;
super.onSaveInstanceState(outState);
}
@Override
protected void onStop() {
// super.onStop();中将mStateSaved置为true
mStateEnable = false;
super.onStop();
}
/**
*
* activity状态是否处于可修改周期内,避免状态丢失的错误
* @return
*/
public boolean isStateEnable() {
return mStateEnable;
}
之后调用修改状态的方法如fragmentDialog的show()、dismiss()方法等,则先判断状态是否可用。
if(isStateEnable()) {
dialog.show();
}