Android Architecture: Part 1 – every new beginning is hard

The goal of this post series is to give an overview of our struggles with the architecture of Android apps. I realized that however painful the implementation of android app architecture might be, it turned out to be the fundament of every fantastic app I have been working on.

Every technology has its natural evolution. Or to be more precise, its community undergoes the process of evolution. Early adopters of a new computer language or a framework are enthusiasts who just want to get their hands on the technology and get some stuff done as quickly as possible. Typically, the new community is small and has limited potential for knowledge transfer among developers, that is, everybody is learning from their own mistakes since there are no architecture guidelines available yet.

android early years

Android in its early days wasn’t an exception. It was cool and attractive for developers, and it offered an opportunity for the early adopters to be a part of community around this fast-developing technology.

Pain points of the early Android years: does Google care at all?

You can argue that there are numerous senior guys out there who have a lot of experience with different technologies and who will come up with standards in no time. Well, not necessarily. If there is a strong company behind the technology that counts on making money, sure, they’ll have their own technology evangelists who will spread the word about how this new language is cool and you can do a lot of stuff with it and it’s easy peasy to learn, scale, and satisfy millions of customers.

Microsoft does that often with its technologies. On the other hand, when Google bought Android I honestly think they treated it just as some other side project. If you have been in the Android world since its beginnings, you must remember the frustrations and the feelings that Google simply doesn’t care about you. Those couple of guys who have extra experience, ability, and the will to help the community are now Android superstars or little demi-gods—Jake Wharton for example.

“When Google bought Android, I honestly think they treated it just as some other side project.”

xamvxi

Another thing you could say is that you don’t have to think a lot about the architecture and the organization of your code because the framework does that for you. Android forces you to put your screen stuff into activities, reusable screen stuff into fragments, and background stuff into services and to talk to others using broadcast receivers, so it pretty much makes your life good. Right? No.

First of all, there are some good practices and principles that are simply good, regardless of technology. The single responsibility principle for example, or the dependency inversion principle, program against interfaces, kill global state, try to murder all states, etc.

Frameworks rarely force you to follow principles. Au contraire, they violate them in the worst possible ways. Think of context, the God object that you have to use everywhere, all kinds of singleton managers, fragments with their lifecycles (what kind of nightmarish life is that), AsyncTasks that are usually implemented incorrectly, so they suck the memory, food, water, and air out of your app.

It’s easy for a young developer without guidance to make a monster instead of an app. Think about it as a spaghetti monster black hole—it’s good as a pastafarian  deity, but not as good an app as it could be.

android spagetti monster

And lastly, technology and frameworks hide the purpose of the app. Ok, it’s an Android app, but what kind of an Android app? Newsreader? Music player? Language-learning app? Weather app? Maybe it is a yet another to-do list app. If everything is bundled up in the classes provided by the framework, you cannot tell.

As Robert Martin, aka Uncle Bob, says, “Your architecture should scream the purpose of the app.” More technically said, business logic should be clearly separated and independent of the framework.

Four golden rules of Android architecture (or any architecture really)

I hope that it’s clear now that you cannot rely on the framework to make your code neat and organized, especially with Android. At Five, we realized that a long time ago but lacked the experience to come up with a bull’s-eye right away. It takes a lot of time for fails to manifest, and you cannot change the whole architecture in the middle of the project. You also can’t get time to refactor the old project into the new, cool, “wanna be” best architecture. So, we took the gradual approach, slowly building up our architecture from project to project and learning from our mistakes. There are couple of goals that we think our architecture should satisfy, and we can compare our approaches against those goals. Good architecture should do the following:

  1. Satisfy a multitude of stakeholders.
  2. Encourage separation of concerns.
  3. Run away from the real world (Android, DB, Internet…).
  4. Enable your components to be testable.

 

I. Satisfy a multitude of stakeholders.

Stakeholder (in this post) is any person interested in the development of your app. Besides developers, there are UI designers, UX designers, PMs, DB admins, QA, etc. 

And of course, you can’t organize your code in a way that a UX designer, for example, could open a project and understand everything immediately and maybe even make some changes. It’s a unicorn.

stakeholders

What I mean is that you CAN organize your code in a way that a developer who currently works WITH the UX designer can manage only the stuff related to the UX. So, all interactions are separated in the classes/modules/components/whatever whose job is interactions, and that particular developer works only on those components while working on the UX part of the app.

II. Encourage separation of concerns.

What I have said before is an example of separation of concerns. We encourage that particular approach because it maps nicely to the organization of teams and project phases, but your architecture should also encourage separation of concerns generally. Separation of concerns, or the single responsibility principle, says that every component should have only one reason to change.

III. Run away from the real world (Android, DB, Internet…).

This particular point, to run away from the real world, was already mentioned before. We have already said that we want to scream what the app really does; that’s it. We want to emphasize business logic and leave framework details under the hood. This point should be even stronger: we would like to not only hide framework details but all the details related to the outside world.

under the hood

All the nitty gritty dirty Android stuff like sensors, notification mechanisms, screen details, database access, Internet access, etc.

IV. Enable your components to be testable.

You should unit test your app as much as possible and your architecture should allow you to do it. If you can’t unit test everything, you should at least cover your business logic with tests. Separation from the real world goes nicely with this. It’s easier to test your business logic if it is clearly separated from the rest of the app.

giphy-5

First iteration – God activity

public final class UsersActivity extends ListActivity {

   @Override

   protected void onCreate(Bundle savedInstanceState) {

       super.onCreate(savedInstanceState);

       //...

       new ListUsers().execute();

   }

   private final class ListUsers extends AsyncTask<Void, Void, Void> {

       @Override

       protected Void doInBackground(Void... voids) {

           // final SQLiteOpenHelper sqLiteOpenHelper = ...

           // JsonObjectRequest jsObjRequest = new JsonObjectRequest

           // (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

           // MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);

           // showData(user);

           return null;

       }

   }

}

 

You have probably seen code like this back in the “olden times.” If you haven’t, you’re young. What’s wrong with this? Everything!

no

We have an activity that touches databases, goes to the Internet, does parsing, spawns threads, and renders data. So, all the stakeholders are looking at the single class, none of the concerns is separated, it’s not testable, and business logic is mixed with the Android stuff.

Second iteration – MVP

The first approach obviously didn’t work. One of the first things we tried was MVP, or model-view-presenter. Everybody is familiar with MVP. It is one of the most popular architectural patterns. It looks something like this:

Here we have separated views that are actually our Android fragments, we have (domain) models that represent our business, and finally we have presenters that coordinate all of that. It’s better for sure. Concerns are somewhat separated, stakeholders aren’t so confused anymore, and you can write some tests. We are still mixed up with the real world, though, as the presenter directly touches the database and everything. The elephant in the room here is the presenter. It’s a god object. It deals with the models, it sends the data to the view, it holds the business logic (business logic are those gears :) ), and it goes to the database and Internet, fetches sensor data, etc. So yeah, better, but it could get way more better.

Third iteration – MVP + managers

What does government do when it doesn’t know what to do? It forms an agency. What do developers do when they don’t know what to do? They introduce some manager. You don’t have to name it “manager.” There are lots of names for these kind of classes: utils, helpers, fooBarBuzz-ator, etc. So, we introduced managers.

To be honest, this even sort of worked. Business logic is contained in the manager classes. Stakeholders know where to look at, concerns are sort of separated but they could be even more so, you can write more tests but you are still touching Android directly so you have to write Android test cases and prefill the database to test business logic and that’s slow. And yeah, managers tend to be huge beasts and very quickly become hard to maintain. You can argue that it doesn’t need to get more complicated than it is, that you can deliver code faster by using simpler architecture, but there will be more bugs with that approach, and maintainability will suffer.

Conclusion

In this first part of the series, we went through the challenges of creating an Android architecture that actually works. Good Android architecture should satisfy a multitude of stakeholders, encourage separation of concerns, emphasize business logic and leave framework details under the hood and enable all your components to be testable. In the second part of the series, we will show you how we managed that what worked for us. Until then, do you have any suggestions on how to create a proper Android workflow? What were the problems you encountered?

Continue to part 2.

  • Pingback: Android Architecture - part 2: the clean architecture • Five()

  • Pingback: Android Architecture: Part 3 – Applying clean architecture on Android • Five()

  • Ra fik

    Very nice article, but i’m confused about the meaning of stakeholders here, is it mean : final users ?

    • Tomo

      Hmm yes, maybe I wasn’t clear enough about that. Stakeholders aren’t necessarily clients, but anybody that is interested, or included in the implementation in some way. For instance db admin can be a stakeholder, project manager, designer, etc. The point is that every stakeholder has his own area of interest in the app, and a good architecture enables him to be involved in that area, without messing with other stakeholders’ areas. So as a designer, I’m interested in the design, and I’ll be looking into screens, but if my app is written in a way that I’m accessing a database in the same files where screen is designed (andI saw that), I could easily mess up e.g. account fetching because I added some new animation.

  • Pingback: Swift Tutorial: An Introduction to the MVVM Design Pattern - Cible()

  • Tojonirina Ramaroson

    Very good article. I’m moving to part two :)

  • Esteban Echeverry Perez

    Nice article! This is something that should be taken into account much more than the common X is faster than Y. A good architecture eases maintainability and collaboration. We surely need more articles like this!