当Activity重新启动后调用'getSupportFragmentManager()'时出现'IllegalStateException: Activity has been destroyed'异常。

10

我有一个父Fragment Activity,其中包含一个子ViewPager。 子ViewPager 包含每个页面的Fragments。 我使用回调接口在这些子页面片段和顶部父Fragment Activity之间进行通信,例如。

public interface Callbacks {
    public void onItemSelected(Link link);
}
在父Fragment Activity中,我监听onItemSelected事件,例如。
@Override
public void onItemSelected(Link link) {
    Bundle argumentsFront = new Bundle();
    argumentsFront.putParcelable(FragmentComments.ARG_ITEM_ID, link);
    fragmentComments = new FragmentComments();
    fragmentComments.setArguments(argumentsFront);
    getSupportFragmentManager().beginTransaction().replace(R.id.post_container, fragmentComments).commitAllowingStateLoss();
}

当应用程序首次启动时,这很好用。

如果您旋转设备以更改方向,则Activity会重新启动。所有片段都将重新初始化,因为我使用了setRetainInstance(true);(在子ViewPager的页面片段中不调用setRetainInstance(true),因为它不受支持)。但是,如果我在子ViewPager的片段中单击列表项,则会出现此异常:

FATAL EXCEPTION: main
java.lang.IllegalStateException: Activity has been destroyed
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1342)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:578)

有人知道为什么会发生这种情况吗?

谢谢

5个回答

11
当您旋转设备时,Android 会保存、销毁并重新创建您的Activity及其FragmentsViewPager。由于ViewPager使用您的ActivityFragmentManager,因此它会为您保存和重用这些Fragments(而不是创建新的),所以它们将保留对原始Activity的旧引用(现在已被销毁),从而导致IllegalStateException错误。
在您的子Fragments中,请尝试像这样做:
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    Log.v(TAG, "onAttach");

    // Check if parent activity implements our callback interface
    if (activity != null) {
        try {
            mParentCallback = (Callbacks) activity;
        }
        catch (ClassCastException e) {
        }
    }
}

然后当选择发生时:

if(mParentCallback != null) {
    mParentCallback.onItemSelected(selectedLink);
}

由于 onAttach 是作为 Fragment 生命周期的一部分被调用的,所以您的 Fragments 将在旋转时更新其回调引用。


很遗憾,这个方法不起作用。我的 onAttach 方法已经和你提供的一样了。在屏幕方向改变时,我的 onAttach 方法没有再次被调用,因此我的回调没有被更新。父 Fragment 已经设置了 setRetainInstance(true),我猜这会阻止子 Fragment 被分离? - Milo
3
我找到了一个解决方法。因为我的嵌套片段如下:(FragmentActivity -> 片段1 -> 片段2(带有ViewPager)-> ViewPager子片段)我不得不将回调函数移到片段1(其中正在调用onAttach和onDetach)。然后我从ViewPager的子片段中的onItemSelected方法中向父片段(片段1)的回调函数进行静态调用。这似乎有点像黑客,但很有效。我在片段1中使用setRetainInstance(true),这样子片段也会保留状态。 - Milo
2
啊,我明白了,你的架构有点复杂。我以前也做过类似的事情,其中一个“Fragment”回调到其父“Fragment”。我在onAttach中使用了与上面相同的代码,但是我将父“Activity”转换为我的回调,而是将getParentFragment()转换为获取实现回调的父“Fragment”的引用。也许这可以帮助找到更清晰的解决方案,但无论如何,很高兴你找到了一个解决方法。 - Steven Byle
这确实听起来是一个更好的解决方案,我会尝试使用它而不是依赖静态调用。感谢您的帮助。 - Milo
1
非常感谢大家。起初我对为什么这是被接受的答案感到困惑,但第三和第四条评论帮了很多忙。这就是我所拥有的:Parentfragment -> viewpager with fragments - > child fragment。我在viewpager片段中向ParentFragment放置了一个监听器,在onAttach中使用getParentFragment,然后在parentfragment中放置了第二个监听器将数据传递给fragmentactivity。我想这就是Steven所说的。现在我可以正确地获取所有内容:) - Karl
显示剩余2条评论

0

我曾经遇到过嵌套片段的问题,但是stackoverflow上的解决方案都不适用于我。似乎支持库存在一个bug,即已关闭的片段仍然存储着对前一个活动的指针(因此getFragmentManager()返回null,因为它是在已销毁的活动上调用的),这就是为什么需要自己管理指针。最终我得到了以下解决方案:
1. 在第一级片段中,我保存了指向该方法的活动的指针

public void onAttach(Activity activity) {
        super.onAttach(activity);
        parentActivity = activity; // parentActivity is static variable
}

2. 在处理片段的活动中,我最终得到了这段代码:

private void launchFragment(Fragment fragment, Activity parent) {
            FragmentTransaction transaction;
            if(parent == null)
                transaction = mFragmentManager.beginTransaction();
            else    // for nested child fragments, workaround for Android parent pointer bug
                transaction = parent.getFragmentManager().beginTransaction();
            transaction.replace(R.id.container, fragment);
            transaction.addToBackStack(null);
            transaction.commit();
}

当您调用第二级(嵌套)片段时,只应传递第一级片段的parentActivity,因为似乎只有在将应用程序从前台带回后,嵌套片段才会出现此错误。


0
我曾经遇到过类似的问题,我认为这是因为片段被保留并保持对已销毁活动的引用,我的解决方案是在活动中保留对片段的引用,例如 Fragment myfragment = null。然后在 MyFragment 中使用以下代码:
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((TestActivity)activity).contentFragment = this;
}

能否再详细解释一下?我不是很理解。在父碎片活动中,您是否保存了每个子碎片的引用?我的子碎片是一个包含多个子碎片页面的ViewPager,因此如果可以避免,我不想保留所有这些页面的静态副本。谢谢。 - Milo

0

0
有一个类似的问题。如果ViewPager只有几个片段,那么在当前活动中存储对它们的引用。不要调用pagerAdapter的getItem()方法,因为它会创建一个新的片段,而且它没有附加到任何活动中,这就是为什么我们会看到“Activity has been destroyed”异常的原因。如果你不想保留片段引用,那么可以使用findViewWithTag()方法来获取Fragment对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接