commit和commitAllowingStateLoss方法的区别

作者 : 开心源码 本文共2948个字,预计阅读时间需要8分钟 发布时间: 2022-05-12 共199人阅读

遇到的问题

 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()方法来避免异常。
大家需要按照自己的需求来选择合适的方法使用。

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

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » commit和commitAllowingStateLoss方法的区别

发表回复