Android Developer’s Guide to Fragment Navigation Pattern

Over the years, I’ve seen many different navigation pattern implementations in Android. Some of the apps were using only Activities, while others Activities mixed with Fragments and/or with Custom Views.

One of my favorite navigation pattern implementations is based on the “One-Activity-Multiple-Fragments” philosophy, or simply the Fragment Navigation Pattern, where every screen in the application is a full screen Fragment and all or most of these fragments are contained in one Activity.

This approach not only simplifies how the navigation is implemented, but it has much better performance and consequently offers a better user experience.

In this article we will look at some common navigation pattern implementations in Android, and then introduce the Fragment based navigation pattern, comparing and contrasting with the others. A demo application implementing this pattern has been uploaded to GitHub.

World of Activities

A typical Android application which uses only activities is organized into a tree-like structure (more precisely into a directed graph) where the root activity is started by the launcher. As you navigate in the application there is an activity back stack maintained by the OS.

A simple example is shown in the diagram below:

Activity A1 is the entry point in our application (for example, it represents a splash screen or a main menu) and from it the user can navigate to A2 or A3. When you need to communicate between activities you can use the startActivityForResult() or maybe you share a globally accessible business logic object between them.

When you need to add a new Activity you need to perform the following steps:

Define the new activity

Register it in the AndroidManifest.xml

Open it with a startActivity() from another activity

Of course this navigation diagram is a fairly a simplistic approach. It can become very complex when you need to manipulate the back stack or when you have to reuse the same activity multiple times, for example when you would like to navigate the user through some tutorial screens but each screen in fact uses the same activity as a base.

World of Fragments

“Android introduced fragments in Android 3.0 (API level 11), primarily to support more dynamic and flexible UI designs on large screens, such as tablets. Because a tablet’s screen is much larger than that of a handset, there’s more room to combine and interchange UI components. Fragments allow such designs without the need for you to manage complex changes to the view hierarchy. By dividing the layout of an activity into fragments, you become able to modify the activity’s appearance at runtime and preserve those changes in a back stack that’s managed by the activity.” – cited from the Google’s API guide for Fragments.

This new toy allowed developers to build a multi-pane UI and reuse the components in other activities. Some developers love this while others don’t. It is a popular debate whether to use fragments or not, but I think everybody would agree that fragments brought in additional complexity and the developers really need to understand them in order to use them properly.

Fullscreen Fragment Nightmare in Android

I started to see more and more examples where the fragments were not just representing a part of the screen, but in fact the whole screen was a fragment contained in an activity. Once I even saw a design where every activity had exactly one full screen fragment and nothing more and the only reason why these activities existed was to host these fragments.
Next to the obvious design flaw, there is another problem with this approach. Have a look at the diagram from below:

How can A1 communicate with F1? Well A1 has total control over F1 since it created F1. A1 can pass a bundle, for example, on the creation of F1 or can invoke its public methods. How can F1 communicate with A1? Well this is more complicated, but it can be resolved with a callback/observer pattern where the A1 subscribes to F1 and F1 notifies A1.

But how can A1 and A2 communicate with each other? This has been covered already, for example via startActivityForResult().

And now the real question comes: how can F1 and F2 communicate with each other? Even in this case we can have a business logic component which is globally available, so it can be used to pass data. But this does not always lead to elegant design. What if F2 needs to pass some data to F1 in a more direct way? Well, with a callback pattern F2 can notify A2, then A2 finishes with a result and this result is captured by A1 which notifies F1.

This approach needs a lot of boilerplate code and quickly becomes a source of bugs, pain and anger.

What if we could get rid all of the activities and keep only one of them which keeps the rest of the fragments?

Fragment Navigation Pattern

Over the years I started to use the “One-Activity-Multiple-Fragments” pattern in most of my applications and I still use it. There are a lot of discussions out there about this approach, for example here and here. What I missed however is a concrete example which I can see and test myself.

Let’s have a look at the following diagram:

Now we have only one container activity and we have multiple fragments which have again a tree like structure. The navigation between them is handled by the FragmentManager, it has its back stack.

Notice that now we don’t have the startActivityForResult() but we can implement a callback/observer pattern. Let’s see some pros and cons of this approach:

Pros:

1. Cleaner and more maintainable AndroidManifest.xml

Now that we have only one Activity, we no longer need to update the manifest every time we add a new screen. Unlike activities, we do not have to declare fragments.

This could seem like a minor thing, but for larger applications which have 50+ activities this can significantly improve readability of the AndroidManifest.xml file.

Look at the manifest file of the example application which has several screens. The manifest file still remains super simple.

2. Centralized navigation management

In my code example, you will see that I use NavigationManager which in my case it is injected into every fragment. This manager can be used as a centralised place for logging, back stack management and so on, so navigation behaviours are decoupled from the rest of the business logic and not spread around in implementations of different screens.

Let’s imagine a situation where we would like to start a screen where the user can select some items from a list of person. You also would like to pass some filtering arguments like age and occupation and gender.

My personal problem with this approach is that these arguments are “extras” and they are not mandatory, so I have to make sure that the receiving activity handles all the different cases when an extra is missing. Later on when some refactoring is made and the “age” extra for example is no longer needed, then I have to search everywhere in the code where I start this activity and make sure that all the extras are correct.

Furthermore, wouldn’t it be nicer if the result (list of persons) would arrive in a form of _List_ and not in a serialized form which must be deserialized?

In case of fragment based navigation, everything is more straightforward. All you have to do is to write a method in the NavigationManager called startPersonSelectorFragment() with the necessary arguments and with a callback implementation.

3. Better means of communication between screens

Between activities, we can share only a Bundle which can hold primitives or serialized data. Now with fragments we can implement a callback pattern where for example, F1 can listen to F2 passing arbitrary objects. Please have a look at the previous examples’ callback implementation, which returns back a _List_.

4. Building Fragments are less expensive than building Activities

This becomes obvious when you use a drawer which has for example 5 menu items and on each page the drawer should be displayed again.

In case of pure activity navigation, each page should inflate and initialize the drawer, which is of course expensive.

On the diagram below you can see several root fragments (FR*) which are the full screen fragments which can be accessed directly from the drawer, and also the drawer is only accessible when these fragments are displayed. Everything which is to the right of the dashed line in the diagram are there as an example of an arbitrary navigation scheme.

Since the container activity holds the drawer, we have only one drawer instance so at every navigation step where the drawer should be visible you don’t have to inflate and initialize it again. Still not convinced how all of these work? Take a look at my sample application which demonstrates drawer usage.

Cons

My greatest fear had always been that if I used fragment based navigation pattern in a project, somewhere down the road I would encounter an unforeseen problem which would be difficult to solve around the added complexity of fragments, 3rd party libraries and the different OS versions. What if I had to refactor everything I’ve done so far?

I must admit that gaining sufficient experience with fragments to be able to solve these problems was a rather long process. But in every case the issue was not that the philosophy is bad, but that I did not understand fragments well enough. Maybe if you understand fragments better than I did you would not even encounter these issues.

The only con that I can mention now is that we can still encounter problems which would not be trivial to solve since there is no mature library out there which showcase all the complex scenarios of a complex application with fragment based navigation.

Conclusion

In this article, we have seen an alternative way to implement navigation in an Android application. We compared it with the traditional navigation philosophy that uses activities and we have seen a few good reasons why it is advantageous to use it over the traditional approach.

In case you haven’t already, check out the demo application uploaded to GitHub implementing. Feel free to fork or contribute to it with nicer examples which would better show its usage.

About the author

Becze is a talented freelance software engineer, who has previously worked as an Android developer at Garmin. He has worked on very large and complex Android projects in Scrum teams using state of the art technologies, including BT Classic, BLE, ANT+, and wearables. [click to continue...]

Comments

grennis

Nice discussion. The drawer layout also is difficult to implement well with multiple activities because ideally you want the navigation to happen while the drawer is animating closed. It would be impossible or at least really difficult (maybe using activity transitions) to do this with different activities.

Asad W

I use a mix of both. Where i need to show drawer i use fragments and where i need to show back or up button i use parent activtiy concept. But it was a good article i learned from it that we can apply a listener on fragments for delivering results to previous fragment.

Ryan Taylor

Hey Becze. Thanks for sharing your thoughts on this topic.
I'm a fan of the simplified cross-scene communications and not having to recreate reused views (I.e. navigation menu, minimized media player, etc.) as you mentioned in your article. However, over the past year of working with a single-Activity architecture, I've noticed a few headaches come up that I think are worth sharing:
a) Toolbar management becomes a mess. For example, say you navigate from a simple, single-titled Toolbar to a CollapsingToolbarLayout or a Toolbar with a nested SearchView. Since Toolbars require that they be managed by an Activity, when dealing with a single Activity structure, those Toolbar changes need to be communicated from every Fragment to that Activity. Transitions between opaque and translucent status bars also need to be toggled in this matter.
b) The single Activity tends to become a sort of God class. When all navigation occurs in the one Activity, that Activity needs to know about every scene in the app.
c) By passing a single navigation management interface, all Fragments gain the power to navigate anywhere in the app. This might not be desirable.
d) Prevents you from performing meaningful Activity transition animations.

Michel H.

Pretty cool! Nice post!

mewa

Nice article, but in my opinion the biggest headache on Android is not the Activity vs Fragment vs Views dispute but the general architecture of an app.
People building Android apps often tend to forget that it's the "God" objects, Ryan has mentioned, that are the main source of issues that often result later in unmaintainable code, bugs and poor testability.
The first thing one would do is to separate your logic and ui.
After having worked with 1-activity-to-many-fragments architecture, I have to admit that personally I prefer Views, as they are really straight-forward, have no async transactions that could introduce "hazards" as well as some other Fragment-sprecific quirks.
Although they do involve writing a bit of boilerplate code, in most use cases using Views results in a simpler application lifecycle.
When it comes to portability issues, there are basically none with this approach, since you could always wrap your View - and actually that's what Fragments really are.
That is just a preference though - View, as I perceive it, could mean the basic UI building block, but it has to be referred to as an idea, that fragments could implement too.
The most important thing therefore is to give the view layer an abstraction that can be easily adapted to changes.

Dmitry Yurchenko

Hi everyone.
I totally agree with Ryan Taylor.
Also one more thing - backstack. FragmentManager will give you some headaches with proper management of it, showing animations on changing fragments and some other stuff.
Fragments were created firstly to represent UI, to give developer ability to create parts of Screen and be able to divide them between screens, if on screen rotation new dimensions of screen will not allow to show same parts in their places.
It is very terrible mistake for all of us, who tried whenever to put business logic, model managment in class Fragment, which is responsible Only for showing user UI, part of it, even if it fills screen.
Also it isn't good idea to put in 1 class all control of everything (what single activity will become soon). You will have very big monster class with 1000+ lines of code and it would be almost impossible to go forward and add new features in future.
In result I found for myself that better will be to have many activities and many fragments, sometimes activities without fragment at all.
My advice is: try not to go in extreme position in using fragments everywhere where it is possible

Aleksandr Mirko

It's not a new idea. I think everybody who develops on Android uses this way for drawer case. Because creation of new Activity with the same drawer is the worst idea.

Mauricio9308

Really interesting pattern in paper, but as Ryan said, when the projects become large enough the main Activity that holds the Fragments references transforms in something like a 'God' class.
The mess that it's caused by it and its maintainability cost, for me, its just not worth it.
In practice I would recommend a mixed approach, with Activities created for each of the modules of the application and Fragments for each of the UI components of it (Tabs, FullScreenDialogs, etc..).

Siarhei Harlukovich

Same for me. I use fragments only when it's needed. If you can implement this particular screen without fragments - go for it!

Siarhei Harlukovich

Lol, Google did it in some of their projects. They copy/paste navigation drawer to every activity.

Damian Szczepański

Fragments are "cool" the same as ASP.Net WebPages was. For me it sucks a little. Too complicated to properly managing them, especially when you want to not to add to back stack.

Ashraf Sayied-Ahmad

Great founding!
Why not to copy paste?

Ashraf Sayied-Ahmad

i think that every immature framework suffers from the standardization of the several approaches to make a robust, maintainable and "with-fun-to-code" architecture.
So every one can choose the best mixture of components to compose the whole solution.
No rules!

Kirill Olenyov

Hi, I looked at the sample app on GitHub and found that you store rootView(from onCreateView) for every Fragment. Not good idea because if you need to put such fragments to backstack it can lead to OOM and app will be crashed.

Raul Guzman Condor

Hello Becze
I'm develop application with Fragment Navigation Pattern, I want write some test with Espresso, but It's difficult to write with this pattern(well in my case). In my view, because a lot of tutorials and articles hand the flow with activities and Espresso recommend it. Do you have any suggestion or advice?
Best Regards.

Ivan Schuetz

A few clarifications for Post and commenters:
1. Fragments are not "only UI" so they are not simply replaceable with views - if they were they would have not been created in the first place! I think it's perfectly fine to put business logic in fragments, e.g. To call a service to get the data to be displayed. This way you can easily compose a screen using multiple fragments that contain the UI as well as the behavior without anything additional.
2. It's a bad idea to pass callback or listeners to fragments to communicate with the container, because the system can re-create the fragments at any point, for which it will not call the constructor you used to initialize it, so this new fragment instance will not have any of the in-memory parameters you passed to it. This is why you have to use bundles to pass data to fragments (this is serialized, so it doesn't disappear when the fragment is re-created) and have to use things like onActivityResult to get data back from it. A listener is also only an in-memory parameter and would disappear.
3. To avoid a "fat activity" you can simply create different components, coordinators, etc. (if necessary at all) to which the activity delegates the work. Anyway, if you put the business logic in the fragment themselves, you don't need for the most part a fat central component. If you feel that your fragment are becoming fat you can any time put their business logic in a business-logic only component but which would be associated only with the fragment.

Artem Dobrovinskiy

Dear Becze,
thank you for the great article! And I mean it — you talking about complex things in a very distinctive and clear way.
I went to the project at the Github — and after spending some time with it I think I should recommend you to read the «Clean code» by Robert Martin.
Inspite of your opinion of the man applying some of the advices from the book could make your code 300% more readable.

Otari Glonti

Hello Becze! Do you give also private classes?
Best wishes

stevan

What if you want to reuse some of the fragments?
Handling fragment button "next" won't take you to the same destination in all cases.
FragA -> FragB -> FragC (first approach)
Reuse FragB
FragD->FragB -> ?
Fragment should not be the one who decides where to go next. It should just notify activity that something happened.
What do you suggest?

Becze is a talented freelance software engineer, who has previously worked as an Android developer at Garmin. He has worked on very large and complex Android projects in Scrum teams using state of the art technologies, including BT Classic, BLE, ANT+, and wearables.