Handling Android Error Views with SOLID

Paulo Pereira
ProAndroidDev
Published in
6 min readMay 17, 2021

--

Not long ago, I went through a project with a common problem in almost every applications, which is Error View Handling.

Every application has its own scenario, and on this article, I will describe to you the way I have approached mine.

Getting Started

The goal here is to have a way to display a custom error to the user in a Fragment. Seems simple, uh? Well, there are many ways to do it, but not all are scalable and easy to maintain and extend.

I will show you how I have approached this problem, using SOLID, keeping scalability in mind. Props to Uncle Bob!

As always, a sample project is on my GitHub, feel free to check. (Good option if you are lazy! Stay here if you are curious to learn 😁)

As of pre-requisites, you’ll need at least:

  • Android Studio 4.2.1
  • Kotlin plugin 1.5.0

If you build and run the app, you’ll see this:

🥶 The Error Fragment

So, we want to display a custom error. There may be a lot of different errors, for example:

  • Not Found
  • Internet
  • Server
  • etc

On this specific scenario, the behavior is the following:

There are specific states observed [Observables], and each of these can have one or more listeners [Observers]. Whenever any state changes, the Observers get notified. [Observer Pattern]

One of the detected states is the kind of error that may be happening.

As soon as this Error State changes, the Error Fragment is shown with the respective labels and images. Notice that errors share the same layout and only change the content.

1st Approach… 💪

Add the different errors handling logic inside ErrorFragment itself

2nd Approach… 💪 💪

Use something like Static Factory Method, which creates object instances based on supplied arguments (or even without them) and you can call them from anywhere without the need of having an instance of a class that contains them.

It is usually used in these cases, to re-use the same Fragment, displaying different content.

  • Create an Enum class with the different error types;
  • Pass the error type to a newInstance() function inside companion object;
  • Create the Fragment passing the error type to the arguments bundle;

However… Our goal is to display the error. Views must be dumb, and all the work of knowing what error to display must be done out of them. Their only responsibility is to display something to the user.

If we ended up following any of these approaches, EVERY time we wanted to add a new error, we would need to come back here and change something, which could break something.

This means these approaches are not scalable, and do not respect Open Closed Principle.

💡 What may be the solution?

The only thing that ErrorFragment needs to know, is that it needs to display an Error. It should not care of what type of error it is. In programming terms, we can represent this using an abstraction.

If we create an Interface representing an Error, we can create as many different Errors as we want. Then, these different errors are aware of their own implementation, and ErrorFragment will be receiving them using the Static Factory Method.

This way, we can achieve the Single Responsibility Principle on ErrorFragment. The only thing that it needs to do, is to display the error.

🧐 Abstractions

ErrorView

The ErrorView interface, represents an Error to be displayed in ErrorFragment. It needs to extend Parcelable to be passed as a bundle argument to the fragment.

Before continue…

We will be using a specific Kotlin Plugin to avoid the need to implement certain functions due to extend from Parcelable (Parcelize annotation).

Go to your build.gradle (app) file, and add the following line inside the plugins block:

id 'kotlin-parcelize'

If you cannot find the plugins block, add instead the following on the top of the same file:

apply plugin: 'kotlin-parcelize'

You should be ok now.

The function setupView() receives FragmentErrorBinding as an argument to be used when building the error view. As mentioned before, in this scenario all errors share the same layout and only the content is changed.

This is the base abstraction that we need, and is what we will provide to ErrorFragment.

I am using ViewBinding

ErrorViewWithWarning

After having the ErrorView abstraction, we can also start to define different types of error abstractions. For example, there are some errors where a Snackbar can be shown. As long as they implement the main ErrorView abstraction, it is fine.

📐 Error Types

Let’s have as examples the previously mentioned error types:

  • Not Found
  • Internet
  • Server

Not Found Error

As you can see, it’s pretty straightforward to extend our implementation, without changing the ErrorFragment.

We can add new errors, simply by implementing the ErrorView abstraction, or ErrorViewWithWarning.

Internet Error

In this error, we also want to have the choice to display a Snackbar, so we implement ErrorViewWithWarning. The IgnoredOnParcel annotation is used to ignore that variable while passing our error to the ErrorFragment.

Server Error

I think you already understood how easy it is now to add new errors with their own implementation! :]

👌 Using our new ErrorFragment

Companion Object has the newError() static function [Static Factory Method], which allows to create a new ErrorFragment based on any ErrorView.

The error is obtained from the fragment arguments in the error variable and setupView() is called inside onViewCreated() to handle how it is displayed.

Every time a new error state is dispatched, the state is handled from outside of the ErrorFragment, and then the error type is chosen:

  • ErrorFragment.newError(NotFoundError())
  • ErrorFragment.newError(InternetError(showWarning = false))
  • ErrorFragment.newError(ServerError())

🎨 SOLID

Let’s walk through the principles, one by one, and inspect they are respected with this implementation:

S — Single Responsibility Principle

Gather together the things that change for the same reasons. Separate things that change for different reasons.

ErrorFragment has a Single Responsibility, which is to display the error.

Different Errors implementations may change, so they must be kept separated from each other and from ErrorFragment.

O — Open Closed Principle

A Module should be open for extension but closed for modification.

Our Error handling is open for extension, since we can keep adding different kinds of errors, and is closed for modification, because we will never have to touch ErrorFragment again when adding new error types.

L — Liskov Substitution Principle

A program that uses an interface must not be confused by an implementation of that interface.

ErrorFragment can receive both ErrorView and ErrorViewWithWarning abstractions, it does not matter.

I — Interface Segregation Principle

Keep interfaces small so that users don’t end up depending on things they don’t need.

If an error doesn’t need to show a warning, it can implement only ErrorView, so it wont depend on things it doesn’t need.

D — Dependency Inversion Principle

Depend in the direction of abstraction. High level modules should not depend upon low level details.

ErrorFragment depends on an ErrorView abstraction, instead of depending in concrete implementations.

Where to go from here?

If you want to check the code in a more cleaner way, feel free to check my GitHub. There is an usage example with Jetpack Navigation.

If you want to extend your knowledge on Jetpack Compose, check the following resources:

If you enjoyed it, and if this helped you, you can also consider paying me a coffee. :]

Thank you!

--

--

Hello! I am Paulo and I’m 25 years old. | Android Engineer @Bloom & Wild| Associate Android Developer certified by Google