Android

Creating Highly Modular Android Apps

By  | 

Android

Inheritance Hell

For such complex structures, such as in UI building, inheritance can quickly become hell. Check the following mock case:

Code built along this inheritance tree quickly becomes unmanageable (“inheritance hell”). To circumvent that, developers often follow the principle of “Composition over the inheritance.”

 

Composition over inheritance

“Composition over inheritance (…) in object-oriented programming is the principle that classes should achieve polymorphic behavior and code reuse by their composition (by containing instances of other classes that implement the desired functionality).”

The Composition Over Inheritance principle is a great idea and certainly can help us solve the issue presented above. There are, however, virtually no libraries, sample code, or tutorials available on how to implement this on Android. An easy way to start hacking around is to use runtime parameters (a.k.a. intent extras) to make feature composition, but that still results in a huge unmanageable single class monstrosity.

Honorable mentions here are the LightCycle and CompositeAndroid libraries. Both are tightly tied to the Activity or Fragment, leaving other modern patterns such as MVP or MVVM out. They are not very flexible as they rely solely on Android’s native callbacks (cannot add extra callbacks) do not support inter-module communication.

 

Decorator pattern

Based on the presented issues developers face every day, the EyeEm PC Game structure team started developing a pattern to solving more flexibly, not directly attached to a component such as Activity or Fragment. The decoupling allows the pattern to be used for any class the developer wishes to make modular through composition.

The pattern is quite similar to the LightCycle/Composite approaches and consists of 3 classes:

  • The base class, called DecoratedObject, dispatches its inherited and extras methods to a dispatcher object.
  • The DecoratorsObject instantiates and holds a list with all the composed objects and dispatches the methods to them.
  • The Decorator abstract class with empty implementations of all the methods and extras interface. Concrete implementations of this class to be created by the developer adding single individual responsibilities to it.

With that approach, developers gain immediate advantages regarding

  • Separation of responsibility
  • Dynamic runtime permutation of features
  • Dev workload parallelization

To implement the above pattern seamlessly for developers, a compile-time code generation tool was created. We’ll see next how easy it is to break all those responsibilities previously presented (and more) into single-responsibility classes.

 

Decorator library

Or how to create your own modular single responsibility app in 3 easy steps

To implement decorators, first, create a blueprint of the code that should be generated. Here we’ll use an Activity with a RecyclerView as an example, but the same could be used in a Fragment, Presenter, or even View. In this sample, we’ll use onCreate/onStart/onStop/onDestroy from the activity lifecycle and create a few extra callbacks that will suit the RecyclerView case.

This simple blueprint annotated with `@Decorate` will generate the complete implementation of the decorator’s pattern and a `Serializable` builder class that can be passed as a parameter. To complete the Activity implementation, we extend the generated class and bind the received builder to it.

Now the responsibilities can be easily distributed to bindable decorators. Every decorator contains all of the lifecycle callbacks and may implement any of the optional interfaces. And finally, the composition can be obtained in a simple builder pattern:

 

Complete sample app

The sample app morphs the Activity execution on each user tap by simply adding/removing decorators from the current activity before starting the next.

Most of the code shown above was hacked from that sample. There you’ll find a list of kind-of real implementation of decorators using Realm and Retrofit that loosely resembles the UI building tasks mentioned at the beginning of this article.

  • CoordinatorLayoutInstigator overrides the default layout to a CoordinatorLayout, optionally instantiates a header
  • ToolbarInstigator takes over the toolbar and applies a title
  • ToolbarUp and ToolbarBack decorators, behavior for the navigation icon on the toolbar
  • LoadMore decorator, adds an infinite scroll functionality to the RecyclerView
  • Loading decorator, adds a “loading” empty state to the RecyclerView
  • PhotoList and PhotoRequest decorators, local data storage, and API request to Photo list API call.

 

Real-world app

EyeEm already operates with decorators — and the experience has never been better. Check it out on the Play Store. We currently use Decorated view presenters (using Square Mortar library) for all the UI elements and Decorated activities for transitions, deal with different API levels, A/B testing, navigation, tracking, and a few special cases onboarding new photographers.

The code and implementation shown above are only samples and intended purely as a guide.

While we built this for Android, the pattern is open to any use case. The library is a pure Java implementation that generates the code during compile time and can be used on any Java class. We encourage developers to look to create modular single responsibility code in any of their Java projects!

Enough said- add it to your build. Gradle and start building modular apps.

 

You must be logged in to post a comment Login