The idea of “organizing a single activity into separate, discrete pieces” was absolutely great. However, judging by the state of the Fragments today (pun intended), the initial implementation and the evolution of this API were very unfortunate.

As of today, Fragments are among the most controversial Android APIs. Many professional Android developers will not use Fragments in their projects under any circumstances. Not because they don’t understand the benefits of Fragments, but because they clearly see Fragments drawbacks and limitations. These developers do have a point.

It’s simply impossible to overstate the complexity of Fragment lifecycle. Just take a look at this diagram. It’s intimidating. Fortunately, you don’t need to understand all of the tricky lifecycle transitions to use Fragments in your apps.

In this post I will describe my approach to handling of Fragment’s lifecycle which hides most of its complexity. I’ve been using this approach for a couple of years now, and it really works.

Activity Lifecycle

As you’ll see shortly, when I use Fragments I try to decouple them from Activity lifecycle as much as possible. However, this doesn’t change the fact that there are many similarities between them.

As I said, Activity’s and Fragment’s lifecycles are similar in many ways. So, in this post I’ll draw lots of analogies between them. However, I don’t want to repeat myslef. Therefore, I’ll assume that you already read that article about Activities. If you haven’t, please do it now before you proceed with this post.

My Take on Fragment Lifecycle

My approach to Fragment’s lifecycle aims to achieve two goals:

Make Fragment’s lifecycle handling logic as similar as possible to Activity’s logic.

By handling Fragment’s lifecycle similar to Activity’s lifecycle I greatly reduce the overall complexity of the application. Developers will need to learn just one approach instead of two different ones. This means less effort during development, easier maintenance and quicker ramp-up of new team members. I’m also completely sure that doing so reduces risk of bugs, though that’s subjective.

Making Fragment independent of Activity’s lifecycle goes back to the diagram of their respective lifecycles. Each of them is extremely complex, but still manageable in isolation. However, thinking about both lifecycles and the inter-dependencies between them is beyond what I’m capable of.

By satisfying this two goals I greatly reduce the complexity associated with Fragments thus making them more attractive.

onCreate(Bundle)

Remember that you don’t have access to Activity’s constructor so you can’t use it to inject dependencies into Activity? Well, the good news is that Fragment does have a public constructor and we can even define additional ones. The bad news is that doing so will lead to serious bugs so we can’t really do it.

Android will destroy and then re-create Fragments during so called save & restore flow. The re-creation mechanism uses reflective calls to Fragment’s no-arguments constructor. So, if you used constructor with arguments to instantiate the Fragments in the first place and passed dependencies into it, all of these dependencies will be set to null after save & restore. Not cool.

Therefore, just like with Activities, you’ll use onCreate(Bundle) method as a replacement for constructors. Dependency injection and initialization of Fragment’s members takes place here.

However, there is one major difference form Activity’s onCreate(Bundle). You must not touch or do anything related to Android Views in Fragment’s onCreate(Bundle). This is very important. The reason for that will become clear in the next section.

Yeah, forgot about it – the casting of Activities into listener interfaces also takes place in onCreate(Bundle). Feel free to throw a more meaningful exception than the generic ClassCastException if you prefer it that way.

View onCreateView(LayoutInflater, ViewGroup, Bundle)

This method is unique to Fragment and it’s the most prominent difference between Activity and Fragment lifecycle.

I will get a bit ahead of myself and tell you that this method is also the “root of all evil” associated with Fragments. I will discuss this “evil” later, but just keep in mind that if you’re using Fragments, you better not underestimate the complexity of View onCreateView(LayoutInflater, ViewGroup, Bundle).

So, what’s the story?

Activities operate under assumption of being associated with a single View hierarchy for the entire lifecycle. You initialize this hierarchy in Activity’s onCreate(Bundle) and it stays attached to it until garbage collected. You can manually change the composition of Activity’s View hierarchy, but Android will not do anything like that on your behalf.

Fragments, on the other hand, operate under assumption that Android should be capable of destroying and re-creating the View hierarchy associated with a specific Fragment instance. In other words, Fragments can have multiple View hierarchies during their lifecycle and it’s up to Android to decide when the replacement takes place.

Given that View hierarchy can be replaced at runtime, it should be clear now why you mustn’t touch it in Fragment’s onCreate(Bundle). This method will be called just once after the fragment is attached to the enclosing Activity, therefore it can’t support the dynamic nature of Fragment’s View hierarchy.

Enter View onCreateView(LayoutInflater, ViewGroup, Bundle).

This method will be called by Android every time a new View hierarchy needs to be created. Your job is to create and initialize View hierarchy to the correct state, and then return it from this method. Android will take care of it from there.

The main rule for implementations of this method is: all Fragment’s members holding references to objects related to View hierarchy must be assigned inside View onCreateView(LayoutInflater, ViewGroup, Bundle). In other words, if Fragment holds references to Views or related objects in it’s fields, make sure that all these fields are assigned in this method. This is really important.

All in all, the general form of View onCreateView(LayoutInflater, ViewGroup, Bundle) should be something along these lines:

Another interesting thing about this method is that it also receives Bundle with saved state. Honestly, I find it troubling. It’s like the developers weren’t sure where exactly the state should be restored, so they injected this Bundle into several methods for us to figure it out ourselves.

Don’t restore state in this method. I will explain why when we talk about onSaveInstanceState(Bundle).

There is no risk of memory leaks if you follow the rules I share in this article. In fact, part of the reasons I use this approach is to mitigate Fragment’s intrinsically higher risk of memory leaks.

The rule for View onCreateView(LayoutInflater, ViewGroup, Bundle) is that every Fragment’s field related to View hierarchy must be assigned in this method. This includes list Adapters, user interaction listeners, etc. The only way to keep Fragment’s code maintainable and free of tricky corner cases is to ensure that this method re-creates the entire View hierarchy and all the associated objects from scratch.

onStart()

This method in Fragment has exactly the same responsibilities and guidelines as Activity’s onStart(). You can read about it my previous article about Activity’s lifecycle.

So, the general form of Fragment’s onStart() will be something like this:

The interesting and surprising part here is unregistration of the click listener. I explained why you might want to do that in the post about Activity lifecycle, so I won’t repeat myself.

However, I would like to use this opportunity to address the question I’ve been asked in context of the previous post: is this unregistration mandatory?

Well, to the best of my knowledge, absolute majority of Android applications don’t do it and are still doing fine. So, I think it’s not mandatory. That said, if you don’t do it you can expect to see a certain amount of errors and crashes due to spurious notifications if you get to scale.

So, not mandatory, but it’s not like it’s a premature optimization. We do know that this is rare, but real issue.

I try, as much as possible, to use my own MVC architectural pattern for organization of applications’ presentation layer. In this approach I can “disconnect” from UI by writing just a single line of code. Therefore, if I get to use this pattern, I always disconnect the UI in onStop().

onDestroyView()

You should not override this method in absolute majority of the cases. I guess some of the readers will be surprised to read this, but I really mean it.

As I said, you must assign all Fragment’s fields which are Views in View onCreateView(LayoutInflater, ViewGroup, Bundle). This requirement stems from the fact that Fragment’s View hierarchy can be re-created, so any View reference you don’t initialize in that method will be null. Doing this is very important because otherwise you might get some really nasty bugs and crashes.

If you do that, Fragment will hold strong references to these Views until View onCreateView(LayoutInflater, ViewGroup, Bundle) will be called the next time, or the entire Fragment is destroyed. There is no risk of memory leak or any other potential issue that I’m aware of.

Now, there is a widespread complementary recommendation that you should set all the aforementioned references to View fields to nulls in onDestroyView(). The motivation is to release these references as quickly as possible to allow garbage collector to claim them right after onDestroyView() returns. This will free the memory associated with these Views much earlier.

While the above explanation sounds reasonable, it’s a classic case of premature optimization. You don’t really need this optimization in absolute majority of the cases. Therefore, there is no need to complicate the already complex Fragment lifecycle handling with override of onDestroyView().

So, you don’t need onDestroyView().

onDestroy()

Just like with Activities, there is no need for you to override this method in Fragments.

onSaveInstanceState(Bundle)

The implementation of this method is essentially the same as of its Activity’s counterpart:

I remind you not to be mislead by the seeming simplicity of this method. Incorrect handling of save & restore flow is one of the major causes for bugs and crashes in Android applications. It’s not a coincidence that the discussion of this method was the longest section in my previous article about Activity lifecycle.

So, handling of save & restore is messy in Activities. You would think that it can’t get worse than that. However, surprisingly, it’s even worse with Fragments.

Javadoc of this method was written by Dianne Hackborn back in February 2011 and contains this really scary part:

This corresponds to Activity.onSaveInstanceState(Bundle) and most of the discussion there applies here as well. Note however: this method may be called at any time before onDestroy(). There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.

If this “note” doesn’t make your hair stand on end then you haven’t internalized the understanding of Activity’s and Fragment’s lifecycles yet.

According to official documentation “this method may be called at any time before onDestroy()”. There are two major issues with this.

First of all, as Dianne explains, the View hierarchy associated with Fragment can be destroyed without actually saving the state. Therefore, if you’d restore Fragment’s state in View onCreateView(LayoutInflater, ViewGroup, Bundle), then you would risk overriding the latest state with a stale one. State corruption is a very serious bug. That’s why I told you to restore the state in onCreate(Bundle) only.

The second issue is that if onSaveInstanceState(Bundle) can be called at any time before onDestroy(), then you don’t have guarantees as to when it’s safe to change Fragment’s state (e.g. replace nested Fragments).

Now, I don’t think that this description is accurate. In fact, I think that it had already been wrong when Dianne Hackborn wrote it back in 2011. The notion of “any time” implies some kind of nondeterminism or randomness which I think has never been there. Most probably, there were just several factors that affected this behavior and Dianne decided not to list them because she didn’t think it was important enough.

Well, if that’s the case then she was obviously wrong.

The alternative explanation for this “note” is even less optimistic. If this description was correct back then, it means that googlers who built this framework did not understand that such a behavior implies the two major issues listed above. In particular, it means that Bundle containing the saved state should’ve never been passed into View onCreateView(LayoutInflater, ViewGroup, Bundle) method.

The good news is that this documentation might be inaccurate (surprisingly, inaccurate documentation can be “good news”). From review of some of AOSP and Support Libraries source it looks like the guarantees for onSaveInstanceState(Bundle) in Fragments are basically the same as in Activities.

setRetainInstance(boolean)

If you do, keep in mind that it changes Fragment’s lifecycle. So nothing you read in this article will be guaranteed to work anymore.

Why Fragments are So Complicated

As you can see, Fragments are indeed very complicated beasts. I would even say too complicated.

The biggest issue with Fragments is that their View hierarchy can be destroyed and re-created independently of the Fragment object itself. If that wouldn’t be the case, Fragment’s lifecycle would be almost identical to Activity’s one.

The natural question now would be: what was the reason for this complication? Well, obviously I don’t know the answer and can only speculate based on my limited understanding. Therefore, take the following discussion with a grain of salt.

I think that this mechanism was introduced for optimization of memory consumption. The ability to destroy Fragment’s View hierarchy allows to free some memory while the Fragment is e.g. not visible. But then the question becomes: Fragments implement support for the standard save & restore flow, why did Google invented yet another mechanism for essentially the same purpose?

I don’t know.

What I do know, however, is that even FragmentStatePagerAdapter doesn’t rely on this optimization and forces Fragments to undergo complete save & restore.

As far as I’m concerned, this entire mechanism is a stunning demonstration of the dangers associated with premature optimization. It wasn’t really required, it’s not being useful and it drives the complexity of using Fragments to the astronomic heights.

To me, it looks like all the additional issues that we experienced with Fragments over the years originated form the “root of all evil”:

Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.Knuth, Donald

And the sad irony is that it looks like Google developers themselves don’t understand Fragment’s lifecycle anymore.

Google released LiveData Architecture Component with a serious bug described by Christophe Beyls in this post. If there would be just one developer working on this feature who would really understand Fragment’s lifecycle, that problem would become evident at design stage.

It took Google several months to fix this bug. Lastly, during Google IO 18 they announced that the issue was fixed. The fix was… introduction of yet another lifecycle for Fragment’s View hierarchy.

So, if you’re using LiveData Architecture Component, now you’ll need to keep in mind that there are two distinct lifecycles associated with Fragment object. All of this makes me very sad.

Conclusion

Alright, let’s conclude this post.

Fragments are a mess. Their lifecycle is rocket science. The only thing worse than Fragments is their official documentation. Google developers don’t fully understand Fragment lifecycle by themselves and continue to increase the complexity of this beast.

Said all that, I’ve been using Fragments for years and will continue to use them in the foreseeable future. Compared to “one Activity per screen” approach, Fragments can provide better user experience and make the application feel snappy.

To use Fragments and keep my sanity I’m using the approach to Fragment’s lifecycle described in this article. It probably doesn’t cover 100% of use cases, but it worked great for me in the past years.

There are two advantages to this approach: it makes the handling of Fragment’s lifecycle very similar to Activity’s lifecycle and independent of it. You might’ve noticed that nowhere in the post I mentioned the state of the enclosing Activity. As long as I stick to this scheme, I don’t care what happens to that Activity. I don’t even care whether this Fragment is top level or nested one.

So, I override minimal number of methods and don’t have inter-dependencies between lifecycles. This makes the complexity of Fragments manageable for me.

However, Fragments are such a mess that I’m fairly confident that I missed some important points while writing this article myself. Therefore, if you have anything to add or want to point out any flaw in this article – feel free to do it in the comments section below.

Hello Bagus Aji Santoso. Thanks for your question.
ViewPager is not tied to Fragments in any way. There are Adapters that allow to use Fragments with ViewPager, but you can also have an Adapter that uses standard Views. The same goes for bottom navigation.
There is a framework named Conductor which aims at providing navigation and backstack capabilities with just standard ViewGroups. I’ve never used it myself though.
I also saw implementations of bottom navigation where different destinations corresponded to different Activities. Can’t say I like this approach, but it’s still an option.

Hi Vasiliy. You didn’t mention onViewCreated(view: View, savedInstanceState: Bundle?) method at all.
Isn’t this a good place to initialize your Views?
P.S. Have you considered writing an article on Services? Woild be awesome

Hi Andrew,
I didn’t mention very many methods from Fragment’s lifecyce, not just this one. That’s why I like this approach 🙂
Seriously though, I don’t see a reason to override onViewCreated, but maybe I’m missing something. Is there a use case for this method that can’t be covered in onCreateView or onStart in your opinion?
As for the Services, I have this article about bound IPC services. It’s a niche article because IPC services are kind of exception and it needs an update too, but maybe you could find something useful there.
When I think about it – a comprehensive post about services might actually be a good idea. I know Services are not that widespread, but when you do need to implement one it’s tricky. Thanks for an idea.

Well, I personally think that neither Activities nor Fragments should be aware of UI implementation details. That’s why I always strive to abstract all UI logic inside MVC views. Therefore, this use case is irrelevant for me personally.
However, even if you put UI logic in Fragments, I would say that adding one more lifecycle callback thus complicating the design to spare several lines of trivial code is not the best trade-off to make, but that might be a bit subjective.

Hey,
In my opinion, onActivityCreated is one of the worst mistakes in Fragment’s API because it “leaks” Activity lifeycle into Fragment. This goes against the fundamentals motivation behind Fragmnets as stated by Dianne Hackborn (quoted at the beginning of this post).
That said, if you do know a use case for this method, please share it for all of us to learn. As I said, it’s very improbable that “my” approach covers 100% of use cases.
As for ViewModel, I already stated my opinion about this library. I don’t plan to invest time into explaining how to implement bad practices. I hope it doesn’t sound too harsh. I really don’t want to promote something that I see as a mistake.

Hello, Vasiliy. As usual, you’ve shared a really enthralling post. These fundamentals really give more room and understanding for a newbie like me. This post turned out easier to read after Activity’s Lifecycle. However, I do have some questions or rather a sort of clarifications I would like to receive about lifecycle.

1) Usually, I use onAttach method to cast activity into Listeners for the fragment. But in this article, you say that “the casting of Activities into listener interfaces also takes place in onCreate”. I prefer using onAttach method as it seems to me more relevant abstraction to cast activity (We’ve just attached to it so let’s make friends with parent activity and get references to a listener). Do you think that onAttach is redundant and has the same capabilities as onCreate?

2) You say that “The rule for View onCreateView(LayoutInflater, ViewGroup, Bundle) is that every Fragment’s field related to View hierarchy must be assigned in this method. This includes list Adapters, user interaction listeners, etc. “. However, according to your logic, the best place to set up listeners is onStart method and I do prefer this kind of flow. Could you please shed some light on this slight contradiction?

P.S. I would like to subscribe to your bugreport in the issue tracker, but see no CC to me there. Maybe it’s because the bug is in the blocked status ATM?

1)
We all learned to cast Activities into listeners in onAttach because that’s what official documentation demonstrates. However, if you think about it, the only reason this method exists at all is because Fragments can be retained.
For example, retained Fragments are not re-created upon configuration change and can be attached to new Activity without onCreate being called at all. Therefore, if you’d do this casting in onCreate of retained Fragment, you’d get strange errors and leak the first Activity that retained Fragment happened to be attached to. Not good.
However, as I wrote in the article, I don’t think that retained Fragments should be used at all. If you don’t use them, it becomes safe to cast Activity in onCreate. So, it’s safe, but why not do it in onAttach?
There is absolutely nothing wrong with doing it in onAttach. It will work and you can do it if you find it cleaner. However, my goal, when it comes to Fragment lifecycles, is to simplify the logic and make it as similar as possible to Activity counterpart. Overriding onAttach just for the sake of casting Activity seems like unnecessary complication of Fragment’s lifecycle to me.

2)
You’re right – this part might read like contradiction. I’ll make it clearer.
What I meant is that if you store references to user interaction listeners in Fragment’s fields (for whatever reason), then you should re-initialize these fields in onCreateView. However, the registration of these listeners that you initialize in onCreateView should still be delayed to onStart. In cases when you use anonymous classes as listeners, it’s alright to just define them and register at the same time in onStart.

Unfortunately, I don’t know why you can’t star the issue in the issue tracker.

I have a question. If I want to restore for example the scroll position of a list, I can’t do it in onCreate, because there’s no view yet. In this case i can only restore the previous state in or after onCreateView, so there’s the risk to restore a previous and invalid state if onSaveInstanceState hadn’t called before the fragment’s view hierarchy destroyed. Am I right? In this case what can you suggest? Should I risk restoring this invalid scroll position?

Hi Mike,
To the best of my knowledge, just like with any other View, Android restores the state of ListView/RecyclerView by itself. Since this hasn’t been the issue with any views so far, I’d say that it’s safe to assume that Android handles it properly.
That’s basically the bug that I opened in Google’s issue tracker – I don’t believe that the documentation of Fragment’s lifecycle is correct.
Therefore, if you’re not going to bind a new data to lists – there is nothing for you to do.
If you’re going to re-bind the data to lists, then you’ll do that in onCreate() (or, at least, you’ll initiate the async process in onCreate()). If you’d like to keep the scrolling position, then you will need to use the standard approach.
To summarize. If you restore the state in onCreateView(), then, according to current documentation (which I believe to be incorrect), you might run into a problem. Therefore, don’t do that. If you want to keep track of list scroll position in the Fragment – store it as Fragment’s field.