Является ли это правильным способом очистки заднего стека фрагментов при выходе из глубоко вложенного стека?

Я использую библиотеку совместимости с Android для реализации фрагментов и расширил образец макета, чтобы фрагмент содержал кнопку, которая запускает другой фрагмент.

В панели выбора слева у меня есть 5 выбираемых элементов — A B C D E.

Каждый загружает фрагмент (через FragmentTransaction:replace) в область сведений — a b c d e

Теперь я расширил фрагмент e, чтобы он содержал кнопку, которая загружает другой фрагмент e1, также на панели сведений. Я сделал это для метода onClick фрагмента e следующим образом:

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details_frag, newFrag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();

Если я сделаю следующие выборы:

E - e - e1 - D - E

Затем фрагмент e находится в области деталей. Это нормально и то, что я хочу. Однако, если я нажму кнопку back в этот момент, это ничего не сделает. Я должен дважды щелкнуть по нему, потому что e1 все еще находится в стеке. Кроме того, после щелчка я получил исключение нулевого указателя в onCreateView:

Чтобы «решить» эту проблему, я добавил следующее всякий раз, когда выбрано A B C D E:

FragmentManager fm = getActivity().getSupportFragmentManager();
for(int i = 0; i < fm.getBackStackEntryCount(); ++i) {
    fm.popBackStack();
}

Просто интересно, правильное ли это решение или мне следует делать что-то другое?


person PJL    schedule 27.04.2011    source источник


Ответы (6)


Что ж, есть несколько способов сделать это в зависимости от предполагаемого поведения, но эта ссылка должна дать вам все лучшие решения, и неудивительно, что она от Дайанны Хакборн.

http://groups.google.com/group/android-developers/browse_thread/thread/d2a5c203dad6ec42< /а>

По сути, у вас есть следующие варианты

  • Используйте имя для вашего исходного состояния обратного стека и используйте FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • Используйте FragmentManager.getBackStackEntryCount()/getBackStackEntryAt().getId(), чтобы получить идентификатор первой записи в заднем стеке, и FragmentManager.popBackStack(int id, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) должен вытолкнуть весь задний стек... Я думаю, что документация для этого просто неверна. (На самом деле, я думаю, это просто не распространяется на случай, когда вы передаете POP_BACK_STACK_INCLUSIVE),
person Joachim    schedule 12.05.2011
comment
Что это значит? FragmentManager.getBackStackEntryCount()/getBackStackEntryAt().getId() - person dannyroa; 10.08.2012
comment
2-й работал для меня. Что это означает для очистки всего стека: getSupportFragmentManager().popBackStack(getSupportFragmentManager().getBackStackEntryAt(0).getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE); - person NickL; 14.11.2012
comment
@JorgeGarcia, возможно ли, что после popbackstack мы просто закончим наш фрагмент, не перезапуская старый. - person duggu; 28.09.2013
comment
Обратите внимание, что существует также версия popBackStackImmediate(), поскольку popBackStack() является асинхронной, то есть очистка не происходит в тот момент, когда вы вызываете метод. - person Genc; 05.09.2016
comment
Утверждает, что группа запрещена как спам, и я не могу получить доступ к ссылке :( у кого-нибудь есть ресурс в другом месте? - person EpicPandaForce; 09.05.2017
comment
Что, если у меня есть несколько фрагментов в потоке, таких как A-->B--->C-->D и от D, если я хочу вернуться к C, а затем к B и, наконец, к A. Но варианты выше всегда приводят меня на A, если я снова нажму на B/C/D. Каким должен быть правильный способ управлять этим? - person NGR; 02.07.2017
comment
@NGR, просто FragmentManager.popBackStack() на каждом шагу. - person CoolMind; 16.12.2019

Другое чистое решение, если вы не хотите выталкивать все записи стека...

getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_container, fragmentInstance).addToBackStack(null).commit();

Это сначала очистит стек, а затем загрузит новый фрагмент, поэтому в любой момент у вас будет только один фрагмент в стеке.

person Pawan Maheshwari    schedule 24.07.2013
comment
Это очень плохо: getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); перед заменой (), потому что загружается фрагмент, вызывающий onCreateView(), onActivityCreated() и т. д. Мгновенное восстановление и уничтожение фрагмента — это плохо. Например, фрагмент может зарегистрировать получателя. Это влияет на производительность. - person VasileM; 10.05.2019
comment
@VasileM, не могли бы вы подробно описать? В каких случаях это плохо? Это из-за асинхронного поведения FragmentTransaction? Есть ли только плохая производительность неправильного стека фрагментов? - person CoolMind; 16.12.2019
comment
@VasileM для этого может быть хорошей практикой регистрировать наблюдателя и другие задания в обратном вызове потока OnViewCreated Fragment или обрабатывать незарегистрированные наблюдатели, когда текущий фрагмент уничтожен ' override fun onDestroy() { super.onDestroy() viewModel.viewState.removeObservers( это) } ' - person Aury0n; 30.03.2021

Благодаря ответу Joachim, я использую код, чтобы окончательно очистить все записи обратного стека.

// In your FragmentActivity use getSupprotFragmentManager() to get the FragmentManager.

// Clear all back stack.
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) {

    // Get the back stack fragment id.
    int backStackId = getSupportFragmentManager().getBackStackEntryAt(i).getId();

    getSupportFragmentManager().popBackStack(backStackId, 
        FragmentManager.POP_BACK_STACK_INCLUSIVE);

} /* end of for */
person Jay Parker    schedule 09.11.2012
comment
Зачем использовать цикл здесь? Для меня FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) очистил все записи обратного стека... - person Marek; 22.05.2013
comment
@Marek, потому что иногда не все фрагменты нужно удалять, для этого подходит цикл. - person CoolMind; 08.12.2016
comment
@CoolMind, для меня это все еще не имеет смысла. popBackStack(backStackId, INCLUSIVE) извлечет все фрагменты (состояния) обратно в backStackId, поэтому, как только вы извлечете самый низкий i, который вы собираетесь извлечь, все более высокие должны быть извлечены одновременно. Так в чем смысл цикла? - person LarsH; 13.11.2019
comment
@LarsH, ты прав. См. stackoverflow.com/a/59158254/2914140. Чтобы подтолкнуть другие фрагменты к требуемому тегу, мы должны сначала добавить фрагмент с addToBackStack(тег). - person CoolMind; 16.12.2019

Я много занимался очисткой Backstack и, наконец, увидел Стек транзакций и управление им. Вот решение, которое сработало лучше всего для меня.

 // CLEAR BACK STACK.
    private void clearBackStack() {
        final FragmentManager fragmentManager = getSupportFragmentManager();
        while (fragmentManager.getBackStackEntryCount() != 0) {
            fragmentManager.popBackStackImmediate();
        }
    }

Приведенный выше метод перебирает все транзакции в бэкстеке и сразу же удаляет их по одной.

Примечание: приведенный выше код иногда не работает, и я сталкиваюсь с ANR из-за этого кода, поэтому, пожалуйста, не пытайтесь это сделать.

Обновите метод ниже, чтобы удалить все фрагменты этого "имени" из стопки.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack("name",FragmentManager.POP_BACK_STACK_INCLUSIVE);
  • name Если не нуль, это имя предыдущего состояния возврата для поиска; если найдено, все состояния до этого состояния будут извлечены. То
  • Флаг POP_BACK_STACK_INCLUSIVE может использоваться для контроля того, всплывает ли само именованное состояние. Если null, выталкивается только верхнее состояние.
person Lokesh    schedule 19.06.2015
comment
Я не понимаю, почему вы удаляете их по одному. Есть ли причина, по которой вы выбрали это решение вместо других решений? Есть ли причина удалять их по одному, а не все сразу? - person miva2; 26.11.2015
comment
нет способа удалить backstack за один раз, см. developer.android.com/reference/ андроид/приложение/ - person Lokesh; 26.11.2015

Я использую тот же код, что и те, которые используют цикл while, но я вызываю количество входов в каждом цикле... поэтому я полагаю, что это несколько медленнее.

FragmentManager manager = getFragmentManager();
while (manager.getBackStackEntryCount() > 0){
        manager.popBackStackImmediate();
    }
person Jorge Lozano    schedule 29.02.2016

Как написано в Как извлечь фрагмент из backstack и LarsH здесь мы можем вытолкнуть несколько фрагментов сверху вниз к определенному тегу (вместе с тегированным фрагментом), используя этот метод :

fragmentManager?.popBackStack ("frag", FragmentManager.POP_BACK_STACK_INCLUSIVE);

Замените «frag» тегом вашего фрагмента. Помните, что сначала мы должны добавить фрагмент в стопку с помощью:

fragmentTransaction.addToBackStack("frag")

Если мы добавим фрагменты с помощью addToBackStack(null), мы не будем извлекать фрагменты таким образом.

person CoolMind    schedule 03.12.2019