165

I've written up a dummy activity that switches between two fragments. When you go from FragmentA to FragmentB, FragmentA gets added to the back stack. However, when I return to FragmentA (by pressing back), a totally new FragmentA is created and the state it was in is lost. I get the feeling I'm after the same thing as this question, but I've included a complete code sample to help root out the issue:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

With some log messages added:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

It's never calling FragmentA.onSaveInstanceState and it creates a new FragmentA when you hit back. However, if I'm on FragmentA and I lock the screen, FragmentA.onSaveInstanceState does get called. So weird...am I wrong in expecting a fragment added to the back stack to not need re-creation? Here's what the docs say:

Whereas, if you do call addToBackStack() when removing a fragment, then the fragment is stopped and will be resumed if the user navigates back.

Community
  • 1
  • 1
Eric
  • 5,038
  • 6
  • 27
  • 33
  • Does the onCreate() method of the fragment get called again after going back to it? If I remember correctly this should not be the case. If this is not the case, you can just save state in instance variables. – Jan-Henk Jul 05 '12 at 21:58
  • 3
    @Jan-Henk What about things that have to be fetched? For instance, the scroll position of a `ListView`. Seems like far too much hoop-jumping to attach a scroll listener and update an instance variable. – Jake Wharton Jul 05 '12 at 22:11
  • 2
    @JakeWharton I agree it should be easier, but as far as I know there is no way around this because onCreateView is called when a fragment is restored from the backstack. But I could be wrong :) – Jan-Henk Jul 05 '12 at 22:19
  • 1
    onCreate does not get called. So apparently it's re-using the same instance but calling onCreateView again? Lame. I guess I can just cache the result of onCreateView and just return the existing view if onCreateView gets called again. – Eric Jul 05 '12 at 22:24
  • Yup, that works. @Jan-Henk if you want to submit an answer I'll mark it as accepted. – Eric Jul 05 '12 at 22:26
  • 1
    Exactly what I was looking for hours. Can you post how you achieved this using instance variable? – LoveMeSomeFood Dec 11 '13 at 01:56
  • hide() and add() instead of replace() will be a good solution though! – Muhammad Babar Apr 02 '14 at 13:37
  • How do you go back to the first fragment with the back button if you didn't override the activity's onBackPressed() method? – Rule Jul 20 '15 at 12:25
  • 1
    So I recently started my own implementation in https://github.com/frostymarvelous/Folio and came across a problem. I'm able to create about 5 complex Pages/Fragments before I start getting OOM crashes. That's what led me here. Hiding and Showing is simply not enough. Views are too memory heavy. – frostymarvelous Mar 27 '16 at 14:54

13 Answers13

123

If you return to a fragment from the back stack it does not re-create the fragment but re-uses the same instance and starts with onCreateView() in the fragment lifecycle, see Fragment lifecycle.

So if you want to store state you should use instance variables and not rely on onSaveInstanceState().

animaonline
  • 4,399
  • 3
  • 25
  • 53
Jan-Henk
  • 4,714
  • 1
  • 21
  • 38
  • 35
    The current version of the documentation contradicts this claim. The flowchart says what you state, but the text in the main area of the page says onCreateView() is only called the _first_ time the Fragment is displayed: http://developer.android.com/guide/components/fragments.html I'm fighting this issue now, and I don't see any methods called when returning a fragment from the backstack. (Android 4.2) – Colin M. Nov 26 '12 at 21:13
  • 10
    Tried to logged its behavior. The onCreateView() is always called when the fragment is being displayed. – princepiero Sep 24 '13 at 11:47
  • This explained so much to me! Thanks a lot! – Otuyh Feb 23 '14 at 18:13
  • 4
    @ColinM. Any solution for the problem? – blizzard Apr 13 '14 at 13:03
  • This is the real answer! Thanks – Tooroop May 15 '14 at 07:24
  • 1
    What if that fragment contains a `WebView`? The WebView relies on `restoreInstanceState()` in order to restore its state. This is a mess. Is this the solution? http://stackoverflow.com/a/17869748/507339 – Nilzor Jul 08 '14 at 11:01
  • 9
    This doesn't work for me. My instance variables are null on returning to the fragment! How can I save the state? – Don Rhummy Oct 08 '14 at 19:22
  • 5
    so if we should not relay on save instance how should save fragment status and data? – Mahdi Jul 27 '16 at 06:44
  • what `instance variables` are we talking about here? – Daksh Gargas Jun 19 '18 at 10:15
  • 1
    im returning from navigation components backstack. using navigateUp(). onViewCreated is called. i save the position in onViewDestroyed in arguments. onViewCreated called on pop and attempted scroll to previous position has no effect.?? – filthy_wizard May 03 '19 at 16:45
  • Perfect Answer. Thanks :) – Rohit Sharma Oct 14 '19 at 11:44
  • can someone explain this in code? I am confused with the term instance variable here – Fugogugo Mar 23 '20 at 06:41
81

Comparing to Apple's UINavigationController and UIViewController, Google does not do well in Android software architecture. And Android's document about Fragment does not help much.

When you enter FragmentB from FragmentA, the existing FragmentA instance is not destroyed. When you press Back in FragmentB and return to FragmentA, we don't create a new FragmentA instance. The existing FragmentA instance's onCreateView() will be called.

The key thing is we should not inflate view again in FragmentA's onCreateView(), because we are using the existing FragmentA's instance. We need to save and reuse the rootView.

The following code works well. It does not only keep fragment state, but also reduces the RAM and CPU load (because we only inflate layout if necessary). I can't believe Google's sample code and document never mention it but always inflate layout.

Version 1(Don't use version 1. Use version 2)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------Update on May 3 2005:-------

As the comments mentioned, sometimes _rootView.getParent() is null in onCreateView, which causes the crash. Version 2 removes _rootView in onDestroyView(), as dell116 suggested. Tested on Android 4.0.3, 4.4.4, 5.1.0.

Version 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

WARNING!!!

This is a HACK! Though I am using it in my app, you need to test and read comments carefully.

Vince Yuan
  • 10,083
  • 2
  • 30
  • 26
  • 1
    nice answer... Thank you my friend. – ovluca May 08 '14 at 11:24
  • 1
    Excellent answer. Though I didn't quite absorb what all happened. I would appreciate if you could point to any detailed documentation related to this. Thanks – Achin Kumar May 25 '14 at 10:57
  • This works great for me, why is this not the accepted answer? – Kartik_Koro Jul 10 '14 at 06:57
  • This worked for me. Excellent wayout for addressing the issue! – SunGa Aug 02 '14 at 11:22
  • I tried it out and ran into a huge issue. It seems the fragment is not correctly attached to the activity in this case, or at least its views are not. Even getActivity() returns null. – Christian Brüggemann Sep 14 '14 at 21:48
  • 39
    Holding a reference to whole fragment's rootview is a bad idea IMO. If you are continuously adding several fragments to a backstack and all of them are holding its rootview (which has a quite big memory footprint), then there is high possibility you end up with the OutOfMemoryError since all of the fragments holds rootview reference and GC cant collect it. I think the better approach is to inflate the view all the time (and let Android system handle its view creation/destroying) and onActivityCreated/onViewCreated check if your data is null. If yes, then load it, else set the data to views. – traninho Oct 30 '14 at 18:20
  • This is the best answer. Cheers! – Kong Ken Nov 26 '14 at 08:47
  • 1
    @gZerone I think that Vince Yuan's approach can be successfuly used if you are sure that you will have limited count of fragments in a backstack (for example max 5). But in my case, I have a movie detail fragment which also contains "recommended movies" grid. If user clicks on a recommended movie then a new movie detail fragment is created and showed. So if I use this approach, then I can easily reach OOME as there will be a lot of movie detail fragments in the backstack. – traninho Dec 01 '14 at 12:32
  • 15
    *Don't do this!* When the Fragment's view hierarchy is created, it contains an internal reference to the Activity that held the fragment at the time. When a configuration change occurs, the Activity is often re-created. Re-using the old layout keeps that zombie Activity around in memory along with whatever objects it also references. Wasting memory like this hinders performance and makes your app a top candidate for immediate termination when not in the foreground. – Krylez Jan 13 '15 at 00:20
  • In my situation, `_rootView.getParent()` just return `null`, because `_rootView` is just my rootview. – roger Jan 27 '15 at 02:33
  • @traninho I asked this in the linked similar question, but wouldn't setting any View as an instance variable cause the bad effects you are describing? Almost every example I have seen does this, and any View holds references to is parent, so what is the difference here? – AllDayAmazing Feb 18 '15 at 21:26
  • 4
    @AllDayAmazing This is a good point. To be honest, I am very confused right now. Can anybody try to explain why holding a reference to a fragment's rootview is not ok but holding a reference only for any child of rootview (which has a reference to rootview anyway) is ok? – traninho Feb 19 '15 at 09:50
  • 2
    STAY AWAY FROM THIS unless you want to waste 5 hours finding out what's bugging out your code.....then only to find that this is the cause. Now I have to refactor a bunch of stuff because I used this hack. It's much better to use fragmentTransaction.add if you want to keep the fragment's UI in-tact when bringing another into view (even on top). fragmentTransaction.replace() is meant to destroy the fragment's views.....don't fight the system. – dell116 Apr 29 '15 at 07:27
  • 1
    Vince, you should seriously just tell others not to use this answer anymore. Even after using the logical paths I suggested, this hack will cause fragments to stay in the FragmentManager after calling FragmentTransaction.remove(hackedFragment) because the inflated viewgroup you're keeping in memory prevents the fragment from being removed (seriously dangerous memory leak). I can confirm this only on API level 22, but I'd down-vote this answer 10 times (killing my own reputation) if I could. Just use FragmentTransaction.add() to keep any previously viewed fragment's view hierarchy in-tact. – dell116 May 04 '15 at 18:41
  • @dell116 Thanks for the comment. But I don't understand it causes memory leak. After calling FragmentTransaction.remove(hackedFragment), hackedFragment's refCount -1, and hackedFragment's member varaibles' refCount -1. Java GC should handle it correctly. – Vince Yuan May 05 '15 at 01:54
  • 2
    @VinceYuan - I tested with the latest v7-appcompat library on Android 5.1 and this left 6 instances of a fragment that should have been removed in my activity's FragmentManager. Even if the GC will handle it correctly (which I don't believe it will) this is causing unnecessary strain on memory for your app as well as the device in general. Simply using .add() completely removes the need for all of this hacky code. Doing this is completely against what using FragmentTransaction.replace() was meant to do in the first place. – dell116 May 05 '15 at 02:13
  • The GC will handle it correctly if no more references exist. But it is up to the developer to clear those. This makes this approach extremely difficult to implement and I will advise against it all the time. Very bad approach. I also don't like the suggestion that Apple has done a better job with this than Google has. Managing fragments is difficult if you don't know how it works. Using other platform approaches (if that is the case) is definitely not helping. Developing for Android requires you to think different. – nickmartens1980 Aug 04 '15 at 15:30
  • Probably the worst idea I've ever seen. If you are having issues with how fast your layout is rendered, you have other issues going on here...You will leak Activity for every screen rotation if the Fragment is retained. – agrosner Dec 09 '15 at 20:24
  • 1method( Attempt to invoke virtual method 'void android.view.ViewGroup.removeView(android.view.View)' on a null object reference) and 2 method is wrong, I get java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position, its a good try, but not works correctly –  Mar 15 '16 at 11:46
  • This is probably not a good idea. `onCreateView` called sparingly, and only when actually the view is required. Hence, the general idea with the platform is that if the platform calls `onCreateXXX` **CREATE it!**. – IcyFlame May 27 '16 at 08:50
  • This is absolutely wrong as the onCreateView needs to inflate/create the view in a lot of different situations for example a configuration change. The actual solution to keeping the state of your data is to actually save it in onSaveInstanceState calls and retrieve them through savedInstanceState bundles – Krzysztof Dubrowski May 27 '17 at 18:08
  • Another issue that no one has mentioned is transition effects. If you use an animation like sliding a fragment in, during the duration of the animation the rootView has a parent but that parent DOES NOT have the rootView as a child. Trying to remove rootView from the parent will not work. If the user pushes back in the middle of the animation your app will crash with an IllegalStateException. – zafrani Oct 18 '17 at 23:23
55

I guess there is an alternative way to achieve what you are looking for. I don't say its a complete solution but it served the purpose in my case.

What I did is instead of replacing the fragment I just added target fragment. So basically you will be going to use add() method instead replace().

What else I did. I hide my current fragment and also add it to backstack.

Hence it overlaps new fragment over the current fragment without destroying its view.(check that its onDestroyView() method is not being called. Plus adding it to backstate gives me the advantage of resuming the fragment.

Here is the code :

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

AFAIK System only calls onCreateView() if the view is destroyed or not created. But here we have saved the view by not removing it from memory. So it will not create a new view.

And when you get back from Destination Fragment it will pop the last FragmentTransaction removing top fragment which will make the topmost(SourceFragment's) view to appear over the screen.

COMMENT: As I said it is not a complete solution as it doesn't remove the view of Source fragment and hence occupying more memory than usual. But still, serve the purpose. Also, we are using a totally different mechanism of hiding view instead of replacing it which is non traditional.

So it's not really for how you maintain the state, but for how you maintain the view.

Gastón Saillén
  • 9,076
  • 4
  • 39
  • 58
kaushal trivedi
  • 3,217
  • 1
  • 26
  • 44
  • In my case, by adding a fragment instead of replacing causes problem when using polling or anyother kind of web request is used in the fragment. I want to pause this polling in Fragment A when Fragment B is added. Any idea on this? – LoveMeSomeFood Dec 11 '13 at 02:29
  • How you are using polling in FirstFragment ? You have to do that manually as both of the fragments remain in memory.So you can use their instances to perform necessary action.Thats the clue,generate an event in main activity which do something when you add second fragment. Hope this will help. – kaushal trivedi Dec 11 '13 at 06:45
  • 1
    Thank you for the clue =). I have this done. But is this the only way to do it? And an appropriate way? Also When I press home button and launch application again all fragments get active again. Suppose Im here in Fragment B through this way. `Activity A{Fragment A --> Fragment B}` when I launch the application again after pressing home button both fragment's `onResume()` is called and hence they start their polling. How can I control this? – LoveMeSomeFood Dec 11 '13 at 17:50
  • 1
    Unfortunately you can not,System doesn't work in normal behaviour in this way,it will consider both fragment as direct child of activity.Though it served the purpose of maintaining the fragment state,other normal things get very hard to manage.Lately i discovered all this issues,now my suggestion is to not go for this way.sorry. – kaushal trivedi Dec 12 '13 at 05:03
  • NP.Thank you for the help! – LoveMeSomeFood Dec 12 '13 at 14:46
  • Also adding too many fragments, lets say 10 for example (hiding the previous) without replacing will that cause memory issues? – LoveMeSomeFood Dec 12 '13 at 14:49
  • 1
    Of course,in last i will say not to go with this approach until you find any other solution.Because it is hard to manage. – kaushal trivedi Dec 15 '13 at 11:55
  • OK.. Thank you so much!! – LoveMeSomeFood Dec 16 '13 at 15:05
  • Thanks for this, I knew i was missing something, Thanks again. – n j Nov 27 '15 at 06:10
8

I would suggest a very simple solution.

Take the View reference variable and set view in OnCreateView. Check if view already exists in this variable, then return same view.

   private View fragmentView;

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

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }
You Qi
  • 5,401
  • 7
  • 42
  • 54
Mandeep Singh
  • 651
  • 7
  • 12
  • 1
    There is a chance of memory leak if we didn't remove 'fragmentView' variable in onDestroy() – Arun P M Apr 15 '20 at 12:18
  • @ArunPM so how to remove fragmentView in onDestroy() ? `if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }` is appropriate to clear memory ? – Mehmet Gür Apr 16 '20 at 00:20
  • @Mandeep Singh I'm using your solution because it's really very simple. But please can you tell me this is reliable solution ? Have you ever face a problem with using this way ? Thanks. – Mehmet Gür Apr 16 '20 at 00:26
  • 1
    @MehmetGür i am using this solution many time. Till now i did not get any memory leak error. But you can use ArunPM solution with that if you want. I think he is telling to set fragmentView to null in OnDestroy() Method. – Mandeep Singh Apr 17 '20 at 10:47
  • 1
    I'm using [LeakCanary](https://square.github.io/leakcanary/) for detecting memory leaks and its throwing leak issues when I followed this method. But as @Mandeep Sigh mentioned in the comment we can overcome this issue by assigning `null` to `fragmentView` variable in `onDestroy()` method. – Arun P M Apr 17 '20 at 14:16
  • 1
    As per my knowledge, when a fragment getting destroys, the view associated with the fragment is cleared in `onDestroyView()`. This clearing is not happening for our backup view variable (here `fragmentView `) and it will cause memory leak when the fragment back stacked/destroyed. You can find the same reference in [Common causes for memory leaks] (https://square.github.io/leakcanary/fundamentals/#common-causes-for-memory-leaks) in LeakCanery introduction. – Arun P M Apr 17 '20 at 14:42
  • @MandeepSingh @Arun P M thanks for answers! I used this way in onDestroy(): `if (view.getParent() != null) { ((ViewGroup)view.getParent()).removeView(view); }` – Mehmet Gür Apr 17 '20 at 18:57
  • Is My way true or I must this: `view=null` ? And also I'm not sure using this in onDestroy() instead of onDestroyView(). I couldn't understand which method more appropriate for this situation. @Arun PM – Mehmet Gür Apr 17 '20 at 19:02
  • Hi @MehmetGür, the leaking mainly happens when you put your fragment into the back stack. You can simply null out your view references like `onDestroy(){fragmentView = null }`. Please check this [stack overflow answer](https://stackoverflow.com/a/59504797/703646) for some more insights. – Arun P M Apr 19 '20 at 10:02
7

I came across this problem in a Fragment containing a map, which has too many setup details to save/reload. My solution was to basically keep this Fragment active the whole time (similar to what @kaushal mentioned).

Say you have current Fragment A and wants to display Fragment B. Summarizing the consequences:

  • replace() - remove Fragment A and replace it with Fragment B. Fragment A will be recreated once brought to the front again
  • add() - (create and) add a Fragment B and it overlap Fragment A, which is still active in the background
  • remove() - can be used to remove Fragment B and return to A. Fragment B will be recreated when called later on

Hence, if you want to keep both Fragments "saved", just toggle them using hide()/show().

Pros: easy and simple method to keep multiple Fragments running
Cons: you use a lot more memory to keep all of them running. May run into problems, e.g. displaying many large bitmaps

Teo Inke
  • 5,513
  • 3
  • 34
  • 36
  • can you please tell me when we remove fragment b and return to A then which method is called in Fragment A ? i want to take some action when we remove the fragment B. – Google Mar 22 '17 at 09:40
5

onSaveInstanceState() is only called if there is configuration change.

Since changing from one fragment to another there is no configuration change so no call to onSaveInstanceState() is there. What state is not being save? Can you specify?

If you enter some text in EditText it will be saved automatically. Any UI item without any ID is the item whose view state shall not be saved.

Marcel Bro
  • 4,356
  • 3
  • 37
  • 62
user2779311
  • 1,390
  • 3
  • 18
  • 27
0

Here, since onSaveInstanceState in fragment does not call when you add fragment into backstack. The fragment lifecycle in backstack when restored start onCreateView and end onDestroyView while onSaveInstanceState is called between onDestroyView and onDestroy. My solution is create instance variable and init in onCreate. Sample code:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

And check it in onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}
Ling Boo
  • 121
  • 5
0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
Hardik Vasani
  • 828
  • 1
  • 8
  • 13
0

My problem was similar but I overcame me without keeping the fragment alive. Suppose you have an activity that has 2 fragments - F1 and F2. F1 is started initially and lets say in contains some user info and then upon some condition F2 pops on asking user to fill in additional attribute - their phone number. Next, you want that phone number to pop back to F1 and complete signup but you realize all previous user info is lost and you don't have their previous data. The fragment is recreated from scratch and even if you saved this information in onSaveInstanceState the bundle comes back null in onActivityCreated.

Solution: Save required information as an instance variable in calling activity. Then pass that instance variable into your fragment.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

So following on with my example: before I display F2 I save user data in the instance variable using a callback. Then I start F2, user fills in phone number and presses save. I use another callback in activity, collect this information and replace my fragment F1, this time it has bundle data that I can use.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

More information about callbacks can be found here: https://developer.android.com/training/basics/fragments/communicating.html

Dodi
  • 1,812
  • 3
  • 22
  • 36
0

first: just use add method instead of replace method of FragmentTransaction class then you have to add secondFragment to stack by addToBackStack method

second :on back click you have to call popBackStackImmediate()

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};
Community
  • 1
  • 1
Milad Ahmadi
  • 571
  • 7
  • 15
0

Replace a Fragment using following code:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

Activity's onBackPressed() is :

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}
Pratibha Sarode
  • 1,583
  • 14
  • 16
0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);
Vishal Yadav
  • 993
  • 3
  • 13
  • 28
  • 1
    Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation would greatly improve its long-term value](//meta.stackexchange.com/q/114762/206345) by showing _why_ this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Machavity Apr 06 '18 at 12:40
0

Perfect solution that find old fragment in stack and load it if exist in stack.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

use it like

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Khemraj Sharma
  • 46,529
  • 18
  • 168
  • 182