Advocating Against Android Fragments

Alternatives for dealing with (painful) Android fragments.

Square Engineering
Oct 8, 2014 · 8 min read

Written by Pierre-Yves Ricau.

Heads up, we’ve moved! If you’d like to continue keeping up with the latest technical content from Square please visit us at our new home https://developer.squareup.com/blog

Recently I gave a tech talk (fr) at Droidcon Paris, where I explained the problems Square had with Android fragments and how others could avoid using any fragments.

In 2011, we decided to use fragments for the following reasons:

  • At this time, we did not yet support tablets — but knew we wanted to eventually. Fragments help build responsive UIs.
  • Fragments are view controllers; they hold decoupled chunks of business logic that can be tested.
  • The fragment API provides backstack management, (i.e. it mirrors the behavior of the activity stack within a single activity).
  • Because fragments are built on top of views and views can easily be animated, fragments would give us better control over screen transitions.
  • Google recommended fragments, and we wanted to make our code standard.

Since 2011, we’ve discovered better options for Square.

What your parents never told you about fragments

The lolcycle

On Android, Context is a god object, and Activity is a context with extra lifecycle. A god with lifecycle? Kind of ironic. Fragments aren’t gods, but they make up for it by having extremely complex lifecycle.

Steve Pomeroy made a diagram of the complete lifecycle, and it’s not pretty:

Created by Steve Pomeroy, modified to remove the activity lifecycle, shared under the CC BY-SA 4.0 license.

The lifecycle makes it difficult to figure out what you should do with each callback. Are they called synchronously or in a post? In what order?

Hard to debug

When a bug occurs in your app, you take your debugger and execute the code step by step to understand what is happening exactly. It usually works great… until you hit FragmentManagerImpl: Landmine!

This code is hard to follow and debug, which makes it hard to correctly fix bugs in your app.

If you ever found yourself with a stale unattached fragment recreated on rotation, you know what I’m talking about. (And don’t get me started on nested fragments.)

As Coding Horror puts it, I am now required by law to link to this cartoon.

After years of in-depth analysis, I came to the conclusion that WTFs/min = 2^fragment count.

View controllers? Not so fast.

Because fragments create, bind, and configure views, they contain a lot of view-related code. This effectively means that business logic isn’t decoupled from view code — making it hard to write unit tests against fragments.

Fragment transactions

Fragment transactions allow you to perform a set of fragment operations. Unfortunately, committing a transaction is async and posted at the end of the main thread handler queue. This can leave your app in an unknown state when receiving multiple click events or during configuration changes.

Fragment creation magic

Fragment instances can be created by you or by the fragment manager. This code seems fairly reasonable:

However, when restoring the activity instance state, the fragment manager may try to recreate an instance of that fragment class using reflection. Since it’s an anonymous class, it has a hidden constructor argument to reference the outer class.

Fragments: lessons learned

Despite their drawbacks, fragments taught us invaluable lessons which we can now reapply when writing apps:

  • Single Activity Interface: there is no need to use one activity for each screen. We can split our app into decoupled widgets and assemble them as we please. This makes animating and lifecycle easy. We can split our widgets into view code and controller code.
  • The backstack isn’t an activity specific notion; you can implement a backstack within an activity.
  • There is no need for new APIs; everything we needed was there from the very beginning: activities, views, and layout inflaters.

Responsive UI: fragments vs custom views

Fragments

Let’s look at the fragment basic example, a list / detail UI.

The HeadlinesFragment is a pretty straightforward list:

Now this is interesting: ListFragmentActivity has to handle whether the detail is on the same screen or not.

Custom views

Let’s reimplement a similar version of that code using only views.

First, we’ll have the notion of a Container, which can show an item and also handle back presses.

The activity assumes there’s always a container and merely delegates the work to it.

The list is also quite trivial.

Now, the meat of the work: loading different XML layouts based on resource qualifiers.

res/layout/main_activity.xml

res/layout-land/main_activity.xml

Here is a very simple implementation for those containers:

It’s not hard to imagine abstracting these containers and building an app this way — not only do we not need fragments, but we also have code that is easier to understand.

Views & presenters

Using custom views works great, but we’d like to isolate business logic into dedicated controllers. We call those presenters. This makes the code much more readable and facilitates testing. MyDetailView from the previous example could look something like that:

Let’s look at code from Square Register, the screen for editing discounts.

The presenter manipulates the view at a high level:

Writing tests for this presenter is a breeze:

Backstack management

Managing a backstack does not require async transactions. We released a tiny library that does just that: Flow. Ray Ryan already wrote a great blog post about Flow.

I’m deep in fragment spaghetti, how do I escape?

Make your fragments shells of themselves. Pull view code up into custom view classes, and push business logic down into a presenter that knows how to interact with the custom views. Then, your fragment is nearly empty, just inflating custom views that connect themselves with presenters:

At that point you can eliminate the fragment.

Migrating away from fragments wasn’t easy, but we went through it — thanks to the awesome work of Dimitris Koutsogiorgas and Ray Ryan.

What about Dagger & Mortar?

Dagger & Mortar are orthogonal to fragments; they can be used with or without fragments.

Dagger helps you modularize your app into a graph of decoupled components. It takes care of all the wiring and therefore makes it easy to extract dependencies and write single concern objects.

Mortar works on top of Dagger and has two main advantages:

  • It provides simple lifecycle callbacks to injected components. This allows you to write singleton presenters that won’t be destroyed on rotation, but can still save their state into a bundle to survive process death.
  • It manages Dagger subgraphs for you and helps you tie them to the activity lifecycle. This effectively lets you implement the notion of scopes: a view comes in, its presenter and dependencies are created as a subgraph. When the view goes out, you can easily destroy that scope and let the garbage collector do its work.

Conclusion

We used fragments intensively and eventually changed our minds:

  • Most of our difficult crashes were related to the fragment lifecycle.
  • We only need views to build a responsive UI, a backstack, and screen transitions.

Square Corner Blog

Buying and selling sound like simple things - and they…

Square Engineering

Written by

The official account for @Square Engineering.

Square Corner Blog

Buying and selling sound like simple things - and they should be. Somewhere along the way, they got complicated. At Square, we're working hard to make commerce easy for everyone.

Square Engineering

Written by

The official account for @Square Engineering.

Square Corner Blog

Buying and selling sound like simple things - and they should be. Somewhere along the way, they got complicated. At Square, we're working hard to make commerce easy for everyone.

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