Single Activity design was discussed in one of the Google I/O talks ’19 along with Android Jetpack Navigation. The Android team now recommends the Single Activity design as the preferred architecture.
Do we have a new initialism here? SAAs (Single Activity Applications). This design approach is not new, even in the Android world.
It can be compared with SPAs (Single Page Applications) from the web world which is a popular design approach for most of the modern web frameworks.
In this post, we will discuss the following:
- What problems does “Single Activity design” solve?
- When should you adopt “Single Activity design”?
- What can be done for existing projects?
But first, let’s start with brief introductions to Activities and Fragments.
What is an Activity?
Activities are entry points to your application. So, you obviously need to declare at least 1 activity in your app.
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with
setContentView(View). While activities are often presented to the user as full-screen windows, they can also be used in other ways…
Do Activities have problems?
Well, not really the kinds of problems that would force you to switch to Single Activity design.
It is quite evident that by design, each screen of your app is supposed to be a separate Activity. At least, that was the approach that had been widely adopted during the initial years of Android, until Fragments were introduced in Honeycomb (Android 3.0) to tablet devices.
What are Fragments?
Fragments can replace Activities, as Fragments can do what Activities can do.
The main objective of Fragments was to make your UI code re-usable to support more dynamic and flexible UI designs, since Activities cannot be embedded within other activities. (LocalActivityManager is now deprecated for good.)
Fragments APIs have evolved a lot. Fragments, from the very beginning have their own lifecycle pretty similar to Activities. Fragments also have their own back-stack which can be managed by using FragmentManager.
Fragments are very useful and you should be definitely using them if you are:
- targeting multiple form-factors.
- using ViewPagers. They kind of force you to use Fragments.
- creating a library that has a UI component, it makes sense to expose it as a Fragment instead of an Activity.
Do Fragments have problems?
Fragments have their own set of problems. But over time, and with the introduction of Architecture Components, especially
LiveData, a lot of problems with Fragments have been solved.
But really, apart from view-based frameworks, you don’t have a choice but to use Fragments.
In this post, I will not be comparing other approaches which avoid using Fragments. For example: Conductor
A good thing about fragments: They are not a part of the Android framework now, but are available as a part of the Jetpack library. So, it’s easy for the Android team to fix bugs and add new features.
So, for your Android apps, you need to declare at least 1 Activity as the entry point if you want to present an UI. But, for implementing other screens, you have a variety of options.
Option 1: Each screen is a separate Activity
Just like in the old days, when Fragments were non-existent.
For example, let’s consider a “Checkout” flow. You might have 3 steps in this flow:
Order list review page,
Shipping details page and
Payment page. The following 3 activities need to be created.
Option 2: Each flow is a single Activity
Each flow of your app is hosted within a single activity. Sub-screens in each of the flows can be implemented by using Fragments.
The following activity and fragments need to be created.
Option 3: Single Activity
You just have a single activity which is the entry point of your application. All the other screens are implemented using Fragments. The single Activity is a host for all the other fragments and is responsible for managing them.
To give an example, almost all the popular cross-platforms frameworks like Xamarin, Ionic, Flutter and React Native take this approach, which have been working well.
What I have seen generally, is people start with Option 1, i.e, only Activities and no Fragments. Gradually, Fragments are introduced, mostly because:
- You need to support phones and tablets
- You need to re-use UI in different parts of your application
- You are using ViewPager which kind of forces you to use Fragments
- You use some third-party libraries which expose UI components as Fragments. For example: Google Maps
In theory, you are somewhere between Option 1 and Option 2. And Option 2 is not much different than completely switching to the Single Activity design.
What problems does Single Activity design solve?
1. Inconsistent behaviour of Activities
Activity is a part of the Android framework. So, its behaviour and capabilities is tied to the API versions. So, new features and bug fixes are not guaranteed to be available on older API levels. You either have to use several compat libraries like
ActivityOptionsCompat etc or handle these corner-cases yourself, which is a pain.
This, not only increases your development time, but testing time as well. The experience and behaviour could also be inconsistent across devices and OS versions.
2. Sharing data between Activities
Between Activities, there’s only the Application Scope which can be used to share data. But, the Application is also common to other Android components likes Services, BroadcastReceivers, ContentProviders etc.
Ideally, there should be a separate scope that can be shared within the components that you choose or design (your app screens), preferably at the Activity level.
3. Poor user experience
When switching between Activities, the whole window is replaced. So, the content along with the Toolbar/ActionBar is also replaced. I personally think that the Toolbar shouldn’t be replaced, but updated with relevant content. To give an example, consider any desktop application. The toolbar is always the same, only the content and the behaviour changes.
Also, due to inconsistent behaviour of Activities, transitions and animations behave differently on different devices and OS versions.
4. Developer experience
When you create multiple activities, each activity needs to go into the AndroidManifest file, which seems a bit odd if you think.
If a specific control is needed on all the screens, this requires some extra effort to implement if you have multiple activities in your app. Ex: navigation drawers, bottom navigation bars, common menu options etc.
Detecting if your app is being actively used (think WhatsApp online status) is not trivial when you have multiple activities on the stack at a time.
Single Activity design seems to be a good architecture choice. But, until now, in the absence of a framework or library, implementing such a design wasn’t economically feasible. There are some view-based single activity frameworks like Conductor and scoop, but they don’t support Fragments.
With the introduction of the Navigation component, it is now very easy to implement such an architecture and doesn’t require any additional effort for the developers.
When should you adopt Single Activity design?
Navigation component handles a lot of non-trivial problems of this design approach. So, if you are starting a new project, you should definitely consider adopting the Single Activity design.
Navigation component provides simple and easy to understand APIs, storyboard-like navigation editor and elaborate documentation and tutorials.
There’s also support for:
- Handling deep links
- Easy and reliable screen transitions
- Easy handling of your app’s Toolbar/ActionBar
- Dynamic feature modules
What can be done for existing projects?
In theory, for existing projects, which already have a mix of Activities and Fragments, it probably makes sense to make gradual changes. You can start with smaller flows in the app and switch to having a single activity and multiple fragments for each flow using the Navigation component.
I would like to discuss on this in details if anyone has an experience of transitioning existing projects into Single Activity architecture.
- Single Activity design is not new
- Frameworks like Conductor and scoop are view-based Single Activity frameworks for Android which do not support fragments
- Implementing a Single Activity design using fragments wasn’t economically feasible before
- Navigation component is based on Fragments and is designed for implementing this architecture
- For new projects, you should definitely consider this new design approach