2

I'm trying to do something using a boolean in a Fragment class each time the Fragment is displayed.

Example

My app launches, opens the FirstFragment and the boolean for the first time is always true, then I have an if clause that checks its value:

if (FirstTime) {
    FirstTime = false;
} else {
    // Other stuff here, cause it's not true.
}

Then, on the first time, when FirstTime is true, I do stuff like go to another Fragment. and when I return to Fragment1 and on my onCreate(), I do the same. It's always true, seems that it's refreshing or something.

Then I thought that could be a problem with Fragment, and every time I press on Fragment1, it restarts or something. Then, I've added a getter and setter in my MainActivity:

public Boolean getFirstTime() {
    return FirstTime;
}

public void setFirstTime(Boolean FirstTime) {
    this.FirstTime = FirstTime;
}

where since the start, it's true and then, I changed my code from Fragment1 for:

if (((MainActivity) getActivity()).getFirstTime())
    ((MainActivity) getActivity()).setFirstTime(false);
} else {
    // Other stuff here, cause it's not true,
}

However, it's still saying that's true.

What I'm doing wrong or what I misunderstood about Fragments?
Is there any way to do it?

Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148
  • do you have an application class? if so save it there – Elltz May 02 '15 at 20:25
  • @Elltz what do you mean? – Skizo-ozᴉʞS ツ May 02 '15 at 21:35
  • what i mean is you are having boolean problems? then an easy approach is to create a singleton class that hangs along as long as both your fragments and activities are alive, and create the variable in that class, an example is a class that extends Application – Elltz May 03 '15 at 00:38

2 Answers2

5

You have made an assumption that the Fragment instance remains in existence as long as the app is alive. It is a reasonable assumption, and your approach would work fine if that assumption were true.

Unfortunately, a Fragment is destroyed when it recedes into the background, and created anew when it returns to the foreground. This is why it appears to "refresh". The same is not true of an Activity. When an Activity recedes into the background, it is not destroyed immediately. Rather, it is maintained on the current task's backstack for some time, and if it returns to the foreground, it is the same instance.

To combat this problem, there are four different ways:

  • Declare FirstTime as static. This should work. I've used this before. However, this should only be used in extreme cases when there is an absolute necessity to preserve the value of a member field, and only when no other way is available. Making a variable static leads to a classic memory leak.
  • Save the value of FirstTime in your Fragment using onSaveInstanceState():

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("FirstTime", FirstTime);
    }
    

    and retrieve the value in onCreate():

    @Override
    public void onCreate (Bundle savedInstanceState){
        super.onCreate();
        FirstTime = savedInstanceState.getBoolean("FirstTime");
    }
    
  • Declare FirstTime in a global constants class instead of putting it in the Fragment:

    public class GlobalConstants{
    
        public static boolean FirstTime = true;
        // other global constants ...
    
    }
    

    and access it in your Fragment like this:

    if (GlobalConstants.FirstTime) {
        GlobalConstants.FirstTime = false;
    } else {
        //Other stuff here cause it's not true
    }
    
  • Save the value of FirstTime in a SharedPreference:

    SharedPreferences sp = getActivity().getPreferences(Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sp.edit();
    editor.putBoolean("FirstTime", FirstTime);
    editor.commit();
    

    and retrieve its value in this way:

    SharedPreferences sp = getActivity().getPreferences(Context.MODE_PRIVATE);
    FirstTime = sp.getBoolean("FirstTime", true);
    

The first three methods will maintain the value of FirstTime while the application is alive. The fourth method will preserve the value of FirstTime beyond the lifetime of the application, i.e. when the app restarts, FirstTime will be true or false depending on what its value was last set before the app exited.

References:

1. Handling the Fragment Lifecycle.

2. Saving Key-Value Sets.

3. Visibility and Lifetime.

EDIT:

To understand how to use onSaveInstanceState(), see the following links:

1. Saving (and Retrieving) Android Instance State.

2. Once for all, how to correctly save instance state of Fragments.

3. Handling Configuration Changes.

It is confusing, but it will be useful to you once you understand it.

Community
  • 1
  • 1
Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
  • I've tried the GlobalConstant method and it still going throught the if, and the onSaveInstance method I don't get how can I do it. Shall I put the FirstTime like Boolean FirstTime = true; and then do the stuff? – Skizo-ozᴉʞS ツ May 03 '15 at 12:43
  • @Skizo: you are always welcome ... we are all here to help each other :) ... I too need some guidance, for example, see [this question](http://stackoverflow.com/questions/29911046/android-zoom-animation-using-animatorset) and [this question](http://stackoverflow.com/questions/29276666/send-a-message-to-a-facebook-friend-from-an-android-app) which I have posted, and I have not received a satisfactory answer for either of them, perhaps you or some of your friends can help me, I'd be most grateful :) – Yash Sampat May 03 '15 at 16:17
  • @Skizo: also, see edited answer above, it may be useful for understanding how to save instance state of `Fragment` and `Activity` ... best :) – Yash Sampat May 03 '15 at 16:18
  • That's really very nice of you, thank you so much :) Why didn't I think of that ? – Yash Sampat May 04 '15 at 15:27
  • @Skizo: I did look at it. I have an idea: can you take a log of the URL that you create in your Java code for the delete operation, and compare it with the URL that you use in Postman ? Are they the same ? Let me know ... – Yash Sampat May 05 '15 at 19:21
  • Yes that does look like a good answer, I plan to try it out soon, thanks again for your selflessness ... :) – Yash Sampat May 06 '15 at 15:12
  • As I said, you deserve it and much more :P Have fun, and let me know when you've got a good answer, also if you need a correct answer in your other question I'll put a bounty too ^^ – Skizo-ozᴉʞS ツ May 06 '15 at 15:22
  • No don't worry my friend, I'll put a bounty on it, funny why I never thought of that. That other question is not important at the moment ... :) – Yash Sampat May 06 '15 at 15:36
  • Okay then, I hope the best of luck for you ^^ – Skizo-ozᴉʞS ツ May 06 '15 at 15:40
  • Can I ask to you something? it's about an error like this (java.lang.IllegalArgumentException: No view found for id 0x7f090022 (info.androidhive.slidingmenu:id/frame_container)) and I don't know what happens this, I think it's at the time I do the replace... – Skizo-ozᴉʞS ツ May 07 '15 at 15:53
  • `No view found for id` means that when you call `findViewById()`, that particular `View` is not present in your current layout. Without more detail, it is hard to tell what exactly the problem is ... – Yash Sampat May 07 '15 at 17:05
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/77260/discussion-between-skizo-and-y-s). – Skizo-ozᴉʞS ツ May 07 '15 at 22:27
1

Simple solution, set your boolean to a static.

This of course does not comply to the good practices of programming.

To answer you question more directly, I'm assuming that the fragments and activities get destroyed and new instances are created, hence the boolean being set to true again. As such, by making the variable static, it's state will remain across all instances of that class.

Alex-v-s
  • 187
  • 1
  • 14