Fragment - getArguments() returns an empty bundle
Asked Answered
A

7

6

Hope somebody can help me to understand this:

  1. I am using a single-activity app and lots of Fragments that are replaced in the same container, and I am testing my app in a real device with the "Don't keep activities" option enabled

  2. When a new fragment is added (using FragmentTransaction replace() method), I am using the setArguments() method to pass information to the new Fragment. It works as expected and I can get that information with getArguments() inside that Fragment. Everything ok so far ...

  3. After this, I send my app to background. I see that all the fragments in the stack are being destroyed, again as expected

  4. I bring my app to foreground and in the getArguments() method I am getting an empty Bundle (not null, just an empty Object) instead of the one with the data I used in #2

According to Android documentation, the arguments supplied in setArguments() will be retained across fragment destroy and creation ... So, my questions are:

  1. Does the "will be retained across fragment destroy and creation" includes the scenario I described?

  2. Does the "Don't keep activities" option can mess up with getArguments()/setArguments() if it is enabled?

  3. Is there a way to test proper fragment creation/destroy besides the "Don't keep activities" option?

  4. What is the better approach to properly keep fragment's arguments "alive"? I could save them in the onSaveInstanceState() method, but would like to know if there are more options besides that.

Ashkhabad answered 24/4, 2017 at 20:48 Comment(5)
Have you tried support fragments? At least they behave consistently across all platforms. It may be easier to troubleshoot.Gerdi
@EugenPechanec I am using support fragmentsAshkhabad
would you mind sharing some code?Malik
@Reyansh Mishra I can't, since the project is from work, not mine. I will try to create a sample project with only the fragment transaction feature to verify the getArguments issueAshkhabad
that's so weird, I'm facing the same issue.Addicted
G
3

Activity and Fragment recreation is one of the points our team provide special attention. So this is some bullets we have in mind.

  1. Does the "will be retained across fragment destroy and creation" includes the scenario I described?

Take in mind "Don't keep Activities" is destroying your Activity when you go to background. Later the system will try to recover your last state recreating the last Activity and its fragments automatically. Android will save the necessary information to recover its last state. So yes, your scenario should be covered.

  1. Does the "Don't keep activities" option can mess up with getArguments()/setArguments() if it is enabled?

Depending on your flow you could experiment some problems. When activity recreates savedInstanceState on the onCreate method will be not null. You should use this information to avoid recreating or reattach your fragment. The system will try to recover it for you, this is the reason why fragments must not have any constructor.

  1. Is there a way to test proper fragment creation/destroy besides the "Don't keep activities" option?

Using FragmentTransaction.

1 - Use FragmentTransaction to add your fragment to your activity without adding it to the backstack.

2 - Use FragmentTransaction to replace the previous fragment with other fragment (or maybe a new instance of the previous fragment). When a fragment is replaced by other and it is not added to the back stack android destroys it.

  1. What is the better approach to properly keep fragment's arguments "alive"? I could save them in the onSaveInstanceState() method, but would like to know if there are more options besides that.

Probably you don't need to keep the arguments bundle in your code. Android will do it for you. But it is a good practice to recover the bundle data in onAttach method (first method called when the fragment is going to be available on the screen) and store them as class attributes for later use.

Galitea answered 5/5, 2017 at 11:20 Comment(1)
I am using FragmentTransaction.replace when changing fragments. I also use addToBackStack method. Right now, I am using the getArguments in the onCreateView method. I'll try to move it to onAttach and test your suggestionAshkhabad
A
3

I guess the problem is that you create a new fragment instance every time your activity's onCreate is called. Assuming your current code looks like this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...
    ...
    fragment = SampleFragment.newInstance("sample");
    getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.container, fragment, "sample_fragment_tag")
            .commit();
    ...
}

So every time your activity is recreated, there is a new instance of fragment is created and attached to the activity. You should avoid this and create a new instance of the fragment only if savedInstanceState is null, meaning your activity has been just created. Otherwise, the saved fragment instance will be restored with its arguments along with activity instance:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...
    ...
    if (savedInstanceState == null) {
        fragment = SampleFragment.newInstance("sample");
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.container, fragment, "sample_fragment_tag")
                .commit();
    } else {
        fragment = (SampleFragment) getSupportFragmentManager().findFragmentByTag("sample_fragment_tag");
    }
    ...
}

Hope, this is what you're looking for.

Anders answered 29/4, 2017 at 8:36 Comment(3)
I already have the savedInstanceState != null line in my code to prevent what you described. It works because the fragment stack is the same it was when the activity was destroyed, only that the fragment on top has the empty bundle in getArguments()Ashkhabad
when within the lifecycle do you call getArguments? Can you post some code?Jemina
@BjörnKechel I can't, since the project is from work, not mine. I will try to create a sample project with only the fragment transaction feature to verify the getArguments issueAshkhabad
G
3

Activity and Fragment recreation is one of the points our team provide special attention. So this is some bullets we have in mind.

  1. Does the "will be retained across fragment destroy and creation" includes the scenario I described?

Take in mind "Don't keep Activities" is destroying your Activity when you go to background. Later the system will try to recover your last state recreating the last Activity and its fragments automatically. Android will save the necessary information to recover its last state. So yes, your scenario should be covered.

  1. Does the "Don't keep activities" option can mess up with getArguments()/setArguments() if it is enabled?

Depending on your flow you could experiment some problems. When activity recreates savedInstanceState on the onCreate method will be not null. You should use this information to avoid recreating or reattach your fragment. The system will try to recover it for you, this is the reason why fragments must not have any constructor.

  1. Is there a way to test proper fragment creation/destroy besides the "Don't keep activities" option?

Using FragmentTransaction.

1 - Use FragmentTransaction to add your fragment to your activity without adding it to the backstack.

2 - Use FragmentTransaction to replace the previous fragment with other fragment (or maybe a new instance of the previous fragment). When a fragment is replaced by other and it is not added to the back stack android destroys it.

  1. What is the better approach to properly keep fragment's arguments "alive"? I could save them in the onSaveInstanceState() method, but would like to know if there are more options besides that.

Probably you don't need to keep the arguments bundle in your code. Android will do it for you. But it is a good practice to recover the bundle data in onAttach method (first method called when the fragment is going to be available on the screen) and store them as class attributes for later use.

Galitea answered 5/5, 2017 at 11:20 Comment(1)
I am using FragmentTransaction.replace when changing fragments. I also use addToBackStack method. Right now, I am using the getArguments in the onCreateView method. I'll try to move it to onAttach and test your suggestionAshkhabad
A
1

Thanks all for your answers. I still don't know why I am having this issue. I created a sample project to test only the fragment transactions and post it here as @Reyansh and @Björn asked. It is a very simple project and - guess what - I can't reproduce the issue in that project: the getArguments() method delivers the same Bundle each time the activity is recreated. So, it has to be something else in my project that is causing this weird behavior.

I decided to mark @jDur answer as the right one because it provides a good explanation to my questions.

Ashkhabad answered 5/5, 2017 at 15:11 Comment(0)
W
0

Well as I know dont keep activities option is provided to test your app in a low memory condition and when other resources are not available to serve your app so Android OS killed your app. At that time you get a chance to save data by using onSavedInstanceState and in fragments you may use setRetainState(true) From my experience for saving bundle data is to use sharedPreferences. SharedPreference never get destroyed untill you uninstall or clear the app data from settings or remove it by SharedPreference Editor programmatically. One thing is clear that if the activity is destroyed then definitely its all Fragments will surely get destroyed. Hope you got my point.

Wilds answered 28/4, 2017 at 15:39 Comment(3)
according to Android documentation, the arguments supplied in setArguments() will be retained across fragment destroy and creation ... Still does not explain why in this scenario, after activity and fragment destruction, getArguments is not delivering the same Bundle that was used in setArguments().Ashkhabad
setRetainInstance works when configuration changes. It doesn't help when the activity is killed, which is simulated by the Don't keep activities option. In all cases you should be using onSaveInstanceState. And attached fragments are indeed saved as well. But this is unrelated to the issue at hand.Gerdi
Please share your code. How you passing data in fragment?Breezy
L
0
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/*Created by Foolish_Guy on 4/29/2017.*/                 

public class TestFragment extends Fragment {
private static final String USER_ID = "param1";
private static final String ARG_PARAM2 = "param2";

String userID;
String mParam2;

public static TestFragment newInstance(String param1, String param2) {
    TestFragment fragment = new TestFragment();
    Bundle args = new Bundle();
    Log.e("Data :", String.valueOf(param1));
    args.putString(USER_ID, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

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

    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        userID = getArguments().getString(USER_ID);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_layout, container, false);
    return view;
}

}

When you initialize the fragment You need to supply the required arguments. You are initializing it by calling

TestFragment frag = TestFragment.newInstance("UID", "TEXT");

Once this is called the Bundle arguments will be set

Next in onCreate (Bundle savedInstance) will be called, Now Bundle would not be empty.

Layamon answered 2/5, 2017 at 19:21 Comment(1)
this is what I am doingAshkhabad
P
0

onSaveInstanceState is the best place to save data when the app is destroyed but still in the recent apps list. You save just enough to rebuild how your app looked when it was sent to the background and thats where the user left your app. Users puts app in background for two or three weeks then brings it to the front and smiles it remembered where it was.

In preferences saving state will make the user of your app throw their phone at a wall. They will clear the app from recent apps then reboot then unistall your app permanently because it didn't start at the beginning that one time it was removed from the recent apps and they expected the app to start at the beginning. Did I loose you yet?

Pyelitis answered 2/5, 2017 at 20:48 Comment(3)
I am aware of onSaveInstanceState. The thing is I am just confused why I get an empty bundle in getArguments instead of the one with the data I put in setArgumentsAshkhabad
It's different than get arguments. Only thoroughbred Android apps use onsaveinstancestate You'll have ask a new question if you can't figure out how to usePyelitis
I also use onSaveInstanceState, but this question is about an issue I found out with my app using a real device (Nexus 6 with Android 6.0) with the getArguments methodAshkhabad
M
0
setRetainInstance(true);

In onCreate method of fragment. Before adding or replacing fragment get from SupportFragmentManager by tag, if fragment value is null add or replace by new fragment. like

Fragment fragment = fragmentManager.findFragmentByTag(tag);
if(fragment==null){
 fragmentManager.beginTransaction().replace(...
}
Mcmaster answered 5/5, 2017 at 12:25 Comment(1)
I am using fragments in the back stack. setRetainInstance does not work in that scenarioAshkhabad

© 2022 - 2024 — McMap. All rights reserved.