解析 IllegalStateException--"Fragment already active"

本文探讨了在Android中使用Fragment时遇到的'Fragment already active'异常,指出该异常通常在尝试使用setArguments时发生。文章强调setArguments应在Fragment与Activity关联前、构造后立即调用,而通过XML布局加载的Fragment已经初始化,导致异常。解决方案是避免在XML中直接引用Fragment,改为在Activity中动态创建并设置参数。同时提到了saveInstanceState方法也可能抛出相同异常,使用时需注意。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家都知道Fragment的数据传递是用setArgument来完成的,但是,实际使用的时候需要注意一些问题,也就是调用setArgument的时机

比如下面的情况:
首先,在Activity的xml布局文件中引用fragment:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/answer_question_fragment_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/answer_question_fragmentyyy"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.emiage.bank.assistant.answer.AnswerQuestionFragment"
        tools:layout="@layout/fragment_answer_question" />
</RelativeLayout>

然后,我在Activity中试图通过setArgument来传递一些必要的参数:

@Override
protected void initViews() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    AnswerQuestionFragment fragment = (AnswerQuestionFragment) fragmentManager.findFragmentById(R.id.answer_question_fragment);
    String categoryName = getIntent().getStringExtra(ExtraName.CATEGORYNAME);
    Bundle bundle = new Bundle();
    bundle.putString(ExtraName.CATEGORYNAME, categoryName);
    fragment.setArguments(bundle);
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.add(R.id.answer_question_fragment_root, fragment);
    transaction.commitAllowingStateLoss();
}

这时候就会抛出上面的异常了:”Fragment already active”。异常是在setArgument的时候抛出的,我们看看setArgument方法:

    /**
     * Supply the construction arguments for this fragment.  This can only
     * be called before the fragment has been attached to its activity; that
     * is, you should call it immediately after constructing the fragment.  The
     * arguments supplied here will be retained across fragment destroy and
     * creation.
     */
    public void setArguments(Bundle args) {
        if (mIndex >= 0) {
            throw new IllegalStateException("Fragment already active");
        }
        mArguments = args;
    }

方法注释上说的很清楚了,setArgument方法要在“attach to activity”之前、构造fragment后立即调用,或许你还在疑惑,我就是在构造后立即调用的啊,怎么回事?其实真正的构造发生在解析xml文件时,通过findFragmentById得到的fragment已经是初始化过了,再调用setArgument就触发了上面的异常。

那么,如何纠正错误?很简单,把xml中的fragment标签去掉,只留下用来放fragment的容器。然后在Activity中commit:

    @Override
    protected void initViews() {
        FragmentManager fragmentManager = getSupportFragmentManager();
        AnswerQuestionFragment fragment = new AnswerQuestionFragment();
        String categoryName = getIntent().getStringExtra(ExtraName.CATEGORYNAME);
        Bundle bundle = new Bundle();
        bundle.putString(ExtraName.CATEGORYNAME, categoryName);
        fragment.setArguments(bundle);

        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.answer_question_fragment_root, fragment);
        transaction.commitAllowingStateLoss();
    }

所以,如果要给fragment传递参数,最好不要使用xml布局引用的方式,而是通过构造new出来,然后设置要传递的参数,再add-commit。

注意
我注意到setArgument方法下面还有一个方法也会同样的方式抛出上面的异常:

    /**
     * Set the initial saved state that this Fragment should restore itself
     * from when first being constructed, as returned by
     * {@link FragmentManager#saveFragmentInstanceState(Fragment)
     * FragmentManager.saveFragmentInstanceState}.
     *
     * @param state The state the fragment should be restored from.
     */
    public void setInitialSavedState(SavedState state) {
        if (mIndex >= 0) {
            throw new IllegalStateException("Fragment already active");
        }
        mSavedFragmentState = state != null && state.mState != null
                ? state.mState : null;
    }

用来保存fragment状态的方法,用来恢复最后保存状态,从代码上来看,这里触发异常的条件是跟setArgument方法一样的,所以,使用上也要注意同样情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值