将选项卡与ViewPager一起使用时出现错误“ Java.lang.IllegalStateException活动已被破坏”


110

我有一个应用程序,其中包含在选项卡模式下使用ActionBarSherlock的应用程序。我有5个选项卡,每个选项卡的内容都是使用片段处理的。但是对于tab2,我有一个片段,该片段的xml文件包含一个ViewPager元素,而该元素又具有一些片段页面。最初启动应用程序时,我可以在选项卡之间进行切换,但是第二次按tab2时,出现上述错误。主要活动如下:

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类中,在显示mViewPager.setAdapter(mMyFragmentPagerAdapter)的行上的onViewCreated方法中。任何帮助都将不胜感激...谢谢。

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 2013年

Answers:


284

这似乎是新添加的对嵌套片段的支持中的错误。基本上,当孩子FragmentManager从活动中分离出来时,其最终的内部状态会破裂。为我解决此问题的一种短期解决方法是,在您调用onDetach()的每项操作中添加以下内容:FragmentgetChildFragmentManager()

@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 2013年

8
此错误已在Android开放源问题跟踪器中跟踪:code.google.com/p/android/issues/detail?id=42601
Kristopher Johnson

3
有点麻烦,但是使用support-v4版本或android.app版本却失败了。因此,该错误不仅发生在支持库中
gbero 2014年

6
在支持库版本24.0.0中似乎已修复。'android.support.v4.app.Fragment'的方法'performDetach()'执行'mChildFragmentManager = null;'。
Emerson Dallagnol

13

我遇到了完全一样的问题。我发现的唯一解决方法是,每次更改选项卡时,都用一个新实例替换这些片段。

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

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


1
哇,非常感谢...解决了这个问题。能否给出解释为什么起作用的原因是我正在试图使用FragmentManager修复错误。
Yulric Sequeira

1
您是否分析了内存占用量?因为这种解决方法可能会增加它,至少我怀疑
nmxprime 2014年

不知道它为什么工作..不知道它如何工作,但是它会大声笑

7

super.onCreate()在方法末尾调用时遇到了相同的问题。原因:attachActivity()在FragmentActivity的onCreate()中调用。覆盖onCreate()并(例如)创建选项卡时,选项卡管理器将尝试切换到片段,同时不将活动附加到FragmentManager。

简单的解决方案:将调用移到super.onCreate()函数主体的头部。

通常,似乎有很多原因可能导致此问题发生。这只是另一个...

马蒂亚斯


非常感谢您,您拯救了我的一天!我遇到了同样的问题,但一周后仍无法解决;(。非常高兴您写下了这个答案!!!
JonasPTFL

4

还要补充一点,我的问题出在一个活动中,我尝试FragmentTransaction在调用onCreate之前进行了创建super.onCreate()。我只是移到super.onCreate()功能顶部,并且工作正常。


2

我遇到此错误是因为我使用的是LocalBroadcastManager,但是我做到了:

unregisterReceiver(intentReloadFragmentReceiver);

代替:

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

1
这不能解决我的问题。但是,感谢它帮助我纠正了使用BroadcastReceivers的错误
nmxprime 2014年

如果您的活动上下文为空,该怎么办。然后,“ this”将无效。
IgorGanapolsky

当Activity上下文可以为null时?我想永远不会。
玛拉基亚斯2015年


1

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


1

我在onPause中将包含子片段的片段强制为NULL,它解决了我的问题

fragment = null;

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();
    }
}

0

我遇到了这个问题,在这里找不到解决方案,因此我想分享我的解决方案,以防其他人再次遇到此问题。

我有以下代码:

public void finishAction() {
  onDestroy();
  finish();
}

并通过删除“ onDestroy();”行解决了该问题。

public void finishAction() {
  finish();
}

我写初始代码的原因:我知道当您执行“ finish()”时,活动将调用“ onDestroy()”,但是我正在使用线程,因此我想确保在开始下一个活动之前,所有线程都被销毁了。 ,并且看起来“ finish()”并不总是立即的。我需要处理/减少很多“位图”并显示大的“位图”,并且我正在努力改善应用程序中的内存使用率

现在,我将使用其他方法杀死线程,并从“ onDestroy();”执行该方法。当我认为我需要杀死所有线程时。

public void finishAction() {
  onDestroyThreads();
  finish();
}

0

我遇到了这个问题,意识到那是因为我在打电话 setContentView(int id),我两次ActivityonCreate


0

这让我为Xamarin疯狂。

我遇到了一个Tab的ViewPager实现,而片段本身在DrawerLayout中实现:

 - DrawerLayout
   - DrawerFragment
     - TabLayout
     - TabViewPager
       - TabViewPagerFragments

因此,您必须在DrawerFragment中实现以下代码。请注意选择正确的FragmentManager-Path。因为您可能有两个不同的FragmentManager引用,所以:

  1. Android.Support.V4.App.FragmentManager
  2. Android.App.FragmentManager

->选择您要使用的那个。如果要使用ChildFragmentManager,则必须为ViewPager使用类声明Android.App.FragmentManager

Android.Support.V4.App.FragmentManager

在您的“主要”片段中实现以下方法-在本示例中:DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/support/v4/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}

Android.App.FragmentManager

public class TabViewPager: Android.Support.V13.App.FragmentPagerAdapter {}

这意味着您必须使用Android.App.FragmentManager初始化ViewPager。

在您的“主要”片段中实现以下方法-在本示例中:DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}

0

该错误已在最新的androidx版本中修复。现在,著名的解决方法将导致崩溃。所以我们现在不需要它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.