美文网首页程序员Android开发Android技术知识
commit和commitAllowingStateLoss方法

commit和commitAllowingStateLoss方法

作者: 空手接白刀 | 来源:发表于2019-03-25 09:04 被阅读380次

    遇到的问题

     java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
            at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:2044)
            at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:2067)
            at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:680)
            at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:634)
    

    前两天使用EventBus在别的页面对MainAcitvity里的Fragment进行操作时,爆出来这个异常,网上找到的解决方案是把 FragmentTransaction 调用的commit()方法替换为 commitAllowingStateLoss()方法就可以解决了,至于为什么用这个方法,最近研究了一下源码,在这里分享一下。

    源码分析及验证

    public abstract class FragmentTransaction {
       ……
        public abstract int commit();
        public abstract int commitAllowingStateLoss();
    }
    

    首先查看FragmentTransaction,很简单的一个抽象类,不多说,下面寻找真正的实现类


    在这里插入图片描述

    通过上图的方式,右键类名点击 Find Usages,然后在Find视图窗口里找到继承自FragmentTransaction的实现类:BackStackRecord,下面来看一下这个类:

    final class BackStackRecord extends FragmentTransaction implements
            FragmentManager.BackStackEntry, Runnable {
        ……
        public int commit() {
            return commitInternal(false);
        }
    
        public int commitAllowingStateLoss() {
            return commitInternal(true);
        }
        int commitInternal(boolean allowStateLoss) {
            ……
            mManager.enqueueAction(this, allowStateLoss);
            ……
        }
        ……
    }
    

    省略掉无关逻辑,commit()和commitAllowingStateLoss()两个方法都调用了同一个方法commitInternal(),commitInternal()方法接收一个boolean的参数allowStateLoss(允许状态丢失),commitInternal()方法里调用了FragmentManager的enqueueAction()方法,并且把boolean参数传递了进去,然后继续看FragmentManager:

    public abstract class FragmentManager {
        ……
        boolean mStateSaved;
        ……
        public void enqueueAction(Runnable action, boolean allowStateLoss) {
            if (!allowStateLoss) {
                checkStateLoss();
            }
        }
    
        private void checkStateLoss() {
            if (mStateSaved) {
                throw new IllegalStateException(
                        "Can not perform this action after onSaveInstanceState");
            }
        }
        ……
        Parcelable saveAllState() {
            ……
            if (HONEYCOMB) {
                mStateSaved = true;
            }
            ……
        }
        ……
        public void noteStateNotSaved() {
            mStateSaved = false;
        }
    
        public void dispatchCreate() {
            mStateSaved = false;
            ……
        }
    
        public void dispatchActivityCreated() {
            mStateSaved = false;
            ……
        }
    
        public void dispatchStart() {
            mStateSaved = false;
            ……
        }
    
        public void dispatchResume() {
            mStateSaved = false;
            ……
        }
        ……
        public void dispatchStop() {
            mStateSaved = true;
            ……
        }
        ……
    }
    

    这里可以看出来,enqueueAction()根据allowStateLoss参数做了一个判断,如果allowStateLoss=false,也就是如果你调用的是commit()方法的话,那么就要做一个判断,如果mStateSaved为true,那么就要抛出异常,这个异常就是文章开头所说的那个异常。

    问题的关键来了,这个mStateSaved是如何变化的:
    上面的代码可以看出在saveAllState()方法和dispatchStop() 方法中,mStateSaved会变为true(HONEYCOMB意思是是否大于api11,不用管),这两个方法分别是在FragmentActivity的onSaveInstanceState()和onStop()中被调用,而onSaveInstanceState()的调用时机是在onPause()之后onStop()之前,这样可以总结出来当Activity的onSaveInstanceState()方法调用之后如果调用了该Activity的FragmentTransaction的commit方法,就会抛出异常(验证文章开头的结论)。

    相反的,其他几个将mStateSaved变为false的方法,根据方法名可以推断出是在FragmentActivity的生命周期的其他几个回调方法里运行的,可以推断出,当Activity重新回到栈顶显示之后,调用commit是没有问题的。

    总结

    当Activity中的Fragment发生了变化,FragmentManager会在特定的时间点保存所有Fragment的状态,方便Activity因为被回收之后重建时,重新设置Fragment,如果状态没有被保存,那么Activity就只能按照默认方式显示每个Fragment,显示效果可能跟app的预期不一样。
    如果项目中有特定的需求,比如需要在别的Activity中通过广播或者Eventbus的方式控制MainActivity的Fragment切换,那么就需要使用commitAllowingStateLoss()方法来避免异常。
    大家需要按照自己的需求来选择合适的方法使用。

    如果文章中有错误或者不严谨的地方,希望提出来,共同学习。

    相关文章

      网友评论

        本文标题:commit和commitAllowingStateLoss方法

        本文链接:https://www.haomeiwen.com/subject/cfimvqtx.html