完美解决DialogFragment Can not perform this action after onSaveInstanceState 问题
问题解析
参考一下掘金一位大佬的解释让你不再俱怕 Fragment State Loss
简单的来说,就是dialogFragment所依赖的Fragment/FragmentActivity,
在onSaveInstance之后,会调用dialogFragment的saveAllState,
使得fragmennt的成员变量mStateSaved=true。
在这之后 dialogFragment的FragmentTransaction执行了commit(),
而commit操作过程中会进行***checkStateLoss()***校验,
如果mStateSaved || mStopped为true,就会抛出异常
throw new IllegalStateException("Can not perform this action after onSaveInstanceState");
在什么时机执行commit操作合适?
我们再来参考一篇文章
Fragment Transactions & Activity State Loss
我们来看一下表格
- 当Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
在onStop()之后 commit会产生异常 - 当Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
在onPause()和onStop()之间会loss state,在onStop()之后 commit会产生异常
那么我们只需要 让dialogFragment的FragmentTransaction的commit的执行时机发生在onResume-onPause之间就OK,即 dialog的show和dismiss在onResume-onPause,问题就迎刃而解了
解决方案
- 重写activity/fragment 的生命周期方法,添加一个成员变量 isStateSaved
在每次show和dismiss时 进行isStateSaved判断
就像让你不再俱怕 Fragment State Loss一样。
问题:当activity/fragment不在前台时 dialog弹出时机有误,那么此次dialog弹出将会丢失。
如:网络请求结果 或其他耗时操作的结果中 需要弹窗,在耗时操作期间,用户回到了home或前往其他activity时,这个弹窗事件将会丢失,需要再次处理 - 我们可以利用LiveData的特性来解决这个问题
MVVM学习之LivaData
liveData的特点
1、感知对应Activity的生命周期,
2、只有生命周期处于onStart与onResume时,LiveData处于活动状态,才会把更新的数据通知至对应的Activity
3、当生命周期处于onStop或者onPause时,不回调数据更新,直至生命周期为onResume时,立即回调
4、当生命周期处于onDestory时,观察者会自动删除
实现
//ViewModel
public class BaseDialogViewModel extends AndroidViewModel {
private MutableLiveData<Boolean> isDialogShow;
public BaseDialogViewModel(@NonNull Application application) {
super(application);
isDialogShow=new MutableLiveData<>();
}
public MutableLiveData<Boolean> getDialogLiveData() {
return isDialogShow;
}
}
//简易版的dialogFragment
public abstract class BaseDialogFragment extends DialogFragment {
private BaseDialogViewModel baseDialogViewModel;
private FragmentManager fragmentManager;
public BaseDialogFragment(FragmentActivity fragmentActivity) {
baseDialogViewModel = new ViewModelProvider(fragmentActivity)
.get(BaseDialogViewModel.class);
fragmentManager = fragmentActivity.getSupportFragmentManager();
//初始化value
baseDialogViewModel.getDialogLiveData().setValue(null);
//添加观察
baseDialogViewModel.getDialogLiveData().observe(fragmentActivity, isShow -> {
LogUtil.log(" getDialogLiveData= " + isShow + " " + this.toString());
if (isShow == null) {
return;
}
if (isShow) {
show(fragmentManager, getFragmentTag());
} else {
//dismiss前 取消观察
baseDialogViewModel.getDialogLiveData().removeObservers(fragmentActivity);
dismiss();
}
});
}
public BaseDialogFragment(Fragment fragment) {
baseDialogViewModel = new ViewModelProvider(fragment)
.get(BaseDialogViewModel.class);
fragmentManager = fragment.getChildFragmentManager();
baseDialogViewModel.getDialogLiveData().setValue(null);
baseDialogViewModel.getDialogLiveData().observe(fragment, isShow -> {
LogUtil.log(" getDialogLiveData= " + isShow + " " + this.toString());
if (isShow == null) {
return;
}
if (isShow) {
show(fragmentManager, getFragmentTag());
} else {
baseDialogViewModel.getDialogLiveData().removeObservers(fragment);
dismiss();
}
});
}
@Override
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(getLayoutRes(), container, false);
bindView(v);
return v;
}
//showDialog和dismissDialog不在显示调用
//通过post liveData value
//利用liveData的特点,规避什么周期问题(即saveInstance时saveState变更)
//同时能够在条件不符时,保留数据,直至onResume发送数据
//或onDestory销毁
public void showDialog() {
LogUtil.log("showDialog");
if (baseDialogViewModel != null) {
baseDialogViewModel.getDialogLiveData().postValue(true);
} else {
LogUtil.log("初始化viewModel失败_show");
}
}
public void dismissDialog() {
LogUtil.log("dismissDialog");
if (baseDialogViewModel != null) {
baseDialogViewModel.getDialogLiveData().postValue(false);
} else {
LogUtil.log("初始化viewModel失败_dismiss");
}
}
/**
* @return layoutId
*/
@LayoutRes
public abstract int getLayoutRes();
public abstract void bindView(View v);
}