Say no to BaseActivity and BaseFragment
Many apps face a similar challenge. Where do we put common logic for all of our screens? A base class is often the solution, but it can become a nasty beast. Let’s have a look for better options.

The problem
There are many situations where you want to add functionality to all of an app’s screens. Examples can be:
- Analytics
- Navigation
- Displaying dialogs on screen
- Handling runtime permissions
- Reacting to lifecycle events for various libraries
This kind of logic needs to be shared and it usually needs a Context
, Fragment
or Activity
. The straightforward solution is often using a base class. Even if the logic is extracted into multiple dependencies, the base class starts to grow over time.
There are often issues with this approach:
- Virtual methods need to be added in order to tweak behaviour.
- Many lifecycle methods need to be implemented.
- We need slightly different behaviour and base class doesn’t allow it, requiring changes.
- For each scenario, another tweak needs to be added to the base class.
After a while you end up with a BaseBeast
with lots of code, that everyone is afraid to touch. You can see snippets like:
1667: if(rootViewEnabled()) {
1668: setContentView(R.id.rootView)
1669: }
Composition ❤
The Android framework offers built-in ways to solve many of the cases above. It is Application.ActivityLifecycleCallbacks
for Activities or FragmentManager.FragmentLifecycleCallbacks
for Fragments.
Example 1: We may have analytics code that reports events for some Activity lifecycle methods.
To avoid adding this dependency into a BaseActivity
, we can implement the Application.ActivityLifecycleCallbacks
interface:
Then, we can register it within our Application
class:
Example 2: We need to show a message on any screen when a push notification is received whilst the app is in the foreground. Example API:
This is one of the cases in which you need a reference to an activity at some point in time to show the message. The implementation could be:
Registration of this callback will happen similarly to the previous example:
Dagger Multibindings
The use of lifecycle callbacks could separate out your application logic, however you may already feel that there is another issue. Our Application
class might become a dumping ground of logic configuring dependencies. This becomes particularly problematic in a multi-module project.
Dagger Multibindings can help. We can compose a set of ActivityLifecycleCallbacks
with the @IntoSet
annotation.
With this, the application code only needs to register those callbacks, hiding the implementation details:
onActivityResult() case
Whilst implementing permission logic or interacting with many other APIs like Dynamic Delivery, we may need onActivityResult()
implementations in several places.
Inheritance could be appealing again, but we can avoid it by extracting our desired logic into separate invisible Activity classes with a shared dependency. Using this technique we transform onActivityResult()
into a pure JVM callback, usable from anywhere.
A great example of this approach can be seen in the RxPermission library, where RealRxPermission.getInstance
uses a static singleton to share the instance. When implementing this by ourselves, providing PermissionHandler
by dependency injection could be another option.
Wrap up
Keep your base classes lean. Sharing code through inheritance is appealing, but as the code grows it can hit you hard. The Android framework provides ways to share code via simple composition or we can use the invisible Activity technique to separate other responsibilities.
This separation becomes crucial as code evolves over time and if done with caution, maybe you don’t need a b̵e̵a̵s̵t̵ base class at all. 🎉
How big are your base classes? Do they cause you pain or are you happy with them? Let me know.
Happy coding!