FragmentManager is already executing transactions. When is it safe to initialise pager after commit?

I have an activity hosting two fragments. The activity starts off showing a loader while it loads an object. The loaded object is then passed to both fragments as arguments via newInstance methods and those fragments are attached.

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

The second fragment contains a android.support.v4.view.ViewPager and tabs. onResume we initialise it like follows

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

The problem is android then throws

java.lang.IllegalStateException: FragmentManager is already executing transactions

With this stack trace: (I took android.support out of the package names just for brevity)

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

The data shows if setOffscreenPageLimit(...); is removed. Is there another way to avoid this issue?

When in the lifecycle is the fragment transaction complete so that I can wait to setup my pager?


Simply use childFragmentManger() for viewpager inside a Fragment

mPagerAdapter = new ScreenSlidePagerAdapter(getChildFragmentManager());
mPager.setAdapter(mPagerAdapter);

I had this exception when quickly replacing 2 fragments AND using executePendingTransactions(). Without calling this there was no exception.

What was my case? I open a fragment A and in its onResume() (under a condition) I ask the activity to replace the fragment with fragment B. At that point the exception occurs.

My solution was to use a Handler.post(runnable) which places the query on the end of the thread queue instead of running it immediately. This way we ensure that the new transaction will be executed after any previous transactions are completed.

So my solution was as simple as:

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

If you're targeting sdk 24 and above you can use:

FragmentTransaction.commitNow()

instead of commit()

If you're targeting older versions, try calling:

FragmentManager.executePendingTransactions()

after the call to commit()