Fragment onResume() & onPause() is not called on backstack
Asked Answered
N

20

230

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.

Nuclear answered 4/7, 2012 at 9:23 Comment(7)
Add the activity code that handles the fragments.Vaclava
i'm having the sample problem, on pause not get called, how did you resolve this,Foraminifer
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)Cwm
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 :(Odum
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.Hypsometer
why do you inflate login_fragment in the home fragment? is that a typing mistake?Adiaphorous
@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.Nuclear
I
208

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.

Immolate answered 27/4, 2013 at 13:55 Comment(6)
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 ?Vile
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.Hammon
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.Hypsometer
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)Csc
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...Adiaphorous
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.Urquhart
O
23
  • 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.
Orthodontics answered 27/4, 2013 at 13:45 Comment(2)
onViewStateRestored is called if you have setRetainInstance(true)Kinnikinnick
and setRetainInstance is currently deprecated in favor of the viewModel pattern :/Charterhouse
R
18

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;
}
Refuse answered 24/11, 2016 at 17:41 Comment(0)
M
13

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();
Medallist answered 24/4, 2013 at 15:55 Comment(0)
L
8
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

Lauer answered 13/10, 2016 at 13:47 Comment(1)
Thanks, it works, but if only one fragment is visible (so, ViewPager with fragments will reference to its fragments).Aspiration
A
4

I have a code very similar to yours and if it works onPause () and onResume (). When changing the fragment, these functions are activated respectively.

Code in fragment:

 @Override
public void onResume() {
    super.onResume();
    sensorManager.registerListener(this, proximidad, SensorManager.SENSOR_DELAY_NORMAL);
    sensorManager.registerListener(this, brillo, SensorManager.SENSOR_DELAY_NORMAL);
    Log.e("Frontales","resume");
}

@Override
public void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
    Log.e("Frontales","Pause");

}

Log when change of fragment:

05-19 22:28:54.284 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:28:57.002 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:28:58.697 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:00.840 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:29:02.248 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:03.718 2371-2371/madi.cajaherramientas E/Frontales: Pause

Fragment onCreateView:

View rootView;
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    rootView = inflater.inflate(R.layout.activity_proximidad, container, false);
    ButterKnife.bind(this,rootView);
    inflar();
    setTextos();
    return rootView;
}

Action when I pulse back (in the activity where I load the fragment):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();

    } else {
        getFragmentManager().popBackStack();
    }

 }
Achievement answered 19/5, 2017 at 20:45 Comment(1)
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 firedKinnikinnick
L
4

I use in my activity - KOTLIN

supportFragmentManager.addOnBackStackChangedListener {
                val f = supportFragmentManager.findFragmentById(R.id.fragment_container)

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }
Labarum answered 10/5, 2019 at 22:20 Comment(0)
C
3

What i do in child fragment:

@Override
public void onDetach() {
   super.onDetach();
   ParentFragment pf = (ParentFragment) this.getParentFragment();
   pf.onResume();
}

And then override onResume on ParentFragment

Calle answered 10/12, 2017 at 9:1 Comment(3)
You should never call lifecycle methods manually, especially inside each otherFaustina
@Faustina this technique works. Do you have any other way?Violetavioletta
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.Faustina
S
1

If you add the fragment in XML, you can't swap them dynamically. What happens is they overly, so they events don't fire as one would expect. The issue is documented in this question. FragmenManager replace makes overlay

Turn middle_fragment into a FrameLayout, and load it like below and your events will fire.

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();
Scevo answered 24/8, 2015 at 7:22 Comment(0)
O
1

You can try this,

Step1: Override the Tabselected method in your activity

@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    // When the given tab is selected, switch to the corresponding page in
    // the ViewPager.
    try {
    if(MyEventsFragment!=null && tab.getPosition()==3)
    {
        MyEvents.fragmentChanged();
    }
    }
    catch (Exception e)
    {

    }
    mViewPager.setCurrentItem(tab.getPosition());
}

Step 2: Using static method do what you want in your fragment,

public static void fragmentChanged()
{
    Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}
Outmarch answered 20/1, 2016 at 12:25 Comment(0)
D
1

onPause() method works in activity class you can use:

public void onDestroyView(){
super.onDestroyView    
}

for same purpose..

Dogtooth answered 2/4, 2017 at 13:44 Comment(0)
A
1

Based on the answer of @Gor I wrote similar in Kotlin. Place this code in onCreate() of an activity. It works for one fragment visible. If you have ViewPager with fragments, it will call ViewPager's fragment, not a previous one.

supportFragmentManager.addOnBackStackChangedListener {
    supportFragmentManager.fragments.lastOrNull()?.onResume()
}

After reading https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6 I understood that it would be better in many situations to attach new fragments with replace, not add. So a need in onResume in some cases will disappear.

Aspiration answered 20/7, 2018 at 12:43 Comment(0)
K
1

for me, the cause was using navGraph to switch between fragments

app:navGraph

create the fragment like this (in Kotlin) and then all the lifecycle functions get called

val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.nav_host_fragment, fragment)
transaction.addToBackStack(null)
transaction.commit()
Klement answered 1/6, 2021 at 14:39 Comment(0)
N
0

You simple can't add a fragment to a fragment. This needs to happen in the FragmentActivity. I assume you are creating the LoginFragment in a FragmentActivity, so in order to get this working you need to add the HomeFragment via the FragmentActivity when the login closes.

The general point is that you need a FragmentActivity class from where you add each Fragment to the FragmentManager. It is not possible to do this inside a Fragment class.

Nyasaland answered 7/11, 2012 at 20:53 Comment(3)
Yes they are inside a fragment activity.Nuclear
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 > tagDue
Nested fragments are now fully supported by AndroidX.Rhinoceros
A
0

A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle. For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments

Aleydis answered 16/7, 2014 at 11:23 Comment(0)
P
0

Follow the below steps, and you shall get the needed answer

1- For both fragments, create a new abstract parent one.
2- Add a custom abstract method that should be implemented by both of them.
3- Call it from the current instance before replacing with the second one.

Protective answered 9/4, 2018 at 21:9 Comment(1)
How does it help in situation when a user returns from fragment 2 to fragment 1?Aspiration
A
0

Calling ft.replace should trigger the onPause (of the replaced fragment) and onResume (of the replacing fragment).

I do notice that your code inflates login_fragment on the home fragment, and also doesn't return the views in the onCreateView. If these are typos, can you show how these fragments are being called from within your activity?

Adiaphorous answered 20/11, 2018 at 9:44 Comment(1)
This is not an answered, what if "add" is must for someone's design?!Kinnikinnick
P
0

There are two possible override methods guaranteed to be called when a fragment appears/disappears.

oncreateview/ondestroyview - this is called whenever the fragment visibility changes

onpause/onresume - called when the app is closed by external or you use fragment replace

If you wish to create a hook as I did so you can by notified whenever a fragment is closed and catch either situation without creating a duplicate call as sometimes both could be called.

try extending the fragment class and add a boolean flag in both overrides so you catch the open and close for sure without duplicating

Pino answered 26/10, 2021 at 2:10 Comment(0)
N
0

kotlin

requireFragmentManager().addOnBackStackChangedListener {
            val fragments: List<Fragment> = requireFragmentManager().fragments
            if (fragments.isNotEmpty() && fragments[fragments.size - 1] is YOUR_FRAGMENT) {
                //todo if fragment visible
            } else {
                //todo if fragment invisible
            }
        }
Narcosis answered 12/10, 2022 at 0:47 Comment(0)
C
-3

While creating a fragment transaction, make sure to add the following code.

// Replace whatever is in the fragment_container view with this fragment, 
// and add the transaction to the back stack 
transaction.replace(R.id.fragment_container, newFragment); 
transaction.addToBackStack(null); 

Also make sure, to commit the transaction after adding it to backstack

Cheeks answered 7/3, 2013 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.