Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
272 views
Welcome To Ask or Share your Answers For Others

1 Answer

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();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...