Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do popBackStack() and replace() operations differ?

I've encountered some curious behavior in my application when managing Fragments and I was wondering if SO could help shed some light onto why this happens.

I have two Fragments, we'll call them Fragment A and Fragment B. The general flow of my application is that when the user interacts with Fragment A in some way, Fragment B is shown by calling fragmentTransaction.replace() (this happens in all cases). When I show Fragment B, I add Fragment A to the back stack; then, when the user presses the back button on Fragment B, Fragment A gets shown again by popping from the back stack.

That's all well and good, but today I discovered that there is a flow from Fragment B which calls fragmentTransaction.replace(), replacing Fragment B with the same instance of Fragment A that is currently on the back stack.

In and of itself there's nothing wrong with that, however the strange behavior arises when I go back from Fragment A into Fragment B. If I called fragmentTransaction.replace(), the onCreate() method of Fragment B is not called.

However, if I popped Fragment A from the back stack and then replaced it with Fragment B, the onCreate() method of Fragment B is fired. Why is this?

Note that all instances of Fragment A and Fragment B are created at the time their host Activity is launched.

Edit for clarification. The case where onCreate() is called a second time is the following: Attach Fragment A => replace with Fragment B, adding Fragment A to the back stack => pop Fragment A using popBackStack() => replace Fragment A with Fragment B again.

like image 586
Jonathan Avatar asked Jul 22 '13 17:07

Jonathan


People also ask

What is popBackStack?

public abstract void popBackStack (int id, int flags) Pop all back stack states up to the one with the given identifier. This function is asynchronous -- it enqueues the request to pop, but the action will not be performed until the application returns to its event loop. Parameters. id.

What is the purpose of addToBackStack () while commiting fragment transaction?

addToBackStack. Add this transaction to the back stack. This means that the transaction will be remembered after it is committed, and will reverse its operation when later popped off the stack.

Does popBackStack destroy fragment?

FragmentManager popBackStack doesn't remove fragment.


1 Answers

replace() does 2 things:

  1. Remove currently added fragment (A) from the container (C) you indicated
  2. Add new fragment (B) to the same container

These 2 operations are what is saved as a Backstack record / transaction. Note that fragment A remains in created state, and its view is destroyed.

Now popBackStack() reverses your last transaction that you've added to BackStack.

In this case that would be 2 steps:

  1. Remove B from C
  2. Add A to C

After this, fragment B becomes detached, and if you did not keep references to it, it will be garbage collected.

To answer first part of your question, there's no onCreate() call, because FragmentB remained in created state. And answer to second part of the question is a bit longer.

First, it is important to understand that you don't actually add Fragments to Backstack, you add FragmentTransactions. So when you think that you "replace with Fragment B, adding Fragment A to the back stack", you actually add this whole operation to backstack - that is replacement of A with B. This replacement consists of 2 actions - remove A and add B.

Then, next step is popping of the transaction that contains this replacement. So you're not popping FragmentA, you're reversing "remove A, add B", which reversed is "remove B, add A".

And then final step should be clearer - there's no B that FragmentManager is aware of, so when you add it by replacing A with B at your last step, B needs to go through its early lifecycle methods - onAttach() and onCreate().

Code below illustrates what is happening.

FragmentManager fm  = getFragmentManager();
FragmentA fragmentA = new FragmentA();
FragmentB fragmentB = new FragmentB();

// 1. Show A
fm.beginTransaction()
  .add(fragmentA, R.id.container)
  .commit();

// 2. Replace A with B
// FragmentManager keeps reference to fragmentA;
// it stays attached and created; fragmentB goes 
// through lifecycle methods onAttach(), onCreate()
// and so on.
fm.beginTransaction()
  .replace(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();

// 2'. Alternative to replace() method
fm.beginTransaction()
  .remove(fragmentA)
  .add(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();

// 3. Reverse (2); Result - A is visible
// What happens:
//   1) fragmentB is removed from container, it is detached now;
//      FragmentManager doesn't keep reference to it anymore
//   2) Instance of FragmentA is placed back in the container
// Now your Backstack is empty, FragmentManager is aware only
// of FragmentA instance
fm.popBackStack();

// 4. Show B
// Since fragmentB was detached, it goes through its early
// lifecycle methods: onAttach() and onCreate().
fm.beginTransaction()
  .replace(fragmentB, R.id.container)
  .addToBackstack(null)
  .commit();
like image 147
mindeh Avatar answered Oct 25 '22 18:10

mindeh