FragmentActivity onSaveInstanceState не вызывается

Я видел несколько похожих вопросов о том, что onSaveInstanceState не вызывается для Fragment, но в моем случае Fragment работают нормально, а у основного FragmentActivity возникают проблемы.

Соответствующий код выглядит довольно просто:

public class MyFActivity extends FragmentActivity implements ActionBar.TabListener { 
    String[] allValues; // data to save

    @Override
    protected void onSaveInstanceState (Bundle outState) {
        Log.d("putting it!", allValues.toString());
        outState.putStringArray("allValues", allValues);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            allValues = savedInstanceState.getStringArray("allValues");
            Log.d("getting it!", allValues.toString());
        }
    }
}

При приостановке действия (с помощью кнопки «Назад») onSaveInstanceState никогда не вызывается, и, следовательно, savedInstanceState всегда является null в методе onCreate при возобновлении работы приложения. Я попытался добавить такой блок:

@Override
public void onPause() {
    super.onPause();
    onSaveInstanceState(new Bundle());      
}

который был предложен в https://stackoverflow.com/a/14195202/362657, но в то время как onSaveInstanceState затем вызывается, savedInstanceState остается null в рамках onCreate метода. Что мне не хватает?


person SaltyNuts    schedule 10.04.2013    source источник
comment
При приостановке действия (с помощью кнопки «Назад») обычно это уничтожает фрагмент, поэтому savedInstanceState теряется. Видите ли вы соответствующий saveInstanceState в onCreate() при повороте устройства?   -  person Sam    schedule 11.04.2013
comment
Вместо onCreate, возможно, вам следует использовать onResume()? Если действие только приостановлено, то onCreate() не должен вызываться снова, onResume() должен   -  person Tom    schedule 11.04.2013
comment
Хм, это объясняет! Уничтожено оно.   -  person SaltyNuts    schedule 11.04.2013


Ответы (3)


Проблема здесь в том, что вы неправильно понимаете, как работает onSaveInstanceState. Он предназначен для сохранения состояния Activity/Fragment в случае, если ОС необходимо уничтожить его по причинам памяти или изменениям конфигурации. Затем это состояние передается обратно в onCreate, когда Activity/Fragment возвращается в / перезапускается.

В Fragment все их обратные вызовы жизненного цикла напрямую связаны с их родительским Activity. Таким образом, onSaveInstanceState вызывается на Fragment, когда его родитель Activity вызывает onSaveInstanceState.

При приостановке активности (с помощью кнопки «Назад») onSaveInstanceState никогда не вызывается, и, следовательно, saveInstanceState всегда имеет значение null в методе onCreate при возобновлении работы приложения.

При нажатии назад пользователь уничтожает Activity и, следовательно, его дочерние элементы Fragments, поэтому нет причин вызывать onSaveInstanceState, так как экземпляр уничтожается. Когда вы снова открываете Activity, это совершенно новый экземпляр без сохраненного состояния, поэтому Bundle, переданное в onCreate, равно null. Это ведет себя именно так, как задумано. Однако попробуйте повернуть устройство или нажать кнопку «Домой», тогда вы увидите, что Activity и его дочерние Fragment вызываются onSaveInstanceState и возвращаются обратно в onCreate при возврате.

Добавленный вами хак, прямой вызов onSaveInstanceState(new Bundle()); внутри onPause, является очень плохой практикой, так как вы никогда не должны вызывать обратные вызовы жизненного цикла напрямую. Это может поставить ваше приложение в недопустимое состояние.

Если вы действительно хотите, чтобы ваши данные сохранялись за пределами экземпляра вашего приложения, я предлагаю вам изучить использование SharedPreferences или базы данных для получения дополнительных данных. Затем вы можете сохранить свои постоянные данные в onPause() или всякий раз, когда они изменяются.

Надеюсь это поможет.

person Steven Byle    schedule 10.04.2013
comment
Спасибо за подробное объяснение. У меня сложилось ошибочное впечатление, что кнопка «Назад» переводит действие в фоновый режим, а не уничтожает его. - person SaltyNuts; 11.04.2013
comment
Добро пожаловать, это сложная концепция, и она не документирована так хорошо, как хотелось бы. - person Steven Byle; 11.04.2013
comment
@dymmeh это круто, +1 вам, сэр - person Steven Byle; 11.04.2013
comment
Отличное объяснение. Я не был уверен, почему onSaveInstanceState не вызывается в моем фрагменте. Теперь я знаю. Спасибо - person speedynomads; 13.09.2013
comment
In a Fragment, all of their lifecycle callbacks are directly tied to their parent Activity. So onSaveInstanceState gets called on the Fragment when its parent Activity has onSaveInstanceState called. А как насчет фрагментов, размещенных в одном действии? Теоретически их такие методы, как onSavedInstanceState и ..., никогда не будут вызываться, поскольку они передаются в одном и том же действии, верно? - person inverted_index; 15.10.2017
comment
@inverted_index, если вы просто заменяете Fragments в одном Activity, onSavedInstanceState не будет вызываться, потому что заменяемый Fragment отбрасывается и ему не нужно сохранять какое-либо состояние. Однако я полагаю (вам, возможно, придется проверить это), если вы используете Fragment обратного стека, ОС будет вызывать onSavedInstanceState на сложенном Fragments, когда/если это потребуется. И да, если родителю Activity нужно вызвать onSavedInstanceState, он также сначала вызовет его для своих дочерних элементов Fragments. - person Steven Byle; 17.10.2017

В обновлении принятого ответа:

Фрагмент onSaveInstanceState может быть вызван, если вы используете ViewPager с FragmentStatePagerAdapter (а не FragmentPagerAdapter)

FragmentStatePagerAdapter

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

И не забывайте:

При использовании FragmentPagerAdapter узел ViewPager должен иметь действительный набор идентификаторов.

person Tanner Perrien    schedule 29.01.2014

Не точный ответ на вопрос, но может кому-то помочь. В моем случае я позвонил

@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
  super.onSaveInstanceState(outState, outPersistentState);
}

Я заменил приведенный выше код, как показано ниже, и все заработало

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
}
person Ebin Joy    schedule 30.07.2020