PROBLEM:
I have an Android application that allows a user to browse to a user\'s profile ViewProfileFragment
. Inside ViewProfile
It turns out that fragments share the same lifecycle as their parent activity. According to the Fragment documentation:
A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle. For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments. However, while an activity is running (it is in the resumed lifecycle state), you can manipulate each fragment independently.
So the step that you took to clean up some resources in onPause()
of the fragment wouldn't trigger unless the parent activity pauses. If you have multiple fragments that are being loaded by a parent activity then most likely you are using some kind of mechanism for switching which one is active.
You might be able to solve your issue by not relying on the onPause
but by overriding setUserVisibleHint on the fragment. This gives you a good place to determine where to do your setup of resources or clean up of resources when the fragment comes in and out of view (for example when you have a PagerAdapter that switches from FragmentA to FragmentB).
public class MyFragment extends Fragment {
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
//you are visible to user now - so set whatever you need
initResources();
}
else {
//you are no longer visible to the user so cleanup whatever you need
cleanupResources();
}
}
}
As was already mentioned you are stacking items up on a backstack so it's expected that there will be at least a little bit of a memory footprint but you can minimize the footprint by cleaning up resources when the fragment is out of view with the above technique.
The other suggestion is to get really good at understanding the output of the memory analyzer tool (MAT) and memory analysis in general. Here is a good starting point. It is really easy to leak memory in Android so it's a necessity in my opinion to get familiar with the concept and how memory can get away from you. It's possible that your issues are due to you not releasing resources when the fragment goes out of view as well as a memory leak of some kind so if you go the route of using setUserVisibleHint
to trigger cleanup of your resources and you still see a high-volume of memory being used then a memory leak could be the culprit so make sure to rule them both out.
It's hard to see the whole picture (even tho you have shown us a lot of information), without concrete access to your source code, which I'm sure it would be impractical if not impossible.
That being said, there are a few things to keep in mind when working with Fragments. First a piece of disclaimer.
When Fragments were introduced, they sounded like the best idea of all times. Being able to display more than one activity at the same time, kinda. That was the selling point.
So the whole world slowly started using Fragments. It was the new kid on the block. Everybody was using Fragments. If you were not using Fragments, chances were that "you were doing it wrong".
A few years and apps later, the trend is (thankfully) reverting back to more activity, less fragment. This is enforced by the new APIs (The ability to transition between activities without the user really noticing, as seen in the Transition APIs and such).
So, in summary: I hate fragments. I believe it's one of the worst Android implementations of all time, that only gained popularity because of the lack of Transition Framework (as it exists today) between activities. The lifecycle of a Fragment is, if anything, a ball of random callbacks that are never guaranteed to be called when you expect them.
(Ok, I am exaggerating a little bit, but ask any Android seasoned developer if he had trouble with Fragments at some point and the answer will be a resounding yes).
With all that being said, Fragments work. And so your solution should work.
So let's start looking at who/where can be keeping these hard references.
note: I'm just gonna toss ideas out here of how I would debug this, but I will not likely provide a direct solution. Use it as a reference.
WHAT IS GOING ON?: You're adding fragments to the Backstack. The backstack stores a hard reference to the Fragment, not weak or soft. (source)
Now who stores a backstack? FragmentManager and… as you guessed, it uses a hard live reference as well (source).
And finally, each activity contains a hard reference to the FragmentManager.
In short: until your activity dies, all the references to its fragments will exist in memory. Regardless of add/remove operations that happened at Fragment Manager level / backstack.
WHAT CAN YOU DO? A couple of things come to my mind.
Try using a simple image loader/cache lib like Picasso, if anything to make sure that images are not being leaked. You can later remove it if you want to use your own implementation. For all its flaws, Picasso is really simple to use and has come to a state where it deals with memory "the right way".
After you have removed the "I may be leaking bitmaps" problem out of the picture (no pun intended!), then it's time to revisit your Fragment lifecycle. When you put a fragment in the backstack, it's not destroyed, but… you have a chance to clear resources: Fragment#onDestroyView()
is called. And here is where you want to make sure that the fragment nullifies any resources.
You do not mention if your fragments are using setRetainInstance(true)
, be careful with that, because these do not get destroyed/recreated when the Activity is destroyed/recreated (e.g.: rotation) and all the views may be leaked if not properly handled.
StoryViewFragment
.All in all, these are all suggestions that came from my experience, but it's really hard to help you unless we can see more in detail.
Hopefully it proves to be a starting point.
Best of luck.