Image for post
Image for post

How to maximize Android’s UI reusability — 5 common mistakes

During the last few months, I had the opportunity to revisit some of our existing UI at Groupon. As part of this process, we started by identifying what is good in our UI and what are our most common mistakes, with the hope of taking Groupon's UI to the next level.

What makes UI good? Good is relative but it was easy to agree on three main principles that can be applied to any software:

In this article, we will go through some common mistakes that affect our UI’s reusability and see how reusable UIs can make our code more readable and testable.

1. Business model’s in custom views

Sometimes, in order to save time and avoid duplicated code, we decide to reuse in our views, the same models that we use in our network and database models. Let’s take an example:

We use userApiClient to pull our user’s information from backend:

On success, we receive the model:

And we pass that same model to our custom view UserView, that will show the user’s name and address. This may look like an easy win but it’s not. It goes against our reusability principle and can cause serious problems in the long term. Let’s see why.

Why is this bad?

How to fix it?

Fixing this problem is easy but it requires some extra code. We will need to create a UI model and a method to transform the backend model into the UI model.

In our example, we will convert UserModel into UserUIModel and pass it as a parameter to UserView:

Our new implementation requires some extra plumbing and adds the complexity of a new transformation but this is a small price to pay to decouple our UI from our backend. Our new design prevents us from sending unnecessary data to our views and allows us to reuse our views wherever we need them.

2. Monolithic views

Have you ever opened a layout file and see a huge document containing all the activity’s UI? This is, unfortunately, a very common pattern in Android that causes us a lot of troubles.

Why is this bad?

It goes against our three principles:

How to fix it?

Building all our UI logic in one xml file is the equivalent of building all our business logic in the activity class. We should break our UI in smaller pieces, in the same way, that we build DAOs, models and backend clients for our business logic.

Custom views and the <include> and <merge> tags are our best friends. We should always use them to break our UI at the beginning of any new feature. Waiting for a UI component to be reused in order to break it out of a monolithic xml can cause serious issues. At that time, our UI will be heavily coupled with our activity/fragment, making the refactor harder and endangering existing functionalities of our app.

Let’s see an example inspired by a real layout of an open source project. I have removed properties and added comments to make the layout easier to read:

Even after removing properties and adding comments, reading an xml like this is hard. There are several layers of nested layouts, making hard to understand where each component is placed. Without the comments, is hard to tell how the different tags are related and what they represent.

In the xml we can identify at least 6 well-defined UI components. Let’s see how this layout will look like if we create a custom view for these components:

By creating a custom view for each of our views we simplified our code, made our views reusable and prevented the side effects of future refactors. In this new implementation, the comments are no longer necessary because our code is self-descriptive.

Think how easy it will be to implement a new progress animation if all our activities use ProgressOverlay. It will only require to change one class vs all our activities using the monolithic approach.

Groupon’s approach of favoring small UI components over monolithic xmls it is inspired by Brad Frost’s book Atomic Design. I recommend taking a look at this book, especially if you are passionate about UI.

3. Business logic in custom views

In the previous point, we talked about the benefits of using custom views, custom views are an amazing tool but if we don’t use them carefully they can be a double-edged sword. Coupling our business logic to a custom view it is surprisingly easy. Adding logs, AB tests, and decision-making logic may look a great way to compartmentalize our code but it is not.

Why is this bad?

How to fix it?

There are a lot of ways to extract the business logic out of our views. The way to do it will depend on your preferred architecture. If you use MVP, any kind of logic belongs to the Presenter. If you prefer MVVM, your logic should be part of your ViewModel.

At Groupon, we have found that MVI and unidirectional data flows are a great way to fix this problem. The business logic should be part of our Intent, that will produce an immutable state object, that will be rendered by our view.

If you are interested in unidirectional data flows and how to implement reusable UI components. I strongly encourage you to read Pratyush Kshirsagar and Yichen WU article Cross view communication using Grox. They do a great job explaining how unidirectional data flows can help us build our UI.

4. Over-optimization

At this point, you may have realized that we haven’t talked about performance. You might even be surprised that we didn’t even consider performance as one of our principles for good UIs.

While we do believe that performance it is important, readable, reusable and testable code is even more important than optimized code. After all, that’s the reason why we use high-level programming languages instead of writing more efficient assembler code.

In Android, nested layouts and double taxation are two big problems that affect the performance of our UI. Because of that, we are constantly bombarded with articles, podcast and talks telling us to use ConstrainLayout and avoid nested layouts. ConstrainLayout is an amazing tool, is more powerful than RelativeLayout and it doesn’t incur in double taxation. The problem, as usual, happens when we take this to the extreme.

Based on all the articles and talks that we have heard, we may decide that we are going to implement the UI of our activity only using one ConstraintLayout.

Why is this bad?

How to fix it?

Sacrificing good code for performance is a hard choice and should always be our last resort. To prevent over-optimization we need to make performance tests part of our development process. Our test should tell us when we have a problem and we should only create a monolithic view when no other solution is possible. You will be surprised by how many times UI performance problems are caused by our binding logic doing too much or because our code is refreshing the UI more than needed.

5. Neglecting our UI in code reviews

Reviewing code is hard, time-consuming and to make it worst, xml files are not easy to understand (especially monolithic xmls). For those reasons, we tend to neglect our UI when we review code.

Why is this bad?

How to fix it?

There are a few things we can do to improve our review process.

As a reviewer:

As a reviewee:

Conclusions

Building reusable UI components it is not hard but it requires discipline. It requires us to stop thinking in terms of screens and start thinking in terms of components and their relationships.

Reusing our UI help us build new functionalities faster. There is a big difference, in terms of speed and quality, between composing existing UI components and building a complete UI from scratch. Also, reused components contain bug fixes and consider edge cases we may have never thought about.

Summarizing some of the tips that we have discussed:

I hope these tips help you with your next Android adventure. With 💚 Carlos Palacin Rubio from the Groupon Android team.

Groupon Product and Engineering

All things technology from Groupon staff

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store