Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FragmentManager is already executing transactions after using method setOffscreenPageLimit()

i have a navigation drawer with a number of fragments and one of them is a tablayout with two recyclerview fragments , the RecyclerView items disappear after switching between navigation drawer fragments , i found a solution that i need to use method setOffscreenPageLimit() with the ViewPager but it gives me the bug that is mentioned in the question title

here is my logcat

java.lang.IllegalStateException: FragmentManager is already executing transactions
                                                                               at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:1626)
                                                                               at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:679)
                                                                               at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:143)
                                                                               at android.support.v4.view.ViewPager.populate(ViewPager.java:1240)
                                                                               at android.support.v4.view.ViewPager.populate(ViewPager.java:1088)
                                                                               at android.support.v4.view.ViewPager.setOffscreenPageLimit(ViewPager.java:852)
                                                                               at com.amir.ahmed.EELUStudentUnion.SelectionFragment.setupViewPager(SelectionFragment.java:59)
                                                                               at com.amir.ahmed.EELUStudentUnion.SelectionFragment.onActivityCreated(SelectionFragment.java:40)
                                                                               at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:2089)
                                                                               at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1133)
                                                                               at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1290)
                                                                               at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:801)
                                                                               at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1677)
                                                                               at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:536)
                                                                               at android.os.Handler.handleCallback(Handler.java:739)
                                                                               at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                               at android.os.Looper.loop(Looper.java:158)
                                                                               at android.app.ActivityThread.main(ActivityThread.java:7225)
                                                                               at java.lang.reflect.Method.invoke(Native Method)
                                                                               at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
                                                                               at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

an that is the tab layout fragment code

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    viewPager = (ViewPager) getActivity().findViewById(R.id.viewpager);
    setupViewPager(viewPager);

    viewPager.setOffscreenPageLimit(2);

    tabLayout = (TabLayout) getActivity().findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(viewPager);
    setupTabIcons();
}



private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter adapter = new ViewPagerAdapter(getFragmentManager());
    adapter.addFrag(new Courses(), "IT");
    adapter.addFrag(new CoursesBussiness(), "Business");
    viewPager.setAdapter(adapter);
}
like image 662
Mero_vic280 Avatar asked Oct 17 '16 19:10

Mero_vic280


People also ask

What is the function of the FragmentManager?

FragmentManager is the class responsible for performing actions on your app's fragments, such as adding, removing, or replacing them, and adding them to the back stack.

What is the difference between FragmentManager and SupportFragmentManager?

These two classes are clearly related. Is SupportFragmentManager used for Fragments generated using FragmentTransaction , while the "regular" FragmentManager is used exclusively to test Fragments generated using .

What is the difference between commit and commitNow ()?

Version 24.0. 0 of the support library added commitNow() as a better alternative to executePendingTransactions(). The former only executes the current transaction synchronously, whereas the latter will execute all of the transactions you have committed and are currently pending.


2 Answers

I encountered the similar issue as you, after several hours research, I found the issue. If your ViewPager is created inside the Fragment, you need to use getChildFragmentManager() for the FragmentPagerAdapter, rather than getFragmentManager().

So your setupViewPager() method should look like this.

private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager());
    adapter.addFrag(new Courses(), "IT");
    adapter.addFrag(new CoursesBussiness(), "Business");
    viewPager.setAdapter(adapter);
}

The root cause of the issue is you embeded Fragments inside the Fragment, that's why it will throw the exception.

java.lang.IllegalStateException: FragmentManager is already executing transactions

According to the Google's docs:

You can now embed fragments inside fragments. This is useful for a variety of situations in which you want to place dynamic and re-usable UI components into a UI component that is itself dynamic and re-usable. For example, if you use ViewPager to create fragments that swipe left and right and consume a majority of the screen space, you can now insert fragments into each fragment page. To nest a fragment, simply call getChildFragmentManager() on the Fragment in which you want to add a fragment. This returns a FragmentManager that you can use like you normally do from the top-level activity to create fragment transactions.

like image 179
Weidian Huang Avatar answered Oct 21 '22 10:10

Weidian Huang


I think the real answer would be to call setOffscreenPageLimit before setting the adapter, because after the adapter is set, the ViewPager will start populate the child fragments. That's why it will create a race condition between the child fragments instantiation and setting page limits. If the page limit is smaller than default I think it will have to destroy some fragments while trying to create them.

Solution

viewPager.setOffscreenPageLimit(2);
...
viewPager.setAdapter(adapter);

Reason

Checking the source code of ViewPager the default offscreen pages is 1 (DEFAULT_OFFSCREEN_PAGES). Any other value used for setOffscreenPageLimit() will trigger a populate() call inside the ViewPager.

public void setOffscreenPageLimit(int limit) {
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
                + DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }
    if (limit != mOffscreenPageLimit) {
        mOffscreenPageLimit = limit;
        populate();
    }
}
like image 27
pumnao Avatar answered Oct 21 '22 11:10

pumnao