FragmentManager вже виконує транзакції. Коли безпечно ініціалізувати пейджер після коміту?


77

У мене є робота з двома фрагментами. Діяльність починається з показу завантажувача під час завантаження об’єкта. Потім завантажений об'єкт передається обом фрагментам як аргументи за допомогою методів 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 та вкладки. onResume ми ініціалізуємо це наступним чином

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

Проблема в тому, що андроїд потім кидає

java.lang.IllegalStateException: FragmentManager вже виконує транзакції

За допомогою цього трасування стека: (Я взявandroid.support з пакета імена лише для стислості)

v4.app. .populate (ViewPager.java:1235) на v4.view.ViewPager.populate (ViewPager.java:1083) на v4.view.ViewPager.setOffscreenPageLimit (ViewPager.java:847)

Дані показують, якщо setOffscreenPageLimit(...); видалено. Чи є інший спосіб уникнути цієї проблеми?

Коли в життєвому циклі транзакція фрагмента завершена, щоб я міг зачекати, щоб налаштувати пейджер?


2
очевидно, андроїд ViewPagerпідвищить NullPointerExceptionвнутрішню onCreateViewчастину фрагмента всередині пейджера до IllegalStateException : FragmentManager is already executing transactions... на всякий випадок, якщо хтось заїде і йому знатиметься
Ловіс,

Відповіді:


30

Якщо ви націлюєтеся на sdk 24 і вище, ви можете використовувати:

FragmentTransaction.commitNow()

замість commit()

Якщо ви націлюєтеся на старіші версії, спробуйте зателефонувати:

FragmentManager.executePendingTransactions()

після дзвінка до commit()


21
FragmentManager.executePendingTransactions () також спричинить виняток, оскільки всередині його коду є виклик execPendingActions () -> sureExecReady (true) -> if (mExecutingActions) {throw new IllegalStateException ("FragmentManager уже виконує транзакції") ; }
user2924714

13
Це не працює для мене. Два методи показують збій: java.lang.IllegalStateException: FragmentManager вже виконує транзакції
thalissonestrela

commitNow разом із addToBackStack також не вдалося використати :(
Нарендра Сінгх

204

Просто використовуйте childFragmentManger()для viewpagerвсерединіFragment

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

18
Чувак! Ти врятував мій день !! : D Спасибі тонна! Я використовував вкладені фрагменти, і твій фокус "getChildFragmentManager" спрацював як шарм! ^ _ ^
Вівек Соланкі

6
Це спрацювало у випадку, коли повернення `` назад '' (за допомогою кнопки `` Назад '') до комбінації активності / фрагменту також падало. Гарний дзвінок.
Джастін

1
Просте та чудове рішення, Ви врятували мій час, дякую.
vishva vijay

Дякую!! У мого додатка стався збій, коли я використав Застосувати зміни та Перезапустити діяльність як кнопку запуску.
user1090751

56

Я мав цей виняток, коли швидко замінював 2 фрагменти І використовував executePendingTransactions (). Без цього не було винятку.

Який був мій випадок? Я відкриваю фрагмент A і в його onResume () (за умови) я прошу діяльність замінити фрагмент фрагментом B. У цей момент відбувається виняток.

Моє рішення полягало у використанні Handler.post (що запускається), який розміщує запит у кінці черги потоку, замість того, щоб запускати його негайно. Таким чином ми гарантуємо, що нова транзакція буде виконана після завершення будь-яких попередніх транзакцій.

Отже, моє рішення було таким простим, як:

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

2
Простий та ефективний. Однак я використовував postDelayed. У моєму випадку це було ще краще, оскільки на етапі ініціалізації відбувається багато чого
городечний

1
Це спрацювало, але цікаво, як це вирішує проблему внутрішньо?
Девід

7

У мене була подібна проблема,

ОсновнийАктивність, додаючи фрагментA.

Потім fragmentA основна активність зворотного виклику, щоб замінити себе fragmentB.

MainActivity кидає виняток "fragmentmanager вже виконує транзакцію" при заміні та фіксації транзакції fragmentB.

Проблема насправді походить від fragmentB.

У мене є TabHost у фрагменті B, який вимагає getfragmentmanager () для додавання tabfragment.

Замініть getfragmentmanager () на getchildfragmentmanager () у fragmentB, щоб вирішити проблему.


2

Якщо хтось використовує Robolectric> 3.6 і принаймні через 4.0.2 з ViewPager, ви можете побачити це навіть із правильним кодом.

У цьому випуску github є відповідна інформація відстежує проблему для Robolectric.

Проблема не вирішена, оскільки я пишу цю відповідь, і єдиними відомими способами є використання @Config (sdk = {27}), який, як видається, працює для деяких, але не працював для мене, або реалізація ViewPagerShadow з обхідним шляхом у тестовий пакет із досить довгим кодом, на який посилається github (я можу включити його сюди, якщо це краще, але це може бути недостатньо актуальним як відповідь?) і використовувати @Config (тіні = {ShadowViewPager.class})


1

Я використав / розширив свій адаптер, FragmentStatePagerAdapterзамість FragmentPagerAdapterцього вирішив мою проблему.

Використовуйте, FragmentStatePagerAdapterякщо фрагменти динамічні і часто змінюються під час виконання.

Використовуйте, FragmentPagerAdapterякщо фрагменти статичні і НЕ часто змінюються під час виконання.

висвітлено з цієї статті, https://medium.com/inloopx/adventures-with-fragmentstatepageradapter-4f56a643f8e0


0

Отримав подібну помилку, не пов’язану з запитанням, але, напевно, це допомагає комусь при роботі з firebase. Видаліть активність, requireActivity або це з .addSnapshotListener (), залиште це поле порожнім.

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

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

                if (snapshot != null) {
                    parseData(snapshot)
                }
            }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.