使用ViewPager和选项卡时出现“Java.lang.IllegalStateException Activity has been destroyed”错误。

112
我有一个应用程序,使用ActionBarSherlock的选项卡模式。我有5个选项卡,每个选项卡的内容都使用片段处理。但是对于第二个选项卡,我有一个包含ViewPager元素的xml文件,其中又有一些片段页面。当我最初启动应用程序时,我可以轻松地在选项卡之间切换,但当我第二次点击选项卡2时,就会出现上述错误。主要活动如下:
public class MainActivity extends SherlockFragmentActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActionBar actionBar = getSupportActionBar();

        ActionBar.Tab tab1 = actionBar.newTab().setText("Tab1");
        ActionBar.Tab tab3 = actionBar.newTab().setText("Tab3");
        ActionBar.Tab tab2 = actionBar.newTab().setText("Tab2");
        ActionBar.Tab tab4 = actionBar.newTab().setText("Tab4");
        ActionBar.Tab tab5 = actionBar.newTab().setText("Tab5");

        Fragment fragment1 = new Tab1();
        Fragment fragment3 = new Tab3();
        Fragment fragment2 = new Tab2();
        Fragment fragment5 = new Tab5();
        Fragment fragment4 = new Tab4();

        tab1.setTabListener(new MyTabListener(fragment1));
        tab3.setTabListener(new MyTabListener(fragment3));
        tab2.setTabListener(new MyTabListener(fragment2));
        tab5.setTabListener(new MyTabListener(fragment5));
        tab4.setTabListener(new MyTabListener(fragment4));

        actionBar.addTab(tab1);
        actionBar.addTab(tab2);
        actionBar.addTab(tab3);
        actionBar.addTab(tab4);
        actionBar.addTab(tab5); 

        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    }

    class MyTabListener implements ActionBar.TabListener
    {
        Fragment fragment;

        public MyTabListener(Fragment fragment)
        {
            this.fragment = fragment;
        }

        @Override
        public void onTabSelected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {
            ft.replace(R.id.fragment_container,fragment);
        }

        @Override
        public void onTabUnselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }

        @Override
        public void onTabReselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }
    }
}

没有ViewPager的片段类如下所示:

public class Tab1 extends Fragment 
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.activity_tab1, container, false);
    }
}

ViewPager中的片段类如下:
public class Tab2 extends Fragment 
{
    ViewPager mViewPager;
    private MyFragmentPagerAdapter mMyFragmentPagerAdapter;  
    private static int NUMBER_OF_PAGES = 5;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        View view =  inflater.inflate(R.layout.activity_tab2, container, false); 
        return view;
    }

    @Override
    public void onViewCreated(View view,Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
        mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());  
        mViewPager.setAdapter(mMyFragmentPagerAdapter);  
    }

    private static class MyFragmentPagerAdapter extends FragmentPagerAdapter 
    {    
        public MyFragmentPagerAdapter(FragmentManager fm) 
        {  
             super(fm);  
        }  

        @Override  
        public Fragment getItem(int index) 
        {  
             return PageFragment.newInstance("My Message " + index);
        }  

        @Override  
        public int getCount() 
        {  
             return NUMBER_OF_PAGES;  
        }  
   }
}

根据我在不同地方阅读到的内容(如有错误,请纠正),这是因为片段管理器在第二次尝试重用来自已不存在的活动的片段,从而导致错误。但我不确定为什么会在这里发生,因为我没有使用片段活动。根据 logcat 的记录,错误出现在 Tab2 类的 onViewCreated 方法中,在 mViewPager.setAdapter(mMyFragmentPagerAdapter) 一行。非常感谢您的帮助...谢谢。
03-04 12:01:05.468: E/AndroidRuntime(2474): FATAL EXCEPTION: main
03-04 12:01:05.468: E/AndroidRuntime(2474): java.lang.IllegalStateException: Activity has been destroyed
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1342)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:578)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:139)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:1011)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:880)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:433)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.example.tabs.Tab2.onViewCreated(Tab2.java:31)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:925)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.handleCallback(Handler.java:587)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.dispatchMessage(Handler.java:92)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Looper.loop(Looper.java:123)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.app.ActivityThread.main(ActivityThread.java:3687)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invokeNative(Native Method)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invoke(Method.java:507)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at dalvik.system.NativeStart.main(Native Method)

所以我认为我可能已经找到了问题所在。当通过Eclipse调试器查看变量mMyFragmentPagerAdapter(类Tab2)时,我发现它有一个FragmentManager变量,当第一次单击Tab2时,它有一个名为mActivity的字段,该字段指向MainActivity。但是在从tab2切换到其他选项卡并再次查看mActivity时,它的值为null,这或许可以解释为什么会出现“Activity已被销毁”的错误。 - Yulric Sequeira
14个回答

286

这似乎是新添加的嵌套片段支持中的一个错误。基本上,当子FragmentManager从活动中分离时,它会产生损坏的内部状态。一个短期解决办法是在每个你调用getChildFragmentManager()FragmentonDetach()中添加以下内容,可以修复此问题:

@Override
public void onDetach() {
    super.onDetach();

    try {
        Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
        childFragmentManager.setAccessible(true);
        childFragmentManager.set(this, null);

    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

21
如果您查看Fragment的实现,您会发现当其进入分离状态时,它会重置其内部状态。然而,它不会重置mChildFragmentManager(这是当前版本支持库中的一个错误)。这导致在重新连接Fragment时无法重新附加子片段管理器,从而引发您看到的异常。 - Marcus Forsell Stahre
14
你一定在开玩笑。很高兴你发了这个帖子,但是天哪。 - secureboot
8
这个漏洞正在Android开源问题跟踪器中被追踪:https://code.google.com/p/android/issues/detail?id=42601。 - Kristopher Johnson
3
盐量要适量,但使用support-v4版本或android.app版本都会失败。因此这个bug不仅出现在support库中。 - gbero
6
似乎是在支持库版本24.0.0中修复了这个问题。'android.support.v4.app.Fragment'类的'method 'performDetach()'会将'mChildFragmentManager'设为null。 - Emerson Dallagnol
显示剩余18条评论

13

我遇到了完全相同的问题。唯一的解决方法是,每次更改标签时,用一个新实例替换片段。

ft.replace(R.id.fragment_container, Fragment.instantiate(PlayerMainActivity.this, fragment.getClass().getName()));

不是一个真正的解决方案,但我还没有找到一种重用之前片段实例的方法...


1
哇,非常感谢……那修复了问题。能否解释一下为什么这样可以解决问题吗?我一直在努力使用FragmentManager来修复错误。 - Yulric Sequeira
1
你分析过内存占用吗?因为这个解决方法可能会增加它,至少我怀疑。 - nmxprime
不知道为什么它能工作...不知道它是如何工作的,但它确实可以工作,哈哈。 - user3600801

7
我在调用方法末尾时遇到了与 super.onCreate() 相同的问题。原因是:在 FragmentActivity 的 onCreate() 方法中会调用 attachActivity()。当重写 onCreate() 方法并创建选项卡等控件时,选项卡管理器将尝试在未将活动附加到 FragmentManager 的情况下切换到片段。

简单解决方案:将对 super.onCreate() 的调用移至函数体开头。

一般来说,似乎有很多原因会导致这种问题发生。这只是其中之一...

Matthias


非常感谢,你救了我一天! 我也遇到了同样的问题,并在一周后仍未解决它 ;( 。 所以我很高兴你写了这个答案!!! - JonasPTFL

4

我想补充一下,我的问题出现在一个活动中,在该活动中我尝试在调用super.onCreate()之前的onCreate函数中进行FragmentTransaction。我只需要将super.onCreate()移到函数顶部就可以正常工作。


2

我遇到了同样的问题,后来发现,在FragmentActivity的onCreate()方法中,我错过了调用super.onCreate(savedInstanceState);的代码。


2
我遇到了这个错误,因为我在使用LocalBroadcastManager时做了以下操作:
unregisterReceiver(intentReloadFragmentReceiver);

改为:

LocalBroadcastManager.getInstance(this).unregisterReceiver(intentReloadFragmentReceiver);

1
这并没有解决我的问题。但是谢谢,它帮助我纠正了在使用BroadcastReceivers时的错误。 - nmxprime
如果您的Activity上下文为空,那么'this'将无效。 - IgorGanapolsky
当Activities上下文可以为空吗?我认为从不。 - Malachiasz

1

我知道这是一个旧帖子,但建议的答案在我的环境下没有起作用。我想把这个留在这里,以防有人会觉得有用。

我的做法是:

@Override
public void onResume() {
    super.onResume();
    // add all fragments
    FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
    for(Fragment fragment : fragmentPages){
        String tag = fragment.getClass().getSimpleName();
        fragmentTransaction.add(R.id.contentPanel, fragment, tag);
        if(fragmentPages.indexOf(fragment) != currentPosition){
            fragmentTransaction.hide(fragment);
        } else {
            lastTag = tag;
        }
    }
    fragmentTransaction.commit();
}

然后在:
@Override
public void onPause() {
    super.onPause();
    // remove all attached fragments
    for(Fragment fragment: fragmentPages){
        getChildFragmentManager().beginTransaction().remove(fragment).commit();
    }
}

1
当尝试在片段完全初始化之前访问子FragmentManager(即附加到Activity或至少调用了onCreateView())时,我遇到了完全相同的错误。否则,FragmentManager将使用null Activity进行初始化,从而导致上述异常。

1
在onPause中将包含子片段的片段强制设置为NULL,可以解决我的问题。
fragment = null;

0

我遇到了这个问题,后来发现是因为我在ActivityonCreate中两次调用了setContentView(int id)


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