35

我遇到了一个非常令人费解的错误,我什至不知道如何开始解决。

我有一个带有一个活动的简单应用程序,视图是用片段实现的。其中一个片段内部有一个 ViewPager;所以我决定我想使用 v4 支持库的 getChildFragmentManager 类。我还必须使用 ActionBarSherlock,这会导致问题,因为它没有随 v4 库的 v11 一起提供。

我通过将 ABS 中的 v4 支持库替换为 v11 库来解决此问题,并且所有已编译并似乎正在工作,包括 ViewPager。

这是奇怪的部分:

带有 ViewPager 的片段第一次打开时,它可以正常工作;但是第二次导航到,应用程序崩溃,给出一个无用的堆栈跟踪。通过调试,我发现问题出在 getChildFragmentManager 返回的 FragmentManager 上;它抛出无活动错误。

有人知道是什么原因造成的吗?

我将发布您认为相关的代码。

谢谢你,大卫

4

8 回答 8

46

我点击了jeremyvillalobos 答案中的链接(这很有帮助),这让我找到了这个解决方法

public class CustomFragment extends Fragment {
    private static final Field sChildFragmentManagerField;

    static {
        Field f = null;
        try {
            f = Fragment.class.getDeclaredField("mChildFragmentManager");
            f.setAccessible(true);
        } catch (NoSuchFieldException e) {
            Log.e(LOGTAG, "Error getting mChildFragmentManager field", e);
        }
        sChildFragmentManagerField = f;
    }

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

        if (sChildFragmentManagerField != null) {
            try {
                sChildFragmentManagerField.set(this, null);
            } catch (Exception e) {
                Log.e(LOGTAG, "Error setting mChildFragmentManager field", e);
            }
        }
    }

    ...
}

它对我很有效,无需重新实例化片段。

于 2013-09-18T14:45:11.733 回答
23

这似乎是报告的错误

https://code.google.com/p/android/issues/detail?id=42601

变量

FragmentManagerImpl mChildFragmentManager;

在 Fragment.java 中,分离时未设置为 null。所以下次加载片段时,变量仍然指向最后一个父级。

正如在该线程上所讨论的,一种解决方法是重新实例化 Fragment。

就我而言,我在 ActionBar 选项卡中的片段之间切换。有问题的 Fragment 嵌套了 Fragment 并且在返回文件加载器 Fragment 时正在崩溃应用程序。所以这是解决方法代码:

class MainTabsListener implements ActionBar.TabListener {
    public Fragment fragment;
    public int TabPosition;

    public MainTabsListener(Fragment fragment, int tab_position) {
        this.fragment = fragment;
        TabPosition = tab_position;
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        CurrentFragment = fragment;
        CurrentTabSelectedPos = TabPosition;

        /**
         * This is a work-around for Issue 42601
         * https://code.google.com/p/android/issues/detail?id=42601
         * 
         * The method getChildFragmentManager() does not clear up
         * when the Fragment is detached.
         */
        if( fragment instanceof FileLoaderFragment ){
            fragment = reinstatiateFileLoaderFragment();
        }

        ft.replace(R.id.fragment_container, fragment);

    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        ft.remove(fragment);
    }

}
于 2013-05-17T06:09:30.600 回答
4

可悲的是,这是支持 v4 的错误,仍然存在 :(

当您通过导航抽屉或其他类似的东西选择其他片段时,具有子片段的片段被分离。所以那些子片段的 fragmentManager(getChildFragmentManager()) 不再存在。当这些片段返回时,发生了错误。炸弹!

显然,support v4 应该在 onDetach() 中清理 mChildFragmentManager,但它没有,所以我们必须依靠自己。例如具有子片段的片段中的以下代码:

@Override
    public void onDetach() {
        try {
            Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
            childFragmentManager.setAccessible(true);
            childFragmentManager.set(this, null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        super.onDetach();
    }

一切都会好的,祝你有美好的一天:)

于 2014-06-13T09:51:46.960 回答
2

你的错误可能是android.view.InflateException?

如果是这样,你应该动态地膨胀 Fragment,不要使用 XML 布局。

并且,您不应将定义为 XML 布局的片段定位到片段事务。

于 2013-02-18T05:32:58.043 回答
2

我也有同样的问题。

在一项活动中,我有 3 个按钮可以用 transaction.replace(...) 切换片段

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.layout_tablet_paneau, mLigneMessageFragment);

其中一个片段包含一个带有自定义 FragmentPagerAdapter 的 ViewPage。因此,我必须执行 getChildFragmentManager() 以允许嵌套片段。

构造函数在这里:

public LignePagerAdapter(Fragment ligneMessageTabletFragment) {
        super(ligneMessageTabletFragment.getChildFragmentManager());
    }

所以我有同样的错误:这个片段的第一次显示工作,但是当我显示其他片段并返回这个片段时,我得到了这个异常:

02-26 11:57:50.798: D/ACRA(776): 等待 Toast + worker 结束。杀死应用程序?真的
02-26 11:57:50.798: E/AndroidRuntime(776): 致命异常: main
02-26 11:57:50.798: E/AndroidRuntime(776): java.lang.IllegalStateException: 没有活动
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1075)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1070)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1861)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1474)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:931)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.os.Handler.handleCallback(Handler.java:587)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.os.Handler.dispatchMessage(Handler.java:92)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.os.Looper.loop(Looper.java:132)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 android.app.ActivityThread.main(ActivityThread.java:4126)
02-26 11:57:50.798: E/AndroidRuntime(776): at java.lang.reflect.Method.invokeNative(Native Method)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 java.lang.reflect.Method.invoke(Method.java:491)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
02-26 11:57:50.798: E/AndroidRuntime(776): 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
02-26 11:57:50.798: E/AndroidRuntime(776): at dalvik.system.NativeStart.main(Native Method)
02-26 11:57:52.818: I/dalvikvm(776): threadid=4: 对信号 3 做出反应
02-26 11:57:52.818: I/dalvikvm(776): 将堆栈跟踪写入“/data/anr/traces.txt”

因此,我可以重新创建它以解决问题,而不是放置相同的片段实例,但我似乎效率不高。

transaction.replace(R.id.layout_tablet_paneau, LigneMessageTabletFragment.newInstance());
于 2013-02-26T11:14:18.320 回答
0

参考@lopisan 答案:

我使用他的解决方案很长一段时间。

但我想有更好的方法来做到这一点!

如果 mChildFragmentManager.mActivity 为 null,则将 mChildFragmentManager 设置为 null。当 performActivityCreated 方法时。

@Override
void performActivityCreated(Bundle savedInstanceState) {
    if (getFragmentManagerActivity(mChildFragmentManager) == null) {
        setChildFragmentManager(this, null);
    }
    super.performActivityCreated(savedInstanceState);
}


public static FragmentActivity getFragmentManagerActivity(FragmentManager fragmentManager) {
    FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
    return fm.mActivity;
}



private static final Field sChildFragmentManagerField;
static {
    /**
     * BUG : causing a java.IllegalStateException error, No Activity, only
     * when navigating to Fragment for the SECOND time
     * http://stackoverflow.com /questions/15207305/getting-the-error-java-lang-illegalstateexception-activity-has-been-destroyed
     * http://stackoverflow.com/questions/14929907/causing-a-java-illegalstateexception-error-no-activity-only-when-navigating-to
     */
    Field f = null;
    try {
        f = Fragment.class.getDeclaredField("mChildFragmentManager");
        f.setAccessible(true);
    } catch (NoSuchFieldException e) {
        Log.e(TAG, "Error getting mChildFragmentManager field", e);
    }
    sChildFragmentManagerField = f;
}

public static void setChildFragmentManager(Fragment fragment, FragmentManager fragmentManager) {
    if (sChildFragmentManagerField != null) {
        try {
            sChildFragmentManagerField.set(fragment, fragmentManager);
        } catch (Exception e) {
            Log.e(TAG, "Error setting mChildFragmentManager field", e);
        }
    }
}
于 2015-09-09T12:50:52.377 回答
0

我在使用 BottomNavigationView 开关和替换导致应用程序崩溃的片段时遇到了同样的错误。在正常速度下它工作得非常好,如果用户快速切换它曾经崩溃说

IllegalStateException 无活动

在日志中

在我有之前

fragmentA = new FragmentA();
fragmentB = new FragmentB();
fragmentC = new FragmentC();

mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
        switch (item.getItemId()) {
   case R.id.fragmentA:
         replaceFragment(fragmentA);
   case R.id.fragmentB:
        repalceFragment(fragmentB);
   case R.id.fragmentC:
        repalceFragment(fragmentC);
   }
};

我认为这种片段的预先创建和使用它们是为了替换正在创建问题,现在我每次都重新创建它。它开始工作正常

mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
        switch (item.getItemId()) {
   case R.id.fragmentA:
        fragment = new FragmentA();
         replaceFragment(fragment);
   case R.id.fragmentB:
        fragment = new FragmentB();
        repalceFragment(fragment);
   case R.id.fragmentC:
        fragment = new FragmentC();
        repalceFragment(fragment);
   }
};

 public void repalceFragment(Fragment fragment) {
    if (!this.isFinishing()) {
        if (!fragment.isAdded()) {
            if (!this.mDisplayedFragment.isRemoving()) {
                getFragmentManager().beginTransaction().replace(R.id.layout_fragment_container, fragment).commit();
                this.mDisplayedFragment = fragment;
            }
        }
    }
}

希望它对某些人有所帮助,我做了太多的防御性编码,但它解决了我快速切换片段的问题

于 2017-07-05T06:52:31.803 回答
-1

请检查此链接的第 32 号:

分离父 Fragment 后保留的子 FragmentManager

脚步:

  1. 下载这个类并将其复制到您的项目中
  2. 使用支持 v4 扩展每个片段(最佳实践)
  3. 使用该类而不是 FragmentStatePagerAdapter
于 2015-12-15T10:35:40.197 回答