Fragment onResume() & onPause() is not called on backstack

286,831

Solution 1

The fragments onResume() or onPause() will be called only when the Activities onResume() or onPause() is called. They are tightly coupled to the Activity.

Read the Handling the Fragment Lifecycle section of this article.

Solution 2

  • Since you have used ft2.replace(), FragmentTransaction.remove() method is called and the Loginfragment will be removed. Refer to this. So onStop() of LoginFragment will be called instead of onPause(). (As the new fragment completely replaces the old one).
  • But since you have also used ft2.addtobackstack(), the state of the Loginfragment will be saved as a bundle and when you click back button from HomeFragment, onViewStateRestored() will be called followed by onStart() of LoginFragment. So eventually onResume() won't be called.

Solution 3

Here's my more robust version of Gor's answer (using fragments.size()is unreliable due to size not being decremented after fragment is popped)

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager() != null) {

                Fragment topFrag = NavigationHelper.getCurrentTopFragment(getFragmentManager());

                if (topFrag != null) {
                    if (topFrag instanceof YourFragment) {
                        //This fragment is being shown. 
                    } else {
                        //Navigating away from this fragment. 
                    }
                }
            }
        }
    });

And the 'getCurrentTopFragment' method:

public static Fragment getCurrentTopFragment(FragmentManager fm) {
    int stackCount = fm.getBackStackEntryCount();

    if (stackCount > 0) {
        FragmentManager.BackStackEntry backEntry = fm.getBackStackEntryAt(stackCount-1);
        return  fm.findFragmentByTag(backEntry.getName());
    } else {
        List<Fragment> fragments = fm.getFragments();
        if (fragments != null && fragments.size()>0) {
            for (Fragment f: fragments) {
                if (f != null && !f.isHidden()) {
                    return f;
                }
            }
        }
    }
    return null;
}

Solution 4

If you really want to replace fragment inside other fragment you should use Nested Fragments.

In your code you should replace

final FragmentManager mFragmentmanager =  getFragmentManager();

with

final FragmentManager mFragmentmanager =  getChildFragmentManager();

Solution 5

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            List<Fragment> fragments = getFragmentManager().getFragments();
            if (fragments.size() > 0 && fragments.get(fragments.size() - 1) instanceof YoureFragment){
                //todo if fragment visible
            } else {
                //todo if fragment invisible
            }

        }
    });

but be careful if more than one fragment visible

Share:
286,831

Related videos on Youtube

Krishnabhadra
Author by

Krishnabhadra

After having dates with C, C++, 8051, AVR, Linux, QT now concentrating on mobile application development(iOS and Android). Still plays with C now and then, can't live without. Now making swift progress with Swift. A post graduate in Electronics, switched career to become a application software developer. Now a proud owner of iOS gold badge :) SOreadytohelp

Updated on October 26, 2021

Comments

  • Krishnabhadra
    Krishnabhadra over 2 years

    I have multiple fragment inside an activity. On a button click I am starting a new fragment, adding it to backstack. I naturally expected the onPause() method of current Fragment and onResume() of new Fragment to be called. Well it is not happening.

    LoginFragment.java

    public class LoginFragment extends Fragment{
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
          final FragmentManager mFragmentmanager =  getFragmentManager();
    
          Button btnHome  = (Button)view.findViewById(R.id.home_btn);
          btnHome.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view){
               HomeFragment fragment    = new HomeFragment();
               FragmentTransaction ft2   =  mFragmentmanager.beginTransaction();
               ft2.setCustomAnimations(R.anim.slide_right, R.anim.slide_out_left
                        , R.anim.slide_left, R.anim.slide_out_right);
               ft2.replace(R.id.middle_fragment, fragment);
               ft2.addToBackStack(""); 
               ft2.commit();    
             }
          });
      }
    
      @Override
      public void onResume() {
         Log.e("DEBUG", "onResume of LoginFragment");
         super.onResume();
      }
    
      @Override
      public void onPause() {
        Log.e("DEBUG", "OnPause of loginFragment");
        super.onPause();
      }
    }
    

    HomeFragment.java

    public class HomeFragment extends Fragment{
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
      }
    
      @Override
      public void onResume() {
         Log.e("DEBUG", "onResume of HomeFragment");
         super.onResume();
      }
    
      @Override
      public void onPause() {
         Log.e("DEBUG", "OnPause of HomeFragment");
         super.onPause();
      }
    }
    

    What I expected, was,

    1. When button is clicked, LoginFragment gets replaced with HomeFragment, onPause() of LoginFragment, and onResume() of HomeFragment gets called
    2. When back is pressed, HomeFragment is poped out and LoginFragment is seen, and onPause() of HomeFragment and onResume() of LoginFragment gets called.

    What I am getting is,

    1. When button is clicked, HomeFragment is correctly replacing LoginFragment, onResume() of HomeFragment is called, but onPause() of LoginFragment is never called.
    2. When back pressed, HomeFragment is correctly popping to reveal LoginFragment, onPause() of HomeFragment gets called, but onResume() of LoginFragment never gets called.

    Is this the normal behaviour? Why is onResume() of LoginFragment not getting called when I press the back button.

    • blessanm86
      blessanm86 almost 12 years
      Add the activity code that handles the fragments.
    • Sam
      Sam almost 12 years
      i'm having the sample problem, on pause not get called, how did you resolve this,
    • Friggles
      Friggles over 11 years
      I had the same problem but realised i was using ft2.add(); instead of ft2.replace(). Only other reason would be if your activity is keeping a reference to the fragment (adding it to a collection, or assigning it to a class variable)
    • ariets
      ariets about 11 years
      I am having the same problem. I noticed that .replace() will call the necessary lifecycle methods, but it essentially destroys the fragment. Also, onSaveInstanceState is not called. As such, I cannot keep its state. So, I need to use add, but the onResume/Pause is not called :(
    • benkc
      benkc over 9 years
      FWIW, my experience is that support library fragments do call onPause and onResume when pushing/popping backstack, but the Android built-in fragments do not. Haven't found a proper workaround for that yet.
    • Maverick Meerkat
      Maverick Meerkat over 5 years
      why do you inflate login_fragment in the home fragment? is that a typing mistake?
    • Krishnabhadra
      Krishnabhadra over 5 years
      @DavidRefaeli Ya it was a typo. I tried to clean up the code base (removing some un important contents) before posting here. I no longer has access to this code (This question was asked 6 years ago). But I remember I got this working that day.
  • Krishnabhadra
    Krishnabhadra over 11 years
    Yes they are inside a fragment activity.
  • thd
    thd about 10 years
    In the article, it is said that "once the activity reaches the resumed state, you can freely add and remove fragments to the activity. Thus, only while the activity is in the resumed state can the lifecycle of a fragment change independently." Does this mean that the fragment onResume can be called even if the activity onResume is not called ?
  • Illegal Argument
    Illegal Argument about 10 years
    if fragments are added dynamically then you can add as many fragment as you want to a fragment but not to the ones defined in xml<fragment > tag
  • Dmide
    Dmide over 9 years
    as for native (not support) fragments in 4.4 (not sure if it's true for older versions) onPause() and onResume() are called not only when these events occur in the activity, but for example when you call replace() or add()/remove() while performing transaction, so this answer is misleading at least for the recent versions of Android.
  • benkc
    benkc over 9 years
    According to that document, the fragment should actually be moved to the stopped state when swapped out into the backstack. But not only are onPause and onResume not getting called, neither are onStop and onStart -- or for that matter, any other lifecycle methods. So the guide is definitely misleading.
  • Sufian
    Sufian almost 9 years
    Although not related but I found this question while searching for my problem of onPause() getting called after onSaveInstanceState() instead of before it. This can be reproduced if you to a different child in my FragmentStatePagerAdapter (say you move from child 0 to child 2, note to self, this happens because child 0 is destroyed when child 2 opens)
  • breakline
    breakline over 6 years
    You should never call lifecycle methods manually, especially inside each other
  • Vikash Parajuli
    Vikash Parajuli over 6 years
    @breakline this technique works. Do you have any other way?
  • breakline
    breakline over 6 years
    Yes, you should add your own implementation to call because lifecycle methods are also called by the system and if you call lifecycle methods inside each other like this you might (and most likely will) cause later issues.
  • CoolMind
    CoolMind almost 6 years
    How does it help in situation when a user returns from fragment 2 to fragment 1?
  • CoolMind
    CoolMind almost 6 years
    Thanks, it works, but if only one fragment is visible (so, ViewPager with fragments will reference to its fragments).
  • Maverick Meerkat
    Maverick Meerkat over 5 years
    Not sure what you mean. Calling ft.replace should trigger the onPause (of the replaced fragment) and onResume (of the replacing fragment). This is done regardless of any activity...
  • Qylin
    Qylin over 5 years
    I can confirm that this effect does not exist in the latest Android 9.0. The fragment::onResume() get invoked when popped up from the backstack. I can also confirm that we had this issue before, about 3/4 years ago.
  • Farid
    Farid over 5 years
    This is not an answered, what if "add" is must for someone's design?!
  • Farid
    Farid over 4 years
    onViewStateRestored is called if you have setRetainInstance(true)
  • Farid
    Farid over 3 years
    Depends on how you are "changing" your fragment. If you add it and then popBack then onResume won't be called. Probably you're replacing fragments and on popBack you get onResume callback fired
  • aberaud
    aberaud about 3 years
    Nested fragments are now fully supported by AndroidX.
  • Izadi Egizabal
    Izadi Egizabal almost 3 years
    and setRetainInstance is currently deprecated in favor of the viewModel pattern :/