堆栈信息

版本分布

机型

问题分析
0,Activity->onBackPressed
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
1,FragmentManager->popBackStackImmediate
/**
* Like {@link #popBackStack()}, but performs the operation immediately
* inside of the call. This is like calling {@link #executePendingTransactions()}
* afterwards.
* @return Returns true if there was something popped, else false.
*/
public abstract boolean popBackStackImmediate();
2,FragmentManagerImpl->popBackStackImmediate
@Override
public boolean popBackStackImmediate() {
checkStateLoss();
executePendingTransactions();
return popBackStackState(mHost.getHandler(), null, -1, 0);
}
3,FragmentManagerImpl->checkStateLoss
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
NOTE:崩溃的原因就是mStateSaved=true抛出了IllegalStateException,需要找到mStateSaved在哪些地方被赋值为true
4,FragmentManagerImpl->saveAllState
Parcelable saveAllState() {
// Make sure all pending operations have now been executed to get
// our state update-to-date.
execPendingActions();
mStateSaved = true;//赋值
.....
}
mSateSaved只会在saveAllState()中被重新赋值为true,继续跟进saveAllState()在哪些场景下会被调用
5, Activity->onSaveInstanceState
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
Parcelable p = mFragments.saveAllState();//在这被调用
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
可以得出初步的结论是当Activity调用onSaveInstanceState,间接改变了FragmentManagerImpl中mStateSaved的值,onBackPressed()被调用时会导致FragmentManagerImpl检查mStateSaved值,为true就抛出异常导致崩溃
6,关于onSaveInstanceState
这一篇介绍了onSaveInstanceState的调用时机,所以分析可能的操作步骤是,Activity->Home(或者电源键)->回Activity->onBackPressed。
当用户回道回到Activity时,生命周期必然会经过onResume,mStateSaved状态会重置成false,onBackPressed就不会引起崩溃,这就是矛盾。
解决方案
反射FragmentActivity中的FragmentController对象,调用noteStateNotSaved方法,修改mStateSaved的值为false。
这样的做的原因是:用户点击back键的目的是退出界面或者dismiss当前界面的dialog(或者其他浮层),对于前者不用考虑状态保存的问题,对于后者Activity的生命周期会重新onResume,不会影响Fragment状态保存。
@Override
public void onBackPressed() {
fixBug();
super.onBackPressed();
}
private void fixBug() {
try {
Class clz = BaseActivity.class;
while (clz != FragmentActivity.class) {
clz = clz.getSuperclass();
}
Field field = clz.getDeclaredField("mFragments");
field.setAccessible(true);
Object object = field.get(this);//获取mFragments对象
Class<?> fieldClazz = object.getClass();
Method method = fieldClazz.getMethod("noteStateNotSaved");
method.setAccessible(true);
method.invoke(object);
} catch (Exception e) {
e.printStackTrace();
}
}
网友评论