Fragments, perhaps by some strange historical accidents, have been seen as the optimal building blocks upon which we can build our Android applications for much of the time that Android has been around. Let’s call this idea – the idea that
Fragments are the best building blocks for our apps – “android-centric” architecture.
This series of posts is about the connection between the testability of android-centric architecture and the other problems that are now leading Android developers to reject it; it’s about how our unit tests are trying to tell us that
Fragments don’t make the best building blocks for our apps because they force us to write code with tight coupling and low cohesion.
In this first part of the series, I want to say a little about why I think android-centric architecture has been dominant for so long and to provide a little background on why I think unit tests have insightful things to say about rejecting android-centric architecture.
What is Android-Centric Architecture?
An android-centric architecture is one in which each screen the user sees is ultimately backed by a class whose main purpose is to interact with the android operating system. As we’ll see later, Diane Hackborne and Chet Haase have both recently stated that
Activitys are an example of such a class. Since
Fragments are very similar to
Activitys, I consider an app where each screen is backed by a
Fragment to also have an android-centric architecture, even if there’s only one
Activity in the app.
MVP and VIPER and RIBLETS and…are a thing now in the Android community. However, these suggestions aren’t necessarily a full rejection of android-centric architecture. Although there may be
Interactorss or whatever involved, these objects are often still built on top of
Fragments; they could still get instantiated by and delegate to android-centric components, one for each screen the user sees.
An app that doesn’t follow android-centric architecture has one
Activity and no
Fragments. Router and Controller type classes are POJOs.
Why Android-Centric Architecture?
I suspect that a part of the reason why we buy into android-centric architecture is that Google hasn’t really been clear on what
Fragments are for until relatively recently. On channels less official and visible than the Android docs, Chet Haase and Diane Hackborne have both suggested that
Activitys aren’t really the kind of things with which you want to build your application.
…With its Java language APIs and fairly high-level concepts, it can look like a typical application framework that is there to say how applications should be doing their work. But for the most part, it is not.
It is probably better to call the core Android APIs a “system framework.” For the most part, the platform APIs we provide are there to define how an application interacts with the operating system; but for anything going on purely within the app, these APIs are often just not relevant.
and here’s Haase:
Application components (activities, services, providers, receivers) are interfaces for your application to interact with the operating system; don’t take them as a recommendation of the facilities you should architect your entire application around.
Hackborne and Haase almost explicitly reject android-centric architecture. I say “almost”, as they both don’t seem to denounce the use of
Fragments as building blocks for our apps. However, there’s a tension between the idea
Activitys are not suitable app components and that
Fragments are, and that tension is as strong as the the many similarities between the two components.
It might even be fair to say that Google has actually suggested an android-centric architecture through the previous Google I/O app samples and the android documentation. The “app components” section of the Android docs is a particularly good example of this. The section introduction tells the reader that they’ll learn “how you can build the components [including
Fragments] that define the building blocks of your app.”
Over the past couple of years, many Android developers – myself included – are starting to realize that
Fragments often are not helpful building blocks for their applications. Companies like Square, Lyft, and Uber are moving away from android-centric architecture. Two common complaints stand out: as the app gets more complicated, the code is difficult to understand and too rigid to handle their varying use-cases.
What does Testing have to do with this?
The connection between testability and understandable, flexible code is well expressed in this quotation from Growing Object Oriented Software Guided by Tests:
for a class to be easy to unit-test, the class must…be loosely coupled and highly cohesive – in other words, well-designed.
Coupling and cohesion have direct bearing on how understandable and flexible your code is, so if this quote is right and if unit testing
Fragments is difficult – and you likely know that even if you haven’t read my posts suggesting as much – then writing unit tests would have shown us, before Google and painful experiences did, that
Fragments aren’t the building blocks we want for constructing our applications.
In the next post, I’ll try and fail to write an example test against an
Activity and show exactly how the tight coupling and low cohesion of
Activitys makes testing difficult. Next, I’ll test drive the same functionality, and we’ll end up with testable code. In the following post, I’ll show how the resulting code is loosely coupled and highly cohesive and talk about some of the benefits of these properties, including how they open up novel solutions to common problems on Android, like runtime permissions and intermittent connectivity.
Image Credit: Maret Hosemann, “Der Bruch,” Creative Commons 2.0