完美解决DialogFragment Can not perform this action after onSaveInstanceState 问题

完美解决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
我们来看一下表格

pre-Honeycombpost-Honeycomb
commit() before onPause()OKOK
commit() between onPause() and onStop()STATE LOSSOK
commit() after onStop()EXCEPTIONEXCEPTION
  1. 当Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB在onStop()之后 commit会产生异常
  2. 当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,问题就迎刃而解了

解决方案
  1. 重写activity/fragment 的生命周期方法,添加一个成员变量 isStateSaved
    在每次show和dismiss时 进行isStateSaved判断
    就像让你不再俱怕 Fragment State Loss一样。 问题:当activity/fragment不在前台时 dialog弹出时机有误,那么此次dialog弹出将会丢失。
    如:网络请求结果 或其他耗时操作的结果中 需要弹窗,在耗时操作期间,用户回到了home或前往其他activity时,这个弹窗事件将会丢失,需要再次处理
  2. 我们可以利用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);

}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值