记无心之下的FragmentManager和FragmentTransaction的坑

时间:2022-02-26 17:39:39

今天开始一个新的项目,一不小心遇到了个奇葩的坑,特做记录警示自己。

由于平时对很多地方都要使用到的变量,喜欢直接定义为全局变量并在onCreate或者onCreateView方法里面实例化,很不幸今天一得意就遇到坑了。

事情是这样的在Activity里面加载Fragment:

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    fragmentManager = getSupportFragmentManager();
    transaction = fragmentManager.beginTransaction();

    //默认加载第一个Fragment:HomeFragment
    doSelectedFragment(0);
}

然后再点击底部导航栏的时候,悲剧了,报异常如下:

java.lang.IllegalStateException: commit already called
        at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:664)
        at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:632)

其实引发这个错误的原因很简单:

如果在你一个类中已经使用一个成员变量transaction去调用了一次commit()方法,那么在其它外部类中就不能再使用一个成员变量transaction去再次调用commit()方法。我这里报异常就因为初始化的时候已经在Activity的onCreate方法里面调用过一次transaction的commit方法了。

真实乐极生悲啊。容我……..
记无心之下的FragmentManager和FragmentTransaction的坑

解决方法很简单,两种方案,一是把fragmentManager和transaction两个变量设定为局部变量,或者在局部方法中实例化他们。如下:

    private void doSelectedFragment(int position) {
    clearSelectedRecord();
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();

    hideFragment(transaction);
    setSelectedNavigation(position);
    addFragmentToFrameLayout(position, transaction);
}

上面代码是第一种方案,把fragmentManager和transaction两个变量设置为doSelectedFragment方法的局部变量。哪里要用到,就调用方法传递。另一种方案是:

FragmentManager fragmentManager;
FragmentTransaction transaction;
private void doSelectedFragment(int position) {
    clearSelectedRecord();
    fragmentManager = getSupportFragmentManager();
    transaction = fragmentManager.beginTransaction();

    hideFragment(transaction);
    setSelectedNavigation(position);
    addFragmentToFrameLayout(position, transaction);
}

上面代码是第二种方案,把fragmentManager和transaction两个变量设置为全局变量,但是,实例化则放在多次被调用的doSelectedFragment方法里面。

这两种方案都可以解决问题。但个人推荐第一种方案,稳定性更好,不易出问题。