Как определить восстановленный фрагмент из backstack

Некоторое время искал эту проблему, но теперь безрезультатно:

Как определить восстанавливаемый фрагмент из бэкстека? Я использую библиотеку совместимости и ListFragment внутри FragmentActivity. Когда выбран элемент внутри ListFragment, запускается новый фрагмент, который заменяет ListFragment.

Я заметил, что когда FragmentActivity приостанавливается, вызывается onSaveInstanceState фрагмента. Но когда фрагмент помещается в задний стек через FragmentTransaction, onSaveInstanceState не вызывается, тогда вызываются методы жизненного цикла onCreateView и onActivityCreated с нулевым пакетом savedInstanceState Bundle.

Я спрашиваю об этом, потому что хочу загрузить некоторые данные при создании или восстановлении фрагмента, но не так, когда пользователь возвращается через. backstack.

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

Изменить: только что заметил http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle) говорит

Однако обратите внимание: этот метод можно вызвать в любое время до onDestroy (). Есть много ситуаций, когда фрагмент может быть в основном разорван (например, когда он помещен в задний стек без отображения пользовательского интерфейса), но его состояние не будет сохранено до тех пор, пока его собственному действию действительно не потребуется сохранить его состояние.

Так что о onSaveInstanceState определенно не может быть и речи ...


person dvd    schedule 27.10.2011    source источник
comment
В качестве небольшого обновления этого вопроса я подозреваю, что все больше и больше это связано с причудами библиотеки совместимости. Еще не пробовал запустить тестовый пример на устройствах 3.0+, проверю, когда это сделаю.   -  person dvd    schedule 20.04.2012
comment
почему бы вам не разместить вызов сервера для загрузки данных в onCreate(). Возможно, это избавит вас от неприятностей. Я считаю, что этот конкретный метод не вызывается, когда фрагмент восстанавливается из заднего стека.   -  person Abhijit    schedule 05.06.2013


Ответы (6)


Я думаю, что самый простой способ сделать это, например, в методе onViewCreated ():

if (savedInstanceState == null && !mAlreadyLoaded) {
    mAlreadyLoaded = true;

    // Do this code only first time, not after rotation or reuse fragment from backstack
}

Потому что, когда Android помещает фрагмент в backstack, он только уничтожает его представление, но не убивает сам экземпляр, поэтому mAlreadyLoaded будет по-прежнему истинным, когда фрагмент будет восстановлен из backstack.

person ATom    schedule 22.07.2012
comment
Это правильный ответ, чтобы определить, был ли экземпляр фрагмента восстановлен из заднего стека. Наконец-то у меня появилась возможность вернуться к этой теме и прочитать исходный код FragmentManager в версии 13 библиотеки поддержки v4. Экземпляр фрагмента, как сказал ATom, сохраняется при добавлении в задний стек. Так что установка и проверка флага в onViewCreated работает. - person dvd; 20.06.2013
comment
Это работает, только если вы вызвали setRetainInstance(true), что не всегда возможно. - person Benoit Duffez; 22.06.2013
comment
@BenoitDuffez Я считаю, что это необходимо, только если вы работаете с Fragment, FragmentTransaction которого не был добавлен в задний стек. Если транзакция была добавлена ​​в задний стек, то этот Fragment будет сохранен автоматически (т.е. его onDestroy не будет вызываться). Из документации. Если вы не добавите его в задний стек, вам придется вызвать setRetainInstance(), как вы сказали, если вы хотите, чтобы он не был уничтожен. - person Tony Chan; 22.10.2014
comment
Есть несколько обходных путей, которые можно использовать, чтобы избежать вызова setRetainInstance() и все же определить, восстановлен ли фрагмент из заднего стека. - person Benoit Duffez; 22.10.2014

getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {    
            public void onBackStackChanged() {
                Log.i(TAG, "back stack changed ");
                int backCount = getSupportFragmentManager().getBackStackEntryCount();
                if (backCount == 0){
                                   // block where back has been pressed. since backstack is zero.
                }
            }
        });

используйте этот addOnBackStackChangedListener.

person Yashwanth Kumar    schedule 27.10.2011
comment
Я просто пробовал это, проблема в том, что onBackStackChanged вызывается после вызова методов жизненного цикла фрагмента. Я хочу: загружать данные при первом создании фрагмента, сохранять данные и восстанавливать при необходимости (без перезагрузки). Это было достигнуто с помощью условной загрузки / восстановления в onActivityCreated. Когда фрагмент восстанавливается путем извлечения заднего стека, не перезагружайте данные. Даже если я установлю флаг в onBackStackChanged, методы жизненного цикла его не увидят. - person dvd; 27.10.2011

Когда фрагмент попадает в задний стек, вызывается onDestroyView(). Не onDestroy().

И когда выскакивает фрагмент из заднего стека, вызывается onCreateView(). Не onCreate().

Итак, добавьте boolean mIsRestoredFromBackstack к фрагменту и выполните следующие действия:

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    mIsRestoredFromBackstack = false;
}

@Override
public void onResume()
{
    super.onResume();
    if(mIsRestoredFromBackstack)
    {
        // The fragment restored from backstack, do some work here!
    }
}

@Override
public void onDestroyView()
{
    super.onDestroyView();
    mIsRestoredFromBackstack = true;
}
person David    schedule 07.01.2016
comment
onDestroyView вызывается из-за блокировки экрана, поэтому мое действие вызывается дважды после возврата из backstack - person ldrrp; 13.02.2018
comment
И когда из заднего стека выскакивает фрагмент, вызывается onCreateView(). Ну нет. - person Farid; 29.02.2020

ОСНОВНОЕ РЕДАКТИРОВАНИЕ: 15 октября 2013 г.

Предыдущее объяснение (приведенное ниже для справки) не работает, когда приложение переводится в фоновый режим и возвращается на передний план.

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


Внимательно посмотрите на рисунок 2 в http://developer.android.com/guide/components/fragments.html#Creating

Этот рисунок говорит вам о том, что когда фрагмент восстанавливается из бэкстека, его onCreate () не вызывается, а его onCreateView () вызывается.


Итак, вы можете сделать что-то вроде этого:

public class MyFragment extends Fragment {
    int mBackStackSize = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBackStackSize = getFragmentManager().getBackStackEntryCount();
    }

    public boolean isRestoredFromBackstack() {
        return mBackStackSize > getFragmentManager().getBackStackEntryCount();
    }
}
person Giorgos Kylafas    schedule 24.03.2013
comment
mBackStackSize ›getFragmentManager (). getBackStackEntryCount () не работал, потому что каждый раз, когда фрагмент возвращается из заднего стека, эти значения равны - person Tamim Attafi; 25.03.2021

Если вы добавили фрагмент в backstack, и после некоторых манипуляций вы скрываете его с помощью fragmentTransaction.hide (fragment), а затем восстанавливаете его из backstack, например fragmentTransaction.show (fragmentManager.findFragmentByTag (fragment.getName ())); вы можете переопределить onHiddenChanged (логическое значение скрыто)

@Override
public void onHiddenChanged(boolean hidden) {
    // TODO Auto-generated method stub
    super.onHiddenChanged(hidden);
    if (!hidden) {
        //fragment became visible
        //your code here
    }
}
person Inc    schedule 25.09.2013
comment
Это именно то, что я искал. Спасибо! Просто обратите внимание, что это должно быть if (!hidden) { - person crubio; 19.04.2017

В некоторых случаях можно использовать метод isVisible для понять, это первый показ фрагмента или он восстановлен из бэкстэка.

person AlexKorovyansky    schedule 16.08.2012