Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fragment - getArguments() returns an empty bundle

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.

like image 305
allo86 Avatar asked Apr 24 '17 20:04

allo86


3 Answers

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.

like image 157
jDur Avatar answered Nov 16 '22 00:11

jDur


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.

like image 24
romtsn Avatar answered Nov 15 '22 23:11

romtsn


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.

like image 1
allo86 Avatar answered Nov 16 '22 01:11

allo86