
In recent years, it was possible to observe the use of various approaches to building navigation in Android applications. Someone uses only activities (Activity), someone mixes them with fragments (Fragment) and / or with modified views (Custom View).
One of the ways to implement navigation in an application is based on the “One activity - many fragments” philosophy. With this approach, each application screen consists of numerous fragments, with all or most of them contain one activity each. This solution simplifies the implementation of navigation and improves performance, which has a positive effect on the impression of using the application.
')
In this article, we will look at several typical approaches to implementing navigation in Android, and then we will talk about the approach using fragments, comparing it with other approaches. The demo application, which illustrates the article as an example, can be downloaded from
GitHub .
World Activity
A typical Android application that uses activity has a tree structure (more precisely, the structure of a directed graph), in which the activity, which is initialized when the application starts, is the “root”. When using the application, the operating system supports the operation of the back stack. A simple example:

Activity A1 is the entry point of the application (this can be the splash screen or main menu), from which the user can go to A2 or A3. To organize the interaction between activities, you can use
startActivityForResult()
startActivityForResult()
, or make common, globally accessible business logic.
If you want to add a new activity, you need to perform the following steps:
- Set the activity itself.
- Register it in AndroidManifest.xml .
- Open it from another activity using startActivity () .
Of course, the above scheme describes a very simplified approach. Everything can be much more complicated if you need to
manipulate the transition stack backwards . Or if you want to use the same activity several times: for example, the user will switch between different training screens, each of which will be based on the same activity.
Fortunately, we have tools such as
tasks and guidelines for
working properly with the transition back stack . However, with the advent of API level 11, fragments were introduced ...
World of fragments
As stated in the official
documentation from Google , “fragments were introduced in Android 3.0 (API level 11) primarily to create more dynamic and flexible interfaces adapted for large screens, for example, tablets. They have a much larger screen area than smartphones, respectively, more space for combining and rearranging interface elements. Thanks to the fragments, you will not have to manage complex changes in the hierarchy of views when creating such interfaces. By dividing the structure of activity into fragments, you get the opportunity to change its display during the execution of the program, protecting the changes controlled by the activity in the transition stack back. ”
This new tool allowed developers to create interfaces consisting of several separate sections, as well as reuse components in other activities. Someone likes it,
some don't . Today, there is a lot of debate about whether to use fragments, but everyone will probably agree that the complexity of development and applications has increased with their appearance, so developers should be well-versed in the proper use of fragments.
Fullscreen snippets as a nightmare
Increasingly, it was possible to find applications in which not part of the screen consists of several fragments, but the entire screen is one fragment placed in activity. You can even stumble upon crafts in which each activity contains one full-screen fragment, and nothing more. That is, the activities act exclusively as a container for these fragments. This approach, in addition to the obvious design error, has another problem. Take a look at the diagram:

How will A1 interact with F1? Yes, A1 completely controls F1, because it was she who created it. For example, A1 might, during creation, pass F1 some bundle, or call its public methods.
And how will F1 interact with A1? Here it is already somewhat more complicated, but you can get out of the situation with the help of the callback / observer technique, in which A1 subscribes to F1, and he notifies his subscriber.
Well, what about the interaction between A1 and A2? This task is solved, for example, using
startActivityForResult()
.
And now the question is: how will F1 and F2 interact with each other? Even in this case, we may have a public component that contains business logic, which you can use to transfer data. But this does not always help create a beautiful design. Suppose F2 has to transfer some data to F1. When using the callback-technique, F2 can notify A2, it generates some kind of result, A1 gets it and notifies F1.
With this approach, you will have to use a lot of template code, which will quickly become a source of bugs, pain and irritation. And what if we get rid of all these activities, leaving only one of them, putting the rest of the fragments in it?
Method of creating fragment-based navigation
The approach that will be described causes a lot of controversy (
Example 1 ,
Example 2 ). Let's look at a specific example.

Now we have the only activity left that performs the role of a container. It contains several fragments that make up the tree structure. Navigation between them is done using the
FragmentManager
, which has its own backward transition stack. Please note that there is no
startActivityForResult()
, but we can still implement the callback / observer technique. Let's look at the advantages and disadvantages of this approach.
Benefits:
1. The AndroidManifest.xml file is cleaner and easier to maintain.
Since there is only one activity left, we no longer need to update the manifest each time a new screen is added. Fragments do not need to declare, in contrast to activities. At first glance, this is a trifle, but if the application is large and contains several dozen activities, the manifest file will become much more readable.
Take a look at the manifest of our sample application containing several screens. It has very simple content:
<?xml version="1.0" encoding="utf-8"?> package="com.exarlabs.android.fragmentnavigationdemo.ui" > <application android:name= ".FragmentNavigationDemoApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name="com.exarlabs.android.fragmentnavigationdemo.ui.MainActivity" android:label="@string/app_name" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
2. Centralized navigation control
After reviewing the application code, you may notice that there is no
NavigationManager
in it. In our case, it is embedded in each fragment. This dispatcher can be used as a center for collecting logs, managing the transition back stack, etc. Therefore, navigation schemes can be separated from business logic and not distributed across implementations of different screens.
Imagine that we need to display a screen on which the user can select several people from the list. You may need to use filters like age, place of residence or gender.
When using several activities, I would have to write:
Intent intent = new Intent(); intent.putExtra("age", 40); intent.putExtra("occupation", "developer"); intent.putExtra("gender", "female"); startActivityForResult(intent, 100);
Next you need to set
onActivityResult
and process the result:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); }
The problem is that all these arguments are “superfluous” and not obligatory. Therefore, you need to make sure that the activity handles all possible cases when filters are not used. Later, when some kind of refactoring is done and one of the filters is no longer needed, you will have to find all the places in the code from which this activity starts, and make sure that all filters are working correctly.
In addition, it is better to make the result (list of people) come in the form of _List_, and not in the serialized form, which will need to be deserialized.
If we use fragment-based navigation, then everything becomes simpler. It is enough to write the
startPersonSelectorFragment()
method in
NavigationManager
with all the necessary arguments for the implementation of callbacks.
mNavigationManager.startPersonSelectorFragment(40, "developer", "female", new PersonSelectorFragment.OnPersonSelectedListener() { @Override public boolean onPersonsSelected(List<Person> selection) { [do something] return false; } });
Or with
RetroLambda :
mNavigationManager.startPersonSelectorFragment(40, "developer", "female", selection -> [do something]);
3. Improving screen interaction
Between activities, only bundles containing primitives or serialized data can be transferred. And thanks to fragments, it is possible to implement a callback method, when F1, for example, can receive arbitrary objects from F2. See previous examples of callback implementations that return _List_.
4. Creating a fragment costs less than creating activities
This becomes apparent when using a navigation panel (drawer) containing several menu items that should be displayed on each page.
If your navigation is built on activities alone, then each page should inflate and initialize the panel, which consumes a lot of resources.
The following diagram shows several root fragments (FR *), which are full-screen. You can access them directly from the panel, and you can access it only when one of the root fragments is displayed. The part of the scheme on the right side of the dotted line is an example of an arbitrary navigation scheme.

Since the panel is located inside the container, we have only one copy of it. Therefore, the panel should be visible at each stage of navigation; it
does not need to be “inflated” and initialized again . It's not entirely clear how this works? Analyze the sample application, it demonstrates the use of the panel.
disadvantages
Starting to implement fragment-based navigation, it would be very unpleasant to delve into the development and face some unforeseen, intractable problems associated with the additional complexity of using fragments, third-party libraries and different versions of the OS. And if you have to refactor all that we have already written?
Indeed, it is necessary to solve problems associated with
nested fragments , as well as with third-party libraries, also using fragments, like
ShinobiControls ,
ViewPagers and
FragmentStatePagerAdapters .
It must be said that it may take a lot of time to gain sufficient experience to cope with difficulties. True, their presence is most often caused by an incomplete understanding of the features of working with fragments, and not by the viciousness of the idea itself. Perhaps you already have enough experience and you will not face any problems at all.
So the only drawback worth mentioning is the lack of good libraries, which would reflect all the complex scenarios from complex applications, whose navigation is built on fragments.
Conclusion
In this article we looked at one of the approaches to the implementation of navigation in Android applications. A comparison was made with the traditional approach, which implies the use of activities, which revealed a number of advantages in comparison with the “classics”.
In case you haven’t seen the sample application yet, welcome to
GitHub . You can freely fork or supplement it with more successful and visual solutions.