4

I have an app with a Home screen that has 2 fragments (for now) and a navigation drawer. Currently I load the fragment A (Explore) on startup and load fragment B when clicked. From then on, I show and hide fragments. It's faster than recreating fragments on every click and my fragment A takes some time to load.

I've noticed that when I go to fragment B and go to another activity (let's call it activity 2) from there and leave the app and wait for it to be killed (or do something crazy like change the device language), and then come back to the same activity, it's still there. When I press back to go back to fragment B, sometimes (50% of times) the fragment B is drawn over fragment A. On clicking fragment A in the drawer, fragment A appears fine, but on clicking fragment B, there's another instance of fragment A and on top of that fragment B.

I've spent more than 2 days on this problem and got nowhere.

Here's my code for selecting the fragment:

private void selectItem(int position, boolean addExploreFragment) {
    Log.d(tag, "selectItem: " + position);

    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

    //add explore fragment - this is called on app startup, but also when the app is killed and resumed which results in 2 explore fragments

    if (addExploreFragment){

        fragmentTransaction.replace(R.id.content_frame, mExploreFragment, EXPLORE_FRAGMENT_TAG);
        Log.d(tag, "Replaced frame and added "+ mFragmentTags[position]);

    } else {
        //add fragment for the first time
        if (getSupportFragmentManager().findFragmentByTag(mFragmentTags[position]) == null && position != 0) {
            fragmentTransaction.add(R.id.content_frame, mFragments[position], mFragmentTags[position]);
            Log.d(tag, "Added Fragment: "+ mFragmentTags[position]);
        }
        //shows and hides fragments
        for (int i = 0; i < mFragments.length; i++) {
            if (i == position) {
                fragmentTransaction.show(mFragments[i]);
                Log.d(tag, "Showing Fragment: "+ mFragmentTags[i]);
            } else {
                if (getSupportFragmentManager().findFragmentByTag(mFragmentTags[i]) != null) {
                    fragmentTransaction.hide(mFragments[i]);
                    Log.d(tag, "Hid Fragment: "+ mFragmentTags[i]);
                }
            }
        }

    }

    fragmentTransaction.commit();
    //not null check for calling selectItem(0) before loading the drawer
    if (mDrawerList != null){
        mDrawerList.setItemChecked(position, true);
    }
}

I know for sure, the explore fragment is getting created twice and the two instances behave independently of each other (just sharing).

I'm lost what to do next. This is an issue which can be reproduced very easily on low end devices but on a device like Nexus 4 (my test device), the issue can be reproduced by changing the device language.

Has anyone got any ideas about this? Basically if the addExploreFragment block doesn't get called when there is already an exploreFragment, this issue could be solved, I think, but I've been unable to do so. Also, I tried removing all the fragments and then adding the exploreFragment but same thing happens (50% of times).

Thanks! and sorry for the long post, I felt I should share all the details.

Update: When I change the device language and come back to the app on Activity 2 and go back to Home activity, it has the fragment B open which is good, but fragment A get recreated because it's a heavy fragment and the system probably removed it from memory. Again, that's ok that it gets recreated IF it got removed by the system but why does it get recreated when it's not removed. I believe it's something with my code, on every 2nd attempt (without closing the app) this happens, 2 instances of the heavy fragment A. Out of ideas.

But shouldn't fragmentTransaction.replace remove all the previously added fragments and then add exploreFragment. It's not working like that. Neither fragment A nor Fragment B are getting removed.

Rahul Sainani
  • 3,437
  • 1
  • 34
  • 48
  • yes,but you still missed some details, how is Fragment A declared? the static way? that is is it declared in xml by name? if yes you created the two instances. and also where do you toggle `addExploreFragment`?. – Elltz May 14 '15 at 18:46
  • @Elltz No, the fragments are all created and added dynamically to the frame layout. addExploreFragment is only true on app startup. Everywhere else, selectItem called with that set as false. So basically, when the explore fragment is added. It's a redundant parameter, now that I come to think of it. – Rahul Sainani May 14 '15 at 18:51
  • If I understand right, selectItem() is called within MainActivity. Which override methods is it called from, in context of the lifecycle? I have been staring at the code, and this is the only issue I see. – The Original Android May 14 '15 at 21:01
  • @TheOriginalAndroid It's called on onCreate with addExploreFragment set to true. Other than that, it's called in onItemClick in DrawerItemClickListener to switch fragments, with addExploreFragment set to false. – Rahul Sainani May 15 '15 at 06:29
  • I agree with your code in onCreate. I did post an answer. – The Original Android May 15 '15 at 07:41
  • I posted another answer because an odd thing happened to me with fragments. – The Original Android May 16 '15 at 08:12
  • How are you doing? any progress with your problem? – The Original Android May 26 '15 at 19:17
  • @TheOriginalAndroid Thank you for asking. I've stopped actively trying to solve the issue and looking at ways to restructure my app in the upcoming updates so this does not affect my app. – Rahul Sainani Jun 03 '15 at 08:54

2 Answers2

1

I found out something new and rather odd to me. When you use fragmentTransaction.add, the listeners you have, like DrawerItemClickListener, on the previous fragment, are still active. And this is even if you use fragmentTransaction.commit.

So...I suspect when the add method is used, you actually clicked on another hidden button or hidden UI that has an event listener on the previous fragment. I don't like this of course and the effect may be very confusing. Yes, this happened to me and I didn't understand why for a while.

For now, I think the easiest code fix would be to use the replace method instead of add. The replace() makes listeners inactive. If it works, then you can make a better/elegant fix.

Let me know what happens....

The Original Android
  • 6,147
  • 3
  • 26
  • 31
  • The listener is attached to the activity so if you're seeing this behaviour, it's really odd. Did you try by enabling logging on fragmentManager. It gave me some insights, confirmed some doubts and created more but there's no solution yet. I can't use replace now because the explore fragment takes time to load. I'm thinking of fixing that and using replace though. But that could take a few updates to the app. Thanks for all your help though. – Rahul Sainani May 20 '15 at 13:34
0

I started to notice your post

when I go to fragment B and go to another activity

When you interact or start another Activity, you start a new set of Fragments. Look at this Google webpage @ Fragments Lifecycle. For clarification of my claim, there is a quote saying

A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle.

You might as well read few paragraphs of it, at least.

I am not sure what your solution should be. Perhaps make the fragments distinctive, different and clear between the two Activities you have.

The Original Android
  • 6,147
  • 3
  • 26
  • 31
  • Thanks for the post but this does not explain why fragment A gets recreated 50% of times when the app is killed (edited the post to include "50% of times"). Fragments A and B are in one activity (let's call it Activity 1) and from fragment B, when I go to another activity (Activity 2) and then press the Home button on the phone to pause the app and change the phone locale and come back to the activity 2, then go back to activity 1 containing 2 fragments, the thing I mentioned in the post happens. Let me know if I missed your point. – Rahul Sainani May 15 '15 at 07:54