【问题标题】:Getting the error "Java.lang.IllegalStateException Activity has been destroyed" when using tabs with ViewPager使用带有 ViewPager 的选项卡时出现错误“Java.lang.IllegalStateException Activity 已被破坏”
【发布时间】:2013-02-18 21:07:43
【问题描述】:

我有一个应用程序,它包括在选项卡模式下使用 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 类中,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(class Tab2) 时,我看到它有一个 FragmentManager 变量,当第一次单击 Tab2 时有一个名为 mActivity 的字段指向 MainActivity。但是从 tab2 切换到 some另一个选项卡并再次查看 mActivity 它的值为 null,这也许可以解释为什么它给出错误 Activity 已被破坏。

标签: android android-fragments android-viewpager actionbarsherlock android-nested-fragment


【解决方案1】:

这似乎是新添加的对嵌套片段的支持中的一个错误。基本上,子FragmentManager 在与活动分离时最终会出现内部状态损坏。为我解决此问题的短期解决方法是将以下内容添加到每个 FragmentonDetach() 中,您将其称为 getChildFragmentManager()

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

【讨论】:

  • 如果你查看 Fragment 的实现,你会发现当移动到分离状态时,它会重置其内部状态。但是,它不会重置 mChildFragmentManager(这是当前版本的支持库中的一个错误)。这会导致它在重新附加 Fragment 时不重新附加子片段管理器,从而导致您看到的异常。
  • 你一定是在开玩笑吧。很高兴你发现发布了这个,但很遗憾。
  • Android 开源问题跟踪器正在跟踪此错误:code.google.com/p/android/issues/detail?id=42601
  • 一小撮盐,但使用 support-v4 版本或 android.app 版本会失败。所以bug不仅仅出现在支持库中
  • 它似乎在支持库版本 24.0.0 中已修复。 'android.support.v4.app.Fragment' 的方法 'performDetach()' 执行 'mChildFragmentManager = null;'。
【解决方案2】:

我遇到了完全相同的问题。 我发现的唯一解决方法是在每次更改选项卡时用新实例替换片段。

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

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

【讨论】:

  • 哇,非常感谢...解决了问题。是否可以解释它为什么起作用?我正在努力使用 FragmentManager 修复错误。
  • 你分析过你的内存占用吗?因为这种解决方法可能会增加它,至少我怀疑
  • 不知道它为什么起作用..不知道它是如何起作用的,但它起作用了哈哈
【解决方案3】:

我在方法结束时调用super.onCreate() 时遇到了同样的问题。原因:attachActivity()在FragmentActivity的onCreate()中被调用。当覆盖onCreate() 并且例如创建选项卡时,选项卡管理器将尝试切换到片段,而没有将活动附加到片段管理器。

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

一般来说,出现此问题的原因似乎有很多。这只是另一个......

马蒂亚斯

【讨论】:

  • 非常感谢,你拯救了我的一天!我有同样的问题,一周后仍然无法解决;(。所以我很高兴你写了这个答案!!!
【解决方案4】:

想补充一点,我的问题出在我尝试在 onCreate 中创建 FragmentTransaction 的活动中,然后我调用了 super.onCreate()。我刚刚将 super.onCreate() 移到函数顶部并且工作正常。

【讨论】:

    【解决方案5】:

    我遇到了这个错误,因为我使用的是 LocalBroadcastManager 并且我做到了:

    unregisterReceiver(intentReloadFragmentReceiver);
    

    代替:

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

    【讨论】:

    • 这并不能解决我的问题。但谢谢它帮助我纠正了我使用 BroadcastReceivers 的错误
    • 如果您的活动上下文为空怎么办。那么“this”就无效了。
    • 活动上下文何时可以为空?我认为永远不会。
    【解决方案6】:

    我遇到了同样的问题,后来发现,我在 FragmentActivity 的onCreate() 中错过了对super.onCreate( savedInstanceState ); 的呼叫。

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

        fragment = null;
        

        【讨论】:

          【解决方案9】:

          我知道这是一篇旧帖子,但建议的答案对我来说不起作用。我想把它留在这里,以防有人会发现它有用。

          我做的是:

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

          【讨论】:

            【解决方案10】:

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

            我有这个代码:

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

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

            public void finishAction() {
              finish();
            }
            

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

            现在我将使用不同的方法终止线程,并从“onDestroy();”执行此方法当我认为我需要杀死所有线程时。

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

            【讨论】:

              【解决方案11】:

              我遇到了这个问题,并意识到这是因为我在ActivityonCreate 中调用了两次setContentView(int id)

              【讨论】:

                【解决方案12】:

                这个让我为 Xamarin 疯狂。

                我在 Fragment 中使用 TabLayout 的 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+"");
                    }
                }
                

                【讨论】:

                  【解决方案13】:

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

                  【讨论】:

                    【解决方案14】:

                    我发现我在后台运行了一个计时器。当活动被杀死时,计时器仍在运行。在计时器完成回调中,我访问片段对象做一些工作,这是错误!!!!片段存在,但活动不存在。

                    如果您有计时器服务或任何后台线程,请确保不要访问片段对象。

                    【讨论】:

                    • 这对我来说更像是评论,而不是答案。但是,无论如何,请注意语法、标点、大写、拼写等很重要。请编辑以解决问题。您可以在 StackOverflow.com/help 的 SO 帮助中心找到更多信息。
                    猜你喜欢
                    • 2018-06-08
                    • 2016-03-02
                    • 1970-01-01
                    • 2015-06-01
                    • 1970-01-01
                    • 2012-08-02
                    • 1970-01-01
                    相关资源
                    最近更新 更多