65

Been searching for this issue for a while to no avail now:

How to determine fragment is being restored from backstack? I'm using the compatibility library and a ListFragment inside a FragmentActivity. When an item inside ListFragment is selected, a new Fragment is started to replace the ListFragment.

I noticed that when the FragmentActivity gets paused, the Fragment's onSaveInstanceState is called. But when the Fragment is put into the back stack via FragmentTransaction, onSaveInstanceState doesn't get called, then the lifecycle methods onCreateView and onActivityCreated gets called with null savedInstanceState Bundle.

I'm asking this because I want to load some data when the Fragment is created or restored, but not so when user comes back via. backstack.

I've looked at How to check if Fragment was restored from a backstack? but want to add more details in hopes this would incite an answer.

Edit: just noticed http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle) says

Note however: this method may be called at any time before onDestroy(). There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.

So onSaveInstanceState is definitely out of the question...

Community
  • 1
  • 1
dvd
  • 1,470
  • 1
  • 16
  • 24
  • As a little update to this question, I'm suspecting more and more it is due to the compatibility library's quirks. Have not yet tried to run a test case on 3.0+ devices yet, will check back when I do. – dvd Apr 20 '12 at 18:24
  • why don't you place your server call to load the data in `onCreate()`. Perhaps, that will alleviate your troubles. I believe this particular method isn't called when a fragment is restored from the back stack. – Abhijit Jun 04 '13 at 22:08

6 Answers6

44

I think that most simple way is do this for example in onViewCreated() method:

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

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

Because when android put fragment on backstack, it only destroy its view, but don't kill instance itself, so mAlreadyLoaded will be still true when fragment will be restored from backstack.

ATom
  • 15,960
  • 6
  • 46
  • 50
  • This is the correct answer to determine if the fragment instance was restored from back stack. I finally got a chance to revisit this topic and read the source to FragmentManager in the support v4 library version 13. The fragment instance like ATom said is kept when adding to back stack. So setting and checking a flag in onViewCreated works. – dvd Jun 20 '13 at 17:49
  • 5
    This only works if you called `setRetainInstance(true)`, which is not always an option. – Benoit Duffez Jun 22 '13 at 15:17
  • 4
    @BenoitDuffez I believe that is only necessary if you're working with a `Fragment` whose `FragmentTransaction` was not added to the back stack. If the transaction was added to the back stack then that `Fragment` will be retained automatically (i.e. its `onDestroy` will not be called). [**From the docs**](http://developer.android.com/guide/components/fragments.html#Transactions). If you don't add it to the back stack then you will have to call `setRetainInstance()` like you said if you want it to not be destroyed. – Tony Chan Oct 21 '14 at 22:01
  • There are some [*workarounds*](http://stackoverflow.com/questions/17252236/how-to-persist-fragment-data-after-backstack-transactions) that can be used to avoid calling `setRetainInstance()` and yet determine if the fragment is restored from the back stack. – Benoit Duffez Oct 21 '14 at 23:34
32
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.
                }
            }
        });

use this addOnBackStackChangedListener.

Yashwanth Kumar
  • 28,931
  • 15
  • 65
  • 69
  • 5
    I just tried this, the problem is onBackStackChanged is called after the fragment lifecycle methods are called. What I want to do is: load data when fragment is first created, store data and restore when needed (no reload). That was achieved with conditional loading/restoring in onActivityCreated. When fragment is restored via popping the back stack, don't reload data. Even if I set a flag in onBackStackChanged, the lifecycle methods won't see it. – dvd Oct 27 '11 at 18:36
21

When a fragment goes to back-stack onDestroyView() called. Not onDestroy().

And when a fragment pops from back-stack onCreateView() called. Not onCreate().

So add a boolean mIsRestoredFromBackstack to fragment and follow as below:

@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;
}
David
  • 37,109
  • 32
  • 120
  • 141
  • onDestroyView is being called from locking the screen, so my action is being called twice after coming back from backstack – ldrrp Feb 13 '18 at 20:34
  • "And when a fragment pops from back-stack `onCreateView()` called." well, no. – Farid Feb 29 '20 at 08:09
8

MAJOR EDIT: Oct 15 2013

The previous explanation (kept below for reference) fails when the application is put to the background and brought back to the foreground.

Instead, it is better to compare the current size of the backstack with the one when the fragment was created & put into the backstack.


Take a good look at Figure 2 in http://developer.android.com/guide/components/fragments.html#Creating

What this figure tells you is that when a fragment is restored from the backstack, its onCreate() is not called, while its onCreateView() is.


So, you may want to do something like this:

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();
    }
}
Giorgos Kylafas
  • 2,243
  • 25
  • 25
  • mBackStackSize > getFragmentManager().getBackStackEntryCount() didn't work because every time a fragment returns from back stack, these values are equal – Tamim Attafi Mar 25 '21 at 16:36
4

If you added fragment to backstack, and after some manipulation you hide it using fragmentTransaction.hide(fragment) and then restore it from backstack like fragmentTransaction.show(fragmentManager.findFragmentByTag(fragment.getName())); you can override onHiddenChanged(boolean hidden)

@Override
public void onHiddenChanged(boolean hidden) {
    // TODO Auto-generated method stub
    super.onHiddenChanged(hidden);
    if (!hidden) {
        //fragment became visible
        //your code here
    }
}
Andro Selva
  • 53,910
  • 52
  • 193
  • 240
Inc
  • 469
  • 5
  • 5
  • That's exactly what I was looking for. Thanks! Just note that it should be `if (!hidden) {` – crubio Apr 19 '17 at 16:05
0

In some cases you can use isVisible method to understand is it first showing of a fragment or is it restored from the backstack.

AlexKorovyansky
  • 4,873
  • 5
  • 37
  • 48