FragmentManager已经在执行事务。提交后初始化寻呼机何时安全?

时间:2016-08-02 13:48:44

标签: android android-fragments

我有一个托管两个片段的活动。活动开始时会在加载对象时显示加载程序。然后通过newInstance方法将加载的对象作为参数传递给两个片段,并附加这些片段。

final FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.container1, Fragment1.newInstance(loadedObject));
trans.replace(R.id.container2, Fragment2.newInstance(loadedObject));
trans.commit();

第二个片段包含android.support.v4.view.ViewPager和tabs。 onResume我们初始化它如下

viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(adapter.getCount()); //the count is always < 4
tabLayout.setupWithViewPager(viewPager);

问题是android然后抛出

  

java.lang.IllegalStateException:FragmentManager已在执行   交易

使用此堆栈跟踪:(为简洁起见,我从包名称中取出android.support

  

v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:1620)                                                                                          在   v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:637)                                                                                          在   v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:143)                                                                                          在v4.view.ViewPager.populate(ViewPager.java:1235)                                                                                          在v4.view.ViewPager.populate(ViewPager.java:1083)                                                                                          在   v4.view.ViewPager.setOffscreenPageLimit(ViewPager.java:847)

数据显示是否已移除setOffscreenPageLimit(...);。还有另一种方法可以避免这个问题吗?

在生命周期中,片段事务是否已完成,以便我可以等待设置寻呼机?

9 个答案:

答案 0 :(得分:136)

只需在childFragmentManger()

中使用viewpager Fragment
mPagerAdapter = new ScreenSlidePagerAdapter(getChildFragmentManager());
mPager.setAdapter(mPagerAdapter);

答案 1 :(得分:38)

我在快速替换2个片段并使用executePendingTransactions()时遇到此异常。没有这称呼,没有例外。

我的情况是什么? 我打开一个片段A并在其onResume()中(在一个条件下)我要求活动用片段B替换片段。此时发生异常。

我的解决方案是使用Handler.post(runnable),它将查询放在线程队列的末尾而不是立即运行它。这样我们就可以确保在完成任何先前的交易之后执行新的交易。

所以我的解决方案很简单:

Handler uiHandler = new Handler();
uiHandler.post(new Runnable()
{
    @Override
    public void run()
    {
        openFragmentB(position);
    }
});

答案 2 :(得分:25)

如果您定位sdk 24及以上版本,则可以使用:

FragmentTransaction.commitNow()

而不是commit()

如果您要定位旧版本,请尝试致电:

FragmentManager.executePendingTransactions()

致电commit()

答案 3 :(得分:4)

我遇到了同样的问题。但是,我使用的是仅获取 FragmentStateAdapterFragmentActivity 构造函数:

package androidx.viewpager2.adapter;

public FragmentStateAdapter(
    @NonNull FragmentActivity fragmentActivity
) {
    this(fragmentActivity.getSupportFragmentManager(), fragmentActivity.getLifecycle());
}

即使它从 fragmentActivity 实例获取生命周期,它也根本不起作用。

然后,深入研究 FragmentStateAdapter 的代码,我看到还有另一个构造函数,您可以在其中传递 Lifecycle 实例。

package androidx.viewpager2.adapter;

public FragmentStateAdapter(
    @NonNull FragmentManager fragmentManager,
    @NonNull Lifecycle lifecycle
) {
    mFragmentManager = fragmentManager;
    mLifecycle = lifecycle;
    super.setHasStableIds(true);
}

因此,我更改了 PageAdapter 构造函数以接收 FragmentManager 作为 childFragmentManager 和来自 viewLifecycleOwner 的 LifeCycle。并将这些参数传递给 FragmentStateAdapter 构造函数:

class MyPagerAdapter(
    fragmentManager: FragmentManager,
    lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {
  //...
}

然后,当调用我的 PagerAdapter 构造函数时,我传递了 FragmentManager 和 我从 viewLifecycleOwner 获取生命周期:

val myPagerAdapter = MyPagerAdapter(
    fragmentManager = childFragmentManager,
    lifecycle = viewLifecycleOwner.lifecycle
)

[更新] MyPagerAdapter 是从 Fragment 设置的。

答案 4 :(得分:2)

如果有人使用Robolectric> 3.6并至少通过ViewPager使用4.0.2,则即使使用正确的代码,您也可能会看到它。

this github issue中有相关信息跟踪Robolectric的问题。

在我撰写此答案时,问题仍未解决,唯一已知的解决方法是使用@Config(sdk = {27}),该方法似乎对某些用户有效,但对我不起作用,或者使用以下命令实现ViewPagerShadow在测试包中有一个变通方法,其中包含在github上引用的相当长的代码(如果更好,我可以在此处包括它,但这可能不足以作为答案?)并使用@Config(shadows = {ShadowViewPager.class})< / p>

答案 5 :(得分:1)

我遇到了类似的问题,

添加片段A的mainAcitivity。

然后使用fragmentA回调mainactivity将自身替换为fragmentB。

当用fragmentB替换并提交事务时,MainActivity抛出异常“ fragmentmanager已经执行事务”。

问题实际上来自fragmentB。

我在片段B中有一个TabHost,它需要getfragmentmanager()来添加tabfragment。

用fragmentB中的getchildfragmentmanager()替换getfragmentmanager()解决了这个问题。

答案 6 :(得分:1)

ViewPager2

这是 <exclude-pattern type="relative">^(?!plugins/(arve-|advanced-re|edd-)).+</exclude-pattern> 时代的一个相当古老的答案,并帮助许多遇到此问题的人解决了这个问题。以防万一您遇到 ViewPager 并面临类似问题,

我们知道,当片段中有 ViewPager2 并且 ViewPager2 类具有带 FragmentStateAdapter 参数的构造函数时会发生这种情况,因此您可以使用它来创建适配器。

喜欢

Fragment

那么,

class ScreenSlidePagerAdapter(fragment: Fragment): FragmentStateAdapter(fragment)

答案 7 :(得分:0)

我使用{ "id" : "BNK007596994", "name" : "Live_HD", "metadataSet" : { "description" : "Live Template", "type" : "Live" } "features" : "HD" } 而不是Sub Macro9() ' ' Macro9 Macro ' ' Range("A3").Select Selection.Copy Range("A2").Select ActiveSheet.Paste Range("B2").Select Application.CutCopyMode = False Application.CutCopyMode = False ActiveCell.FormulaR1C1 = "=+SUM(R[1]C:R[3]C)" Range("B2").Select Selection.Copy Range("B3").Select Selection.End(xlToRight).Select Range("S2").Select Range(Selection, Selection.End(xlToLeft)).Select ActiveSheet.Paste Selection.End(xlToLeft).Select Range(Selection, Selection.End(xlToRight)).Select Application.CutCopyMode = False Selection.Copy Windows("Final.xlsx").Activate Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False Range("A6").Select Windows("copy.xlsm").Activate Rows("2:2").Select Range(Selection, Selection.End(xlDown)).Select Application.CutCopyMode = False Selection.Delete Shift:=xlUp Selection.End(xlToLeft).Select End Sub 扩展了适配器,这解决了我的问题。

如果片段是动态的并且在运行时频繁变化,请使用FragmentStatePagerAdapter

如果片段是静态的,并且在运行时不会频繁更改,请使用FragmentPagerAdapter

本文的启发, https://medium.com/inloopx/adventures-with-fragmentstatepageradapter-4f56a643f8e0

答案 8 :(得分:0)

得到了一个与问题无关的类似错误,但希望它在使用Firebase时能对某人有所帮助。 从.addSnapshotListener()中删除活动,requireActivity或此项,将其保留为空。

 videosListener = videosCollectionRef
            .addSnapshotListener() { snapshot, exception ->

                if (exception != null) {
                    Log.e("Exception", "Could not retrieve documents: $exception")
                }

                if (snapshot != null) {
                    parseData(snapshot)
                }
            }